diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx index fb9be5296d..cc196533ff 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx @@ -439,7 +439,7 @@ void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) { aPushChild(aChildIt.CurrentId(), aNextDepth); } - for (const BRepGraph_ChildRefId& aChildRefId : aSolidEnt.FreeChildRefIds) + for (const BRepGraph_ChildRefId& aChildRefId : aSolidEnt.AuxChildRefIds) aPushChild(aStorage.ChildRef(aChildRefId).ChildDefId, aNextDepth); break; } @@ -452,7 +452,7 @@ void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) { aPushChild(aChildIt.CurrentId(), aNextDepth); } - for (const BRepGraph_ChildRefId& aChildRefId : aShellEnt.FreeChildRefIds) + for (const BRepGraph_ChildRefId& aChildRefId : aShellEnt.AuxChildRefIds) aPushChild(aStorage.ChildRef(aChildRefId).ChildDefId, aNextDepth); break; } @@ -560,12 +560,25 @@ void BRepGraph::markRefModified(const BRepGraph_RefId theRefId) noexcept //================================================================================================= -void BRepGraph::markRefModified(const BRepGraph_RefId /*theRefId*/, +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 (myData->myDeferredMode.load(std::memory_order_relaxed)) + { + myData->myDeferredRefModified.Append(theRefId); + } + else if (myLayerRegistry.HasRefModificationSubscribers()) + { + myLayerRegistry.DispatchRefModified(theRefId); + } + } + if (!theRef.ParentId.IsValid()) return; @@ -861,3 +874,63 @@ const BRepGraph_TransientCache& BRepGraph::transientCache() const { return myTransientCache; } + +//================================================================================================= + +BRepGraph_RefTransientCache& BRepGraph::refTransientCache() +{ + return myRefTransientCache; +} + +//================================================================================================= + +const BRepGraph_RefTransientCache& BRepGraph::refTransientCache() const +{ + return myRefTransientCache; +} + +//================================================================================================= + +const BRepGraphInc::BaseRef* BRepGraph::refEntity(const BRepGraph_RefId theId) const +{ + if (!theId.IsValid()) + return nullptr; + const BRepGraphInc_Storage& aStorage = myData->myIncStorage; + switch (theId.RefKind) + { + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId anId(theId.Index); + return anId.IsValid(aStorage.NbShellRefs()) ? &aStorage.ShellRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId anId(theId.Index); + return anId.IsValid(aStorage.NbFaceRefs()) ? &aStorage.FaceRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId anId(theId.Index); + return anId.IsValid(aStorage.NbWireRefs()) ? &aStorage.WireRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::CoEdge: { + const BRepGraph_CoEdgeRefId anId(theId.Index); + return anId.IsValid(aStorage.NbCoEdgeRefs()) ? &aStorage.CoEdgeRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_VertexRefId anId(theId.Index); + return anId.IsValid(aStorage.NbVertexRefs()) ? &aStorage.VertexRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId anId(theId.Index); + return anId.IsValid(aStorage.NbSolidRefs()) ? &aStorage.SolidRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId anId(theId.Index); + return anId.IsValid(aStorage.NbChildRefs()) ? &aStorage.ChildRef(anId) : nullptr; + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId anId(theId.Index); + return anId.IsValid(aStorage.NbOccurrenceRefs()) ? &aStorage.OccurrenceRef(anId) : nullptr; + } + default: + return nullptr; + } +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx index 1c6530e91c..d3e3092a56 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,7 @@ class BRepGraph_History; //! - **RepId** (Kind + Index): separate geometry/mesh addressing (Surface, //! 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 Sense (Parasolid convention). +//! seam edges use paired CoEdges with opposite Orientation (Parasolid convention). //! - **Lifecycle**: Build() populates from TopoDS_Shape; Builder().Mut*() guards //! provide RAII-scoped mutation with automatic cache invalidation and upward //! propagation. @@ -219,6 +220,14 @@ private: [[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; + + //! 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; + //! @} Standard_EXPORT void invalidateSubgraphImpl(const BRepGraph_NodeId theNode); @@ -260,8 +269,9 @@ private: 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_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 index a9608089f1..7a2b9c6195 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx @@ -12,6 +12,7 @@ // commercial license or contractual agreement. #include +#include #include #include #include @@ -50,6 +51,16 @@ static Standard_GUID generateRandomGUID() return Standard_GUID(aUUID); } +//================================================================================================= + +static void assertMutationBoundary(BRepGraph& theGraph, const char* theContext) +{ + (void)theContext; + const bool isValid = theGraph.Builder().ValidateMutationBoundary(); + Standard_ASSERT_VOID(isValid, theContext); + (void)isValid; +} + } // namespace //================================================================================================= @@ -184,8 +195,7 @@ void BRepGraph_Builder::Perform(BRepGraph& theGraph, populateUIDs(theGraph); - // Auto-create a single root Product pointing to the top-level topology node. - // aTopologyRoot defaults to invalid (Index = -1); set only if topology exists. + // Determine the top-level topology root node. { BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; BRepGraph_NodeId aTopologyRoot; // default: invalid (Index = -1) @@ -230,13 +240,18 @@ void BRepGraph_Builder::Perform(BRepGraph& theGraph, if (aTopologyRoot.IsValid()) theGraph.myData->myRootNodeIds.Append(aTopologyRoot); - BRepGraphInc::ProductDef& aProduct = aStorage.AppendProduct(); - const int aProductIdx = aStorage.NbProducts() - 1; - aProduct.Id = BRepGraph_ProductId(aProductIdx); - aProduct.ShapeRootId = aTopologyRoot; // invalid if no topology matched - aProduct.RootOrientation = theShape.Orientation(); - aProduct.RootLocation = theShape.Location(); - theGraph.allocateUID(aProduct.Id); + // Auto-create a single root Product pointing to the top-level topology node. + // Skipped when CreateAutoProduct is false (e.g. XCAF builder manages Products itself). + if (theOptions.CreateAutoProduct) + { + BRepGraphInc::ProductDef& aProduct = aStorage.AppendProduct(); + const int aProductIdx = aStorage.NbProducts() - 1; + aProduct.Id = BRepGraph_ProductId(aProductIdx); + aProduct.ShapeRootId = aTopologyRoot; // invalid if no topology matched + aProduct.RootOrientation = theShape.Orientation(); + aProduct.RootLocation = theShape.Location(); + theGraph.allocateUID(aProduct.Id); + } } theGraph.myData->myIsDone = true; @@ -266,13 +281,16 @@ void BRepGraph_Builder::Perform(BRepGraph& theGraph, } theGraph.myTransientCache.Reserve(aReservedKindCount, aCounts); } + + assertMutationBoundary(theGraph, "Build: post-build mutation boundary inconsistency"); } //================================================================================================= -void BRepGraph_Builder::AppendFlattened(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const bool theParallel) +void BRepGraph_Builder::AppendFlattened(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions) { if (theShape.IsNull()) return; @@ -308,7 +326,7 @@ void BRepGraph_Builder::AppendFlattened(BRepGraph& theGraph, theShape, theParallel, aAppendedRoots, - BRepGraphInc_Populate::Options(), + theOptions, aParamLayer.get(), aRegularityLayer.get(), aTmpAlloc); @@ -344,6 +362,81 @@ void BRepGraph_Builder::AppendFlattened(BRepGraph& theGraph, } theGraph.myData->myIsDone = true; + + assertMutationBoundary(theGraph, "AppendFlattened: post-append mutation boundary inconsistency"); +} + +//================================================================================================= + +void BRepGraph_Builder::AppendFull(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions) +{ + if (theShape.IsNull()) + return; + + 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(); + BRepGraphInc_Populate::Append(aStorage, + theShape, + theParallel, + theOptions, + aParamLayer.get(), + aRegularityLayer.get(), + 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, "AppendFull: post-append mutation boundary inconsistency"); } //================================================================================================= diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx index 8cec79d222..8ca2d1a85a 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx @@ -51,9 +51,26 @@ public: //! @param[in,out] theGraph graph to extend //! @param[in] theShape shape to add //! @param[in] theParallel if true, per-face geometry extraction is parallel - static Standard_EXPORT void AppendFlattened(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const bool theParallel); + //! @param[in] theOptions populate options (e.g. CreateAutoProduct) + static Standard_EXPORT void AppendFlattened( + BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions = BRepGraphInc_Populate::Options()); + + //! Append a shape to the existing graph without clearing. + //! Preserves the full shape hierarchy: Solid/Shell/Compound/CompSolid nodes + //! are created alongside Face/Edge/Vertex nodes. Shapes already present in + //! the graph (same TShape pointer) are deduplicated and not re-added. + //! @param[in,out] theGraph graph to extend + //! @param[in] theShape shape to add + //! @param[in] theParallel if true, per-face geometry extraction is parallel + //! @param[in] theOptions populate options (e.g. CreateAutoProduct) + static Standard_EXPORT void AppendFull( + BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions = BRepGraphInc_Populate::Options()); private: //! Allocate UIDs for all incidence entities after BRepGraphInc_Populate diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.cxx index e221a6a79e..2a371d9e0a 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.cxx @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -64,6 +66,78 @@ bool hasOtherActiveParent(const BRepGraph& theGraph, return false; } +BRepGraph_NodeId refChildNode(const BRepGraph& theGraph, const BRepGraph_RefId theRef) +{ + return theGraph.Refs().ChildNode(theRef); +} + +bool hasAnyActiveUsage(const BRepGraph& theGraph, const BRepGraph_NodeId theChild) +{ + if (!theChild.IsValid()) + { + return false; + } + + const BRepGraph::RefsView& aRefs = theGraph.Refs(); + const BRepGraph::TopoView& aTopo = theGraph.Topo(); + const auto hasActiveRef = [&](const int theCount, const BRepGraph_RefId::Kind theKind) { + for (int anIndex = 0; anIndex < theCount; ++anIndex) + { + const BRepGraph_RefId aRef(theKind, anIndex); + if (refChildNode(theGraph, aRef) != theChild) + { + continue; + } + + if (!aRefs.IsRemoved(aRef)) + { + return true; + } + } + return false; + }; + + if (hasActiveRef(aRefs.Shells().Nb(), BRepGraph_RefId::Kind::Shell) + || hasActiveRef(aRefs.Faces().Nb(), BRepGraph_RefId::Kind::Face) + || hasActiveRef(aRefs.Wires().Nb(), BRepGraph_RefId::Kind::Wire) + || hasActiveRef(aRefs.CoEdges().Nb(), BRepGraph_RefId::Kind::CoEdge) + || hasActiveRef(aRefs.Vertices().Nb(), BRepGraph_RefId::Kind::Vertex) + || hasActiveRef(aRefs.Solids().Nb(), BRepGraph_RefId::Kind::Solid) + || hasActiveRef(aRefs.Children().Nb(), BRepGraph_RefId::Kind::Child) + || hasActiveRef(aRefs.Occurrences().Nb(), BRepGraph_RefId::Kind::Occurrence)) + { + return true; + } + + if (theChild.NodeKind == BRepGraph_NodeId::Kind::Product) + { + const BRepGraph_ProductId aProduct = BRepGraph_ProductId::FromNodeId(theChild); + for (int anOccIdx = 0; anOccIdx < aTopo.Occurrences().Nb(); ++anOccIdx) + { + const BRepGraphInc::OccurrenceDef& anOcc = + aTopo.Occurrences().Definition(BRepGraph_OccurrenceId(anOccIdx)); + if (!anOcc.IsRemoved && anOcc.ProductDefId == aProduct) + { + return true; + } + } + } + else if (BRepGraph_NodeId::IsTopologyKind(theChild.NodeKind)) + { + for (int aProdIdx = 0; aProdIdx < aTopo.Products().Nb(); ++aProdIdx) + { + const BRepGraphInc::ProductDef& aProduct = + aTopo.Products().Definition(BRepGraph_ProductId(aProdIdx)); + if (!aProduct.IsRemoved && aProduct.ShapeRootId == theChild) + { + return true; + } + } + } + + return false; +} + const char* kindName(const BRepGraph_NodeId::Kind theKind) { switch (theKind) @@ -436,7 +510,7 @@ void initSubEdgeEntity(BRepGraphInc::EdgeDef& theSub, void initSubCoEdgeEntity(BRepGraphInc::CoEdgeDef& theCE, const BRepGraph_EdgeId theEdgeId, const BRepGraph_FaceId theFaceId, - const TopAbs_Orientation theSense, + const TopAbs_Orientation theOrientation, const BRepGraph_Curve2DRepId theCurve2DRepId, const double theParamFirst, const double theParamLast, @@ -444,7 +518,7 @@ void initSubCoEdgeEntity(BRepGraphInc::CoEdgeDef& theCE, { theCE.EdgeDefId = theEdgeId; theCE.FaceDefId = theFaceId; - theCE.Sense = theSense; + theCE.Orientation = theOrientation; theCE.Curve2DRepId = theCurve2DRepId; theCE.ParamFirst = theParamFirst; theCE.ParamLast = theParamLast; @@ -561,9 +635,9 @@ BRepGraph_WireId BRepGraph::BuilderView::AddWire( // Create CoEdge entity for this edge-wire binding. BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.AppendCoEdge(); const int aCoEdgeIdx = aStorage.NbCoEdges() - 1; - aCoEdge.Id = BRepGraph_NodeId(BRepGraph_NodeId::Kind::CoEdge, aCoEdgeIdx); - aCoEdge.EdgeDefId = anEdgeDefId; - aCoEdge.Sense = anOri; + aCoEdge.Id = BRepGraph_NodeId(BRepGraph_NodeId::Kind::CoEdge, aCoEdgeIdx); + aCoEdge.EdgeDefId = anEdgeDefId; + aCoEdge.Orientation = anOri; myGraph->allocateUID(aCoEdge.Id); // CoEdgeRef in ref-table. @@ -929,10 +1003,20 @@ BRepGraph_OccurrenceId BRepGraph::BuilderView::AddOccurrence( //================================================================================================= -void BRepGraph::BuilderView::AppendFlattenedShape(const TopoDS_Shape& theShape, - const bool theParallel) +void BRepGraph::BuilderView::AppendFlattenedShape(const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions) { - BRepGraph_Builder::AppendFlattened(*myGraph, theShape, theParallel); + BRepGraph_Builder::AppendFlattened(*myGraph, theShape, theParallel, theOptions); +} + +//================================================================================================= + +void BRepGraph::BuilderView::AppendFullShape(const TopoDS_Shape& theShape, + const bool theParallel, + const BRepGraphInc_Populate::Options& theOptions) +{ + BRepGraph_Builder::AppendFull(*myGraph, theShape, theParallel, theOptions); } //================================================================================================= @@ -1163,6 +1247,52 @@ void BRepGraph::BuilderView::RemoveSubgraph(const BRepGraph_NodeId theNode) //================================================================================================= +bool BRepGraph::BuilderView::RemoveRef(const BRepGraph_RefId theRef) +{ + if (!theRef.IsValid()) + { + return false; + } + + if (!myGraph->myData->myIncStorage.MarkRemovedRef(theRef)) + { + return false; + } + + myGraph->myLayerRegistry.DispatchOnRefRemoved(theRef); + myGraph->markRefModified(theRef); + return true; +} + +//================================================================================================= + +bool BRepGraph::BuilderView::RemoveRef(const BRepGraph_NodeId theParent, + const BRepGraph_RefId theRef, + const bool theToPruneOrphanedChild) +{ + if (!theRef.IsValid()) + { + return 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)) + { + return false; + } + + if (theToPruneOrphanedChild && aChildNode.IsValid() && !hasAnyActiveUsage(*myGraph, aChildNode)) + { + RemoveSubgraph(aChildNode); + } + return true; +} + +//================================================================================================= + void BRepGraph::BuilderView::RemoveRep(const BRepGraph_RepId theRep) { if (!theRep.IsValid()) @@ -1191,6 +1321,132 @@ BRepGraph_Curve2DRepId BRepGraph::BuilderView::CreateCurve2DRep( //================================================================================================= +void BRepGraph::BuilderView::SetCoEdgePCurve(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& theCurve2d) +{ + BRepGraph_MutGuard aCoEdge = MutCoEdge(theCoEdge); + aCoEdge->Curve2DRepId = + theCurve2d.IsNull() ? BRepGraph_Curve2DRepId() : CreateCurve2DRep(theCurve2d); +} + +//================================================================================================= + +BRepGraph_TriangulationRepId BRepGraph::BuilderView::CreateTriangulationRep( + const occ::handle& theTriangulation) +{ + if (theTriangulation.IsNull()) + return BRepGraph_TriangulationRepId(); + + BRepGraphInc::TriangulationRep& aRep = myGraph->myData->myIncStorage.AppendTriangulationRep(); + const int anIdx = myGraph->myData->myIncStorage.NbTriangulations() - 1; + aRep.Id = BRepGraph_RepId::Triangulation(anIdx); + aRep.Triangulation = theTriangulation; + return BRepGraph_TriangulationRepId(anIdx); +} + +//================================================================================================= + +BRepGraph_Polygon3DRepId BRepGraph::BuilderView::CreatePolygon3DRep( + const occ::handle& thePolygon) +{ + if (thePolygon.IsNull()) + return BRepGraph_Polygon3DRepId(); + + BRepGraphInc::Polygon3DRep& aRep = myGraph->myData->myIncStorage.AppendPolygon3DRep(); + const int anIdx = myGraph->myData->myIncStorage.NbPolygons3D() - 1; + aRep.Id = BRepGraph_RepId::Polygon3D(anIdx); + aRep.Polygon = thePolygon; + return BRepGraph_Polygon3DRepId(anIdx); +} + +//================================================================================================= + +BRepGraph_PolygonOnTriRepId BRepGraph::BuilderView::CreatePolygonOnTriRep( + const occ::handle& thePolygon, + const BRepGraph_TriangulationRepId theTriRepId) +{ + if (thePolygon.IsNull() || !theTriRepId.IsValid()) + return BRepGraph_PolygonOnTriRepId(); + + BRepGraphInc::PolygonOnTriRep& aRep = myGraph->myData->myIncStorage.AppendPolygonOnTriRep(); + const int anIdx = myGraph->myData->myIncStorage.NbPolygonsOnTri() - 1; + aRep.Id = BRepGraph_RepId::PolygonOnTri(anIdx); + aRep.Polygon = thePolygon; + aRep.TriangulationRepId = theTriRepId; + return BRepGraph_PolygonOnTriRepId(anIdx); +} + +//================================================================================================= + +void BRepGraph::BuilderView::ClearFaceMesh(const BRepGraph_FaceId theFace) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveNode(aStorage, theFace)) + return; + + BRepGraphInc::FaceDef& aFace = aStorage.ChangeFace(theFace); + + // Clear coedge polygon-on-tri data for all wires of this face. + for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) + { + if (!aWireRefId.IsValid()) + continue; + const BRepGraphInc::WireRef& aWireRef = aStorage.WireRef(aWireRefId); + if (aWireRef.IsRemoved || !aWireRef.WireDefId.IsValid()) + continue; + const BRepGraphInc::WireDef& aWire = aStorage.Wire(aWireRef.WireDefId); + if (aWire.IsRemoved) + continue; + for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) + { + if (!aCoEdgeRefId.IsValid()) + continue; + const BRepGraphInc::CoEdgeRef& aCoEdgeRef = aStorage.CoEdgeRef(aCoEdgeRefId); + if (aCoEdgeRef.IsRemoved || !aCoEdgeRef.CoEdgeDefId.IsValid()) + continue; + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeRef.CoEdgeDefId); + if (aCoEdge.IsRemoved) + continue; + for (const BRepGraph_PolygonOnTriRepId& aPolyRepId : aCoEdge.PolygonOnTriRepIds) + { + if (aPolyRepId.IsValid()) + RemoveRep(aPolyRepId); + } + aCoEdge.PolygonOnTriRepIds.Clear(); + myGraph->markModified(aCoEdge.Id); + } + } + + // Clear face triangulation data. + for (const BRepGraph_TriangulationRepId& aTriRepId : aFace.TriangulationRepIds) + { + if (aTriRepId.IsValid()) + RemoveRep(aTriRepId); + } + aFace.TriangulationRepIds.Clear(); + aFace.ActiveTriangulationIndex = -1; + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::BuilderView::ClearEdgePolygon3D(const BRepGraph_EdgeId theEdge) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveNode(aStorage, theEdge)) + return; + + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + if (anEdge.Polygon3DRepId.IsValid()) + { + RemoveRep(anEdge.Polygon3DRepId); + anEdge.Polygon3DRepId = BRepGraph_Polygon3DRepId(); + myGraph->markModified(theEdge); + } +} + +//================================================================================================= + void BRepGraph::BuilderView::AddPCurveToEdge(const BRepGraph_EdgeId theEdgeEntity, const BRepGraph_FaceId theFaceEntity, const occ::handle& theCurve2d, @@ -1211,7 +1467,7 @@ void BRepGraph::BuilderView::AddPCurveToEdge(const BRepGraph_EdgeId th aCoEdge.Id = BRepGraph_CoEdgeId(aCoEdgeIdx); aCoEdge.EdgeDefId = theEdgeEntity; aCoEdge.FaceDefId = theFaceEntity; - aCoEdge.Sense = theEdgeOrientation; + aCoEdge.Orientation = theEdgeOrientation; if (!theCurve2d.IsNull()) { BRepGraphInc::Curve2DRep& aCurve2DRep = aStorage.AppendCurve2DRep(); @@ -1440,6 +1696,19 @@ void BRepGraph::BuilderView::EndDeferredInvalidation() noexcept // Clear deferred list for next scope. aDeferredList.Clear(); + + // Dispatch deferred reference modification events to subscribing layers. + NCollection_Vector& aDeferredRefList = myGraph->myData->myDeferredRefModified; + if (!aDeferredRefList.IsEmpty() && myGraph->myLayerRegistry.HasRefModificationSubscribers()) + { + int aRefKindsMask = 0; + for (const BRepGraph_RefId& aRef : aDeferredRefList) + { + aRefKindsMask |= BRepGraph_Layer::RefKindBit(aRef.RefKind); + } + myGraph->myLayerRegistry.DispatchRefsModified(aDeferredRefList, aRefKindsMask); + } + aDeferredRefList.Clear(); } //================================================================================================= @@ -1989,7 +2258,7 @@ void BRepGraph::BuilderView::SplitEdge(const BRepGraph_EdgeId theEdgeEntity, aStorage.ChangeCoEdge(BRepGraph_CoEdgeId(aOldCoEdgeIdx)); if (aOldCoEdge.EdgeDefId == theEdgeEntity) { - const TopAbs_Orientation aOrigOri = aOldCoEdge.Sense; + const TopAbs_Orientation aOrigOri = aOldCoEdge.Orientation; const BRepGraph_FaceId aFaceDef = aOldCoEdge.FaceDefId; const TopLoc_Location aRefLoc = aRef.LocalLocation; @@ -2001,7 +2270,7 @@ void BRepGraph::BuilderView::SplitEdge(const BRepGraph_EdgeId theEdgeEntity, const int aSubBCoEdgeIdx = aStorage.NbCoEdges() - 1; aSubBCoEdge.Id = BRepGraph_CoEdgeId(aSubBCoEdgeIdx); aSubBCoEdge.EdgeDefId = BRepGraph_EdgeId(aSubBIdx); - aSubBCoEdge.Sense = aOrigOri; + aSubBCoEdge.Orientation = aOrigOri; aSubBCoEdge.FaceDefId = aFaceDef; myGraph->allocateUID(aSubBCoEdge.Id); @@ -2078,7 +2347,7 @@ void BRepGraph::BuilderView::SplitEdge(const BRepGraph_EdgeId theEdgeEntity, initSubCoEdgeEntity(aCoEdgeSubA, BRepGraph_EdgeId(aSubAIdx), aCE.FaceDefId, - aCE.Sense, + aCE.Orientation, aCE.Curve2DRepId, aCE.ParamFirst, aPCSplit, @@ -2092,7 +2361,7 @@ void BRepGraph::BuilderView::SplitEdge(const BRepGraph_EdgeId theEdgeEntity, initSubCoEdgeEntity(aCoEdgeSubB, BRepGraph_EdgeId(aSubBIdx), aCE.FaceDefId, - aCE.Sense, + aCE.Orientation, aCE.Curve2DRepId, aPCSplit, aCE.ParamLast, @@ -2245,7 +2514,7 @@ void BRepGraph::BuilderView::ReplaceEdgeInWire(const BRepGraph_WireId theWireDef { aCoEdge.EdgeDefId = theNewEdgeEntity; if (theReversed) - aCoEdge.Sense = TopAbs::Reverse(aCoEdge.Sense); + aCoEdge.Orientation = TopAbs::Reverse(aCoEdge.Orientation); // Update reverse indices incrementally. BRepGraphInc_ReverseIndex& aRevIdx = myGraph->myData->myIncStorage.ChangeReverseIndex(); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.hxx index d39b819cb6..d5e0ca1a16 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_BuilderView.hxx @@ -15,6 +15,7 @@ #define _BRepGraph_BuilderView_HeaderFile #include +#include #include #include #include @@ -24,6 +25,9 @@ class Geom_Surface; class Geom_Curve; class Geom2d_Curve; +class Poly_Polygon3D; +class Poly_PolygonOnTriangulation; +class Poly_Triangulation; //! @brief Non-const view for programmatic graph construction and mutation. //! @@ -177,8 +181,22 @@ public: //! Compound/CompSolid/Solid/Shell inputs are flattened to appended face roots. //! @param[in] theShape shape to add //! @param[in] theParallel if true, per-face geometry extraction is parallel - Standard_EXPORT void AppendFlattenedShape(const TopoDS_Shape& theShape, - const bool theParallel = false); + //! @param[in] theOptions populate options (e.g. CreateAutoProduct) + Standard_EXPORT void AppendFlattenedShape( + const TopoDS_Shape& theShape, + const bool theParallel = false, + const BRepGraphInc_Populate::Options& theOptions = BRepGraphInc_Populate::Options()); + + //! Append a shape to the graph preserving the full topology hierarchy. + //! Solid/Shell/Compound/CompSolid nodes are created alongside Face/Edge/Vertex nodes. + //! Shapes already in the graph (same TShape pointer) are deduplicated. + //! @param[in] theShape shape to add + //! @param[in] theParallel if true, per-face geometry extraction is parallel + //! @param[in] theOptions populate options (e.g. CreateAutoProduct) + Standard_EXPORT void AppendFullShape( + const TopoDS_Shape& theShape, + const bool theParallel = false, + const BRepGraphInc_Populate::Options& theOptions = BRepGraphInc_Populate::Options()); //! Create a new Curve2DRep in storage and return its typed identifier. //! Use this when assigning a new PCurve to an existing CoEdge entity @@ -190,6 +208,45 @@ public: [[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. + //! @param[in] theCoEdge typed coedge identifier to update + //! @param[in] theCurve2d new 2D curve geometry, or null to clear + Standard_EXPORT void SetCoEdgePCurve(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& theCurve2d); + + //! Create a new TriangulationRep in storage and return its typed identifier. + //! @param[in] theTriangulation the triangulation handle + //! @return typed TriangulationRep identifier, or invalid if the handle is null + [[nodiscard]] Standard_EXPORT BRepGraph_TriangulationRepId + CreateTriangulationRep(const occ::handle& theTriangulation); + + //! Create a new Polygon3DRep in storage and return its typed identifier. + //! @param[in] thePolygon the 3D polygon handle + //! @return typed Polygon3DRep identifier, or invalid if the handle is null + [[nodiscard]] Standard_EXPORT BRepGraph_Polygon3DRepId + CreatePolygon3DRep(const occ::handle& thePolygon); + + //! Create a new PolygonOnTriRep in storage and return its typed identifier. + //! @param[in] thePolygon the polygon-on-triangulation handle + //! @param[in] theTriRepId triangulation rep this polygon references + //! @return typed PolygonOnTriRep identifier, or invalid if polygon is null + //! or theTriRepId is invalid + [[nodiscard]] Standard_EXPORT BRepGraph_PolygonOnTriRepId + CreatePolygonOnTriRep(const occ::handle& thePolygon, + const BRepGraph_TriangulationRepId theTriRepId); + + //! Clear all mesh representations for a face and its coedges. + //! Removes TriangulationReps from FaceDef and PolygonOnTriReps from all + //! CoEdges on the face's wires. Resets ActiveTriangulationIndex to -1. + //! @param[in] theFace typed face identifier + Standard_EXPORT void ClearFaceMesh(const BRepGraph_FaceId theFace); + + //! Clear Polygon3D representation from an edge. + //! @param[in] theEdge typed edge identifier + Standard_EXPORT void ClearEdgePolygon3D(const BRepGraph_EdgeId theEdge); + //! Attach a PCurve to an edge for a given face context. //! Creates a new CoEdge entity with Curve2DRep and updates reverse indices. //! This always appends a new CoEdge entry for the edge-face pair; callers @@ -230,6 +287,29 @@ public: //! @param[in] theNode root node to remove Standard_EXPORT void RemoveSubgraph(const BRepGraph_NodeId theNode); + //! Mark a reference entry as removed (soft deletion). + //! This is the builder-level API for detaching a child usage from its parent + //! without removing the referenced definition itself. + //! Invalid or already-removed ids are ignored. + //! @param[in] theRef reference entry to remove + //! @return true if the reference transitioned from active to removed + Standard_EXPORT bool RemoveRef(const BRepGraph_RefId theRef); + + //! Mark an exact parent-owned reference entry as removed (soft deletion). + //! This overload validates that the reference really belongs to the supplied + //! parent and can optionally prune the child subtree when the removed usage + //! was the last active parent usage of that child definition. + //! Use this overload for UI/path-driven detach operations where the parent + //! context is part of the user's selection. + //! @param[in] theParent expected owning parent of the reference usage + //! @param[in] theRef reference entry to remove + //! @param[in] theToPruneOrphanedChild if true, remove the referenced child + //! subtree when no active parent usages remain after detachment + //! @return true if the reference transitioned from active to removed + Standard_EXPORT bool RemoveRef(const BRepGraph_NodeId theParent, + 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 diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx new file mode 100644 index 0000000000..10caa2f317 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx @@ -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. + +#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_CacheView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx index d4fa348e3d..f9ae8764a7 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx @@ -87,6 +87,8 @@ bool BRepGraph::CacheView::Has(const BRepGraph_NodeId theNode, const int theKind //================================================================================================= +// 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) { @@ -132,8 +134,158 @@ void BRepGraph::CacheView::Invalidate(const BRepGraph_NodeId theNode, const int //================================================================================================= -NCollection_Vector> BRepGraph::CacheView::CacheKinds( +BRepGraph_CacheKindIterator BRepGraph::CacheView::CacheKindIter( const BRepGraph_NodeId theNode) const { - return myGraph->transientCache().CacheKinds(theNode); + 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 index 8cd71e77b1..995128212c 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.hxx @@ -15,6 +15,7 @@ #define _BRepGraph_CacheView_HeaderFile #include +#include //! @brief Non-const view for managing transient cache values on nodes. //! @@ -95,12 +96,63 @@ public: //! Invalidate a cached value using a pre-resolved cache-kind slot. Standard_EXPORT void Invalidate(const BRepGraph_NodeId theNode, const int theKindSlot); - //! Return all cache kinds populated on a node. + //! Create a zero-allocation iterator over all cache kinds populated on a node. //! @param[in] theNode node to query - //! @return vector of cache kind descriptors that have stored values on this node - [[nodiscard]] Standard_EXPORT NCollection_Vector> CacheKinds( + 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; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx index 33d27c77e6..190d9e558c 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx @@ -39,6 +39,57 @@ static int childExplorerKindDepth(const BRepGraph_NodeId::Kind theKind) }; return THE_DEPTH[static_cast(theKind)]; } + +static BRepGraph_RefId childRefIdForStep(const BRepGraph& theGraph, + const BRepGraph_NodeId theParent, + const int theStep) +{ + if (!theParent.IsValid() || theStep < 0) + { + return BRepGraph_RefId(); + } + + const BRepGraph::RefsView& aRefs = theGraph.Refs(); + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Product) + { + return aRefs.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); + if (aProduct.ShapeRootId.IsValid()) + { + return BRepGraph_RefId(); + } + + int anActiveIndex = 0; + for (int anIndex = 0; anIndex < aProduct.OccurrenceRefIds.Length(); ++anIndex) + { + const BRepGraph_OccurrenceRefId anOccurrenceRefId = aProduct.OccurrenceRefIds.Value(anIndex); + const BRepGraphInc::OccurrenceRef& anOccurrenceRef = + aRefs.Occurrences().Entry(anOccurrenceRefId); + if (anOccurrenceRef.IsRemoved) + { + continue; + } + + const BRepGraphInc::OccurrenceDef& anOccurrence = + aTopo.Occurrences().Definition(anOccurrenceRef.OccurrenceDefId); + if (anOccurrence.IsRemoved) + { + continue; + } + + if (anActiveIndex == theStep) + { + return anOccurrenceRefId; + } + ++anActiveIndex; + } + + return BRepGraph_RefId(); +} } // namespace //================================================================================================= @@ -388,15 +439,15 @@ void BRepGraph_ChildExplorer::advance() int i = aIdx; for (; i < aComp.ChildRefIds.Length(); ++i) { - const BRepGraphInc::ChildRef& aRef = aRefs.Children().Entry(aComp.ChildRefIds.Value(i)); - if (!aRef.IsRemoved) + const BRepGraph_ChildRefId aRefId = aComp.ChildRefIds.Value(i); + if (!aRefs.IsRemoved(aRefId)) { - aChildNode = aRef.ChildDefId; + aChildNode = aRefs.ChildNode(aRefId); aStepIdx = i; if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); break; } } @@ -410,15 +461,15 @@ void BRepGraph_ChildExplorer::advance() int i = aIdx; for (; i < aCS.SolidRefIds.Length(); ++i) { - const BRepGraphInc::SolidRef& aRef = aRefs.Solids().Entry(aCS.SolidRefIds.Value(i)); - if (!aRef.IsRemoved) + const BRepGraph_SolidRefId aRefId = aCS.SolidRefIds.Value(i); + if (!aRefs.IsRemoved(aRefId)) { - aChildNode = aRef.SolidDefId; + aChildNode = aRefs.ChildNode(aRefId); aStepIdx = i; if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); break; } } @@ -430,39 +481,30 @@ void BRepGraph_ChildExplorer::advance() const BRepGraphInc::SolidDef& aSolid = aDefs.Solids().Definition(BRepGraph_SolidId(aFrame.Node.Index)); const int aNbShells = aSolid.ShellRefIds.Length(); - const int aNbFree = aSolid.FreeChildRefIds.Length(); + const int aNbFree = aSolid.AuxChildRefIds.Length(); int i = aIdx; for (; i < aNbShells + aNbFree; ++i) { + BRepGraph_RefId aRefId; if (i < aNbShells) { - const BRepGraphInc::ShellRef& aRef = aRefs.Shells().Entry(aSolid.ShellRefIds.Value(i)); - if (!aRef.IsRemoved) - { - aChildNode = aRef.ShellDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); - break; - } + aRefId = aSolid.ShellRefIds.Value(i); } else { - const int aFreeIdx = i - aNbShells; - const BRepGraphInc::ChildRef& aRef = - aRefs.Children().Entry(aSolid.FreeChildRefIds.Value(aFreeIdx)); - if (!aRef.IsRemoved) - { - aChildNode = aRef.ChildDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); - break; - } + const int aFreeIdx = i - aNbShells; + aRefId = aSolid.AuxChildRefIds.Value(aFreeIdx); + } + + if (!aRefs.IsRemoved(aRefId)) + { + aChildNode = aRefs.ChildNode(aRefId); + aStepIdx = i; + if (myCumLoc) + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + if (myCumOri) + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + break; } } aFrame.NextChildIdx = i + 1; @@ -473,39 +515,30 @@ void BRepGraph_ChildExplorer::advance() const BRepGraphInc::ShellDef& aShell = aDefs.Shells().Definition(BRepGraph_ShellId(aFrame.Node.Index)); const int aNbFaces = aShell.FaceRefIds.Length(); - const int aNbFree = aShell.FreeChildRefIds.Length(); + const int aNbFree = aShell.AuxChildRefIds.Length(); int i = aIdx; for (; i < aNbFaces + aNbFree; ++i) { + BRepGraph_RefId aRefId; if (i < aNbFaces) { - const BRepGraphInc::FaceRef& aRef = aRefs.Faces().Entry(aShell.FaceRefIds.Value(i)); - if (!aRef.IsRemoved) - { - aChildNode = aRef.FaceDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); - break; - } + aRefId = aShell.FaceRefIds.Value(i); } else { - const int aFreeIdx = i - aNbFaces; - const BRepGraphInc::ChildRef& aRef = - aRefs.Children().Entry(aShell.FreeChildRefIds.Value(aFreeIdx)); - if (!aRef.IsRemoved) - { - aChildNode = aRef.ChildDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); - break; - } + const int aFreeIdx = i - aNbFaces; + aRefId = aShell.AuxChildRefIds.Value(aFreeIdx); + } + + if (!aRefs.IsRemoved(aRefId)) + { + aChildNode = aRefs.ChildNode(aRefId); + aStepIdx = i; + if (myCumLoc) + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + if (myCumOri) + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + break; } } aFrame.NextChildIdx = i + 1; @@ -520,35 +553,26 @@ void BRepGraph_ChildExplorer::advance() int i = aIdx; for (; i < aNbWires + aNbVerts; ++i) { + BRepGraph_RefId aRefId; if (i < aNbWires) { - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aFace.WireRefIds.Value(i)); - if (!aRef.IsRemoved) - { - aChildNode = aRef.WireDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRef.Orientation); - break; - } + aRefId = aFace.WireRefIds.Value(i); } else { - const int aVIdx = i - aNbWires; - const BRepGraphInc::VertexRef& aVRef = - aRefs.Vertices().Entry(aFace.VertexRefIds.Value(aVIdx)); - if (!aVRef.IsRemoved) - { - aChildNode = aVRef.VertexDefId; - aStepIdx = i; - if (myCumLoc) - aChildLoc = aFrame.AccLocation * aVRef.LocalLocation; - if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aVRef.Orientation); - break; - } + const int aVIdx = i - aNbWires; + aRefId = aFace.VertexRefIds.Value(aVIdx); + } + + if (!aRefs.IsRemoved(aRefId)) + { + aChildNode = aRefs.ChildNode(aRefId); + aStepIdx = i; + if (myCumLoc) + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + if (myCumOri) + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + break; } } aFrame.NextChildIdx = i + 1; @@ -561,14 +585,15 @@ void BRepGraph_ChildExplorer::advance() int i = aIdx; for (; i < aWire.CoEdgeRefIds.Length(); ++i) { - const BRepGraphInc::CoEdgeRef& aRef = aRefs.CoEdges().Entry(aWire.CoEdgeRefIds.Value(i)); - if (!aRef.IsRemoved) + const BRepGraph_CoEdgeRefId aRefId = aWire.CoEdgeRefIds.Value(i); + if (!aRefs.IsRemoved(aRefId)) { - aChildNode = aRef.CoEdgeDefId; + aChildNode = aRefs.ChildNode(aRefId); aStepIdx = i; if (myCumLoc) - aChildLoc = aFrame.AccLocation * aRef.LocalLocation; - // CoEdgeRef has no Orientation field. + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + if (myCumOri) + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); break; } } @@ -597,15 +622,14 @@ void BRepGraph_ChildExplorer::advance() { continue; } - const BRepGraphInc::VertexRef& aVRef = aRefs.Vertices().Entry(aVRefId); - if (!aVRef.IsRemoved) + if (!aRefs.IsRemoved(aVRefId)) { - aChildNode = aVRef.VertexDefId; + aChildNode = aRefs.ChildNode(aVRefId); aStepIdx = i; if (myCumLoc) - aChildLoc = aFrame.AccLocation * aVRef.LocalLocation; + aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aVRefId); if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aVRef.Orientation); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aVRefId)); break; } } @@ -614,7 +638,7 @@ void BRepGraph_ChildExplorer::advance() } case Kind::CoEdge: { - // CoEdge owns exactly one Edge child. + // CoEdge owns exactly one Edge child (structural, no RefId). if (aIdx == 0) { const BRepGraphInc::CoEdgeDef& aCoEdge = @@ -622,9 +646,9 @@ void BRepGraph_ChildExplorer::advance() if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId.IsValid()) { aChildNode = aCoEdge.EdgeDefId; - aStepIdx = 0; + aStepIdx = -1; if (myCumOri) - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aCoEdge.Sense); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aCoEdge.Orientation); } } aFrame.NextChildIdx = 1; @@ -632,7 +656,7 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Occurrence: { - // Occurrence references exactly one Product. + // Occurrence references exactly one Product (structural, no RefId). if (aIdx == 0) { const BRepGraphInc::OccurrenceDef& anOcc = @@ -640,7 +664,7 @@ void BRepGraph_ChildExplorer::advance() if (!anOcc.IsRemoved && anOcc.ProductDefId.IsValid()) { aChildNode = anOcc.ProductDefId; - aStepIdx = 0; + aStepIdx = -1; if (myCumLoc) aChildLoc = aFrame.AccLocation * anOcc.Placement; } @@ -654,11 +678,11 @@ void BRepGraph_ChildExplorer::advance() const BRepGraphInc::ProductDef& aProd = aDefs.Products().Definition(aProdId); if (aProd.ShapeRootId.IsValid()) { - // Part product: single child = shape root topology node. + // Part product: single child = shape root topology node (structural, no RefId). if (aIdx == 0) { aChildNode = aProd.ShapeRootId; - aStepIdx = 0; + aStepIdx = -1; if (myCumLoc) aChildLoc = aFrame.AccLocation * aProd.RootLocation; if (myCumOri) @@ -766,6 +790,45 @@ void BRepGraph_ChildExplorer::setCurrentFromFrame(const int theFrameIndex) //================================================================================================= +BRepGraph_NodeId BRepGraph_ChildExplorer::CurrentParent() const +{ + if (!myHasMore || myCurrentFrame <= 0) + { + return BRepGraph_NodeId(); + } + + return myStack[myCurrentFrame - 1].Node; +} + +//================================================================================================= + +BRepGraph_ChildExplorer::LinkKind BRepGraph_ChildExplorer::CurrentLinkKind() const +{ + if (!myHasMore || myCurrentFrame <= 0) + { + return LinkKind::None; + } + + return myStack[myCurrentFrame].StepFromParent >= 0 ? LinkKind::Reference : LinkKind::Structural; +} + +//================================================================================================= + +BRepGraph_RefId BRepGraph_ChildExplorer::CurrentRef() const +{ + if (!myHasMore || myCurrentFrame <= 0) + { + return BRepGraph_RefId(); + } + + const StackFrame& aCurrentFrame = myStack[myCurrentFrame]; + return childRefIdForStep(*myGraph, + myStack[myCurrentFrame - 1].Node, + aCurrentFrame.StepFromParent); +} + +//================================================================================================= + bool BRepGraph_ChildExplorer::shouldDescendFromCurrent() const { if (myMode != TraversalMode::Recursive || myCurrentFrame < 0) diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx index 057eb2c6de..50f627711b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx @@ -58,6 +58,14 @@ class BRepGraph_ChildExplorer public: DEFINE_STANDARD_ALLOC + //! Relationship kind between Current() and CurrentParent(). + enum class LinkKind + { + None, //!< No current incoming link (e.g. root/self match). + Reference, //!< Current() is reached through a parent-owned RefId. + Structural, //!< Current() is reached through a structural non-ref link. + }; + //! Downward traversal strategy. enum class TraversalMode { @@ -133,6 +141,19 @@ public: return {myCurrent, myLocation, myOrientation}; } + //! Returns the immediate parent of Current() in the explored path. + //! Returns invalid NodeId when Current() is the root/self match. + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId CurrentParent() const; + + //! Returns how Current() is linked from CurrentParent(). + [[nodiscard]] Standard_EXPORT LinkKind CurrentLinkKind() const; + + //! 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. + [[nodiscard]] Standard_EXPORT BRepGraph_RefId CurrentRef() const; + [[nodiscard]] Standard_EXPORT TopLoc_Location LocationOf(const BRepGraph_NodeId::Kind theKind) const; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx index 9ac66e5348..39f79698a9 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx @@ -354,7 +354,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_EdgeId::FromNodeId(remapId(aCoEdge.EdgeDefId)); if (aNewEdgeDefId.IsValid()) { - aNewEntries.Append(std::make_pair(aNewEdgeDefId, aCoEdge.Sense)); + aNewEntries.Append(std::make_pair(aNewEdgeDefId, aCoEdge.Orientation)); anOldCoEdges.Append(aCR.CoEdgeDefId); } } @@ -428,7 +428,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aCompactPCurve, aCoEdge.ParamFirst, aCoEdge.ParamLast, - aCoEdge.Sense); + aCoEdge.Orientation); } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx index 364fa6093a..eb04267732 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -56,6 +57,23 @@ occ::handle copyPCurve(const occ::handle& theCrv, bo return occ::down_cast(theCrv->Copy()); } +template +void transferFreshCacheValues(const BRepGraph& theSrcGraph, + const TKeyId theSrcKey, + BRepGraph& theDstGraph, + const TKeyId theDstKey) +{ + for (auto aKindIt = theSrcGraph.Cache().CacheKindIter(theSrcKey); aKindIt.More(); aKindIt.Next()) + { + const occ::handle aKind = aKindIt.Value(); + const occ::handle aValue = theSrcGraph.Cache().Get(theSrcKey, aKind); + if (!aValue.IsNull()) + { + theDstGraph.Cache().Set(theDstKey, aKind, aValue); + } + } +} + } // namespace //================================================================================================= @@ -86,22 +104,6 @@ void BRepGraph_Copy::reserveTransientCache(BRepGraph& theGraph) //================================================================================================= -void BRepGraph_Copy::transferCacheValues(const BRepGraph& theSrcGraph, - const BRepGraph_NodeId theSrcNode, - BRepGraph& theDstGraph, - const BRepGraph_NodeId theDstNode) -{ - const BRepGraph_TransientCache& aSrcCache = theSrcGraph.transientCache(); - if (!aSrcCache.HasCacheValues(theSrcNode)) - { - return; - } - - theDstGraph.transientCache().TransferCacheValues(aSrcCache, theSrcNode, theDstNode, 0); -} - -//================================================================================================= - BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyGeom) { BRepGraph aResult; @@ -119,7 +121,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG { const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(aVertexId); (void)aResult.Builder().AddVertex(aVtx.Point, aVtx.Tolerance); - transferCacheValues(theGraph, aVertexId, aResult, aVertexId); + transferFreshCacheValues(theGraph, aVertexId, aResult, aVertexId); } // Edges. @@ -148,11 +150,13 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG anEdge.ParamLast, anEdge.Tolerance); - BRepGraph_MutGuard aNewEdge = aResult.Builder().MutEdge(anEdgeId); - aNewEdge->IsDegenerate = anEdge.IsDegenerate; - aNewEdge->SameParameter = anEdge.SameParameter; - aNewEdge->SameRange = anEdge.SameRange; - transferCacheValues(theGraph, anEdgeId, aResult, anEdgeId); + { + BRepGraph_MutGuard aNewEdge = aResult.Builder().MutEdge(anEdgeId); + aNewEdge->IsDegenerate = anEdge.IsDegenerate; + aNewEdge->SameParameter = anEdge.SameParameter; + aNewEdge->SameRange = anEdge.SameRange; + } + transferFreshCacheValues(theGraph, anEdgeId, aResult, anEdgeId); } // Wires. @@ -170,10 +174,10 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); if (!aCoEdge.EdgeDefId.IsValid(theGraph.Topo().Edges().Nb())) continue; - aWireEdges.Append(std::make_pair(aCoEdge.EdgeDefId, aCoEdge.Sense)); + aWireEdges.Append(std::make_pair(aCoEdge.EdgeDefId, aCoEdge.Orientation)); } (void)aResult.Builder().AddWire(aWireEdges); - transferCacheValues(theGraph, aWireId, aResult, aWireId); + transferFreshCacheValues(theGraph, aWireId, aResult, aWireId); } // Faces. @@ -208,11 +212,13 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG (void)aResult.Builder().AddFace(aSurf, anOuterWire, anInnerWires, aFace.Tolerance); - BRepGraph_MutGuard aNewFace = aResult.Builder().MutFace(aFaceId); - aNewFace->NaturalRestriction = aFace.NaturalRestriction; - aNewFace->TriangulationRepIds = aFace.TriangulationRepIds; - aNewFace->ActiveTriangulationIndex = aFace.ActiveTriangulationIndex; - transferCacheValues(theGraph, aFaceId, aResult, aFaceId); + { + BRepGraph_MutGuard aNewFace = aResult.Builder().MutFace(aFaceId); + aNewFace->NaturalRestriction = aFace.NaturalRestriction; + aNewFace->TriangulationRepIds = aFace.TriangulationRepIds; + aNewFace->ActiveTriangulationIndex = aFace.ActiveTriangulationIndex; + } + transferFreshCacheValues(theGraph, aFaceId, aResult, aFaceId); } // PCurves via CoEdge data (after edges and faces are created). @@ -234,7 +240,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aNewPC, aCoEdge.ParamFirst, aCoEdge.ParamLast, - aCoEdge.Sense); + aCoEdge.Orientation); } } @@ -245,15 +251,17 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG BRepGraph_ShellId aNewShellId = aResult.Builder().AddShell(); const BRepGraphInc::ShellDef& aShell = theGraph.Topo().Shells().Definition(aShellId); - transferCacheValues(theGraph, aShellId, aResult, aShellId); + transferFreshCacheValues(theGraph, aShellId, aResult, aShellId); for (const BRepGraph_FaceRefId& aRefId : aShell.FaceRefIds) { const BRepGraphInc::FaceRef& aFR = aRefs.Faces().Entry(aRefId); if (aFR.IsRemoved || !aFR.FaceDefId.IsValid(theGraph.Topo().Faces().Nb())) continue; - const BRepGraph_FaceId aFaceDefId = aFR.FaceDefId; - aResult.Builder().AddFaceToShell(aNewShellId, aFaceDefId, aFR.Orientation); + const BRepGraph_FaceId aFaceDefId = aFR.FaceDefId; + const BRepGraph_FaceRefId aNewFaceRefId = + aResult.Builder().AddFaceToShell(aNewShellId, aFaceDefId, aFR.Orientation); + transferFreshCacheValues(theGraph, aRefId, aResult, aNewFaceRefId); } } @@ -264,15 +272,17 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG BRepGraph_SolidId aNewSolidId = aResult.Builder().AddSolid(); const BRepGraphInc::SolidDef& aSolid = theGraph.Topo().Solids().Definition(aSolidId); - transferCacheValues(theGraph, aSolidId, aResult, aSolidId); + transferFreshCacheValues(theGraph, aSolidId, aResult, aSolidId); for (const BRepGraph_ShellRefId& aRefId : aSolid.ShellRefIds) { const BRepGraphInc::ShellRef& aSR = aRefs.Shells().Entry(aRefId); if (aSR.IsRemoved || !aSR.ShellDefId.IsValid(theGraph.Topo().Shells().Nb())) continue; - const BRepGraph_ShellId aShellDefId = aSR.ShellDefId; - aResult.Builder().AddShellToSolid(aNewSolidId, aShellDefId, aSR.Orientation); + const BRepGraph_ShellId aShellDefId = aSR.ShellDefId; + const BRepGraph_ShellRefId aNewShellRefId = + aResult.Builder().AddShellToSolid(aNewSolidId, aShellDefId, aSR.Orientation); + transferFreshCacheValues(theGraph, aRefId, aResult, aNewShellRefId); } } @@ -290,7 +300,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aChildNodeIds.Append(aCR.ChildDefId); } (void)aResult.Builder().AddCompound(aChildNodeIds); - transferCacheValues(theGraph, aCompoundId, aResult, aCompoundId); + transferFreshCacheValues(theGraph, aCompoundId, aResult, aCompoundId); } // CompSolids. @@ -307,7 +317,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aSolidNodeIds.Append(aSR.SolidDefId); } (void)aResult.Builder().AddCompSolid(aSolidNodeIds); - transferCacheValues(theGraph, aCompSolidId, aResult, aCompSolidId); + transferFreshCacheValues(theGraph, aCompSolidId, aResult, aCompSolidId); } // Products. @@ -471,10 +481,10 @@ BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(BRepGraph_VertexId(anOldIdx)); (void)aResult.Builder().AddVertex(aVtx.Point, aVtx.Tolerance); - transferCacheValues(theGraph, - BRepGraph_VertexId(anOldIdx), - aResult, - BRepGraph_VertexId(anIdx - 1)); + transferFreshCacheValues(theGraph, + BRepGraph_VertexId(anOldIdx), + aResult, + BRepGraph_VertexId(anIdx - 1)); } // Add edges in deterministic order. @@ -515,15 +525,17 @@ BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, (void)aResult.Builder() .AddEdge(aNewStart, aNewEnd, aCurve, anEdge.ParamFirst, anEdge.ParamLast, anEdge.Tolerance); - BRepGraph_MutGuard aNewEdge = - aResult.Builder().MutEdge(BRepGraph_EdgeId(aNewEdgeIdx)); - aNewEdge->IsDegenerate = anEdge.IsDegenerate; - aNewEdge->SameParameter = anEdge.SameParameter; - aNewEdge->SameRange = anEdge.SameRange; - transferCacheValues(theGraph, - BRepGraph_EdgeId(anOldIdx), - aResult, - BRepGraph_EdgeId(aNewEdgeIdx)); + { + BRepGraph_MutGuard aNewEdge = + aResult.Builder().MutEdge(BRepGraph_EdgeId(aNewEdgeIdx)); + aNewEdge->IsDegenerate = anEdge.IsDegenerate; + aNewEdge->SameParameter = anEdge.SameParameter; + aNewEdge->SameRange = anEdge.SameRange; + } + transferFreshCacheValues(theGraph, + BRepGraph_EdgeId(anOldIdx), + aResult, + BRepGraph_EdgeId(aNewEdgeIdx)); } // Add wires in deterministic order. @@ -543,15 +555,15 @@ BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, const int* aNewEdgeIdx = anEdgeMap.Seek(aCoEdge.EdgeDefId.Index); if (aNewEdgeIdx == nullptr) continue; - aNewEntries.Append(std::make_pair(BRepGraph_EdgeId(*aNewEdgeIdx), aCoEdge.Sense)); + aNewEntries.Append(std::make_pair(BRepGraph_EdgeId(*aNewEdgeIdx), aCoEdge.Orientation)); } (void)aResult.Builder().AddWire(aNewEntries); { const int anOldWireIdx = aWireSet.FindKey(anIdx); - transferCacheValues(theGraph, - BRepGraph_WireId(anOldWireIdx), - aResult, - BRepGraph_WireId(anIdx - 1)); + transferFreshCacheValues(theGraph, + BRepGraph_WireId(anOldWireIdx), + aResult, + BRepGraph_WireId(anIdx - 1)); } } @@ -581,12 +593,14 @@ BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, } (void)aResult.Builder().AddFace(aSurf, anOuterWire, anInnerWires, aFaceDef.Tolerance); - BRepGraph_MutGuard aNewFace = - aResult.Builder().MutFace(BRepGraph_FaceId(0)); - aNewFace->NaturalRestriction = aFaceDef.NaturalRestriction; - aNewFace->TriangulationRepIds = aFaceDef.TriangulationRepIds; - aNewFace->ActiveTriangulationIndex = aFaceDef.ActiveTriangulationIndex; - transferCacheValues(theGraph, theFace, aResult, BRepGraph_FaceId(0)); + { + BRepGraph_MutGuard aNewFace = + aResult.Builder().MutFace(BRepGraph_FaceId(0)); + aNewFace->NaturalRestriction = aFaceDef.NaturalRestriction; + aNewFace->TriangulationRepIds = aFaceDef.TriangulationRepIds; + aNewFace->ActiveTriangulationIndex = aFaceDef.ActiveTriangulationIndex; + } + transferFreshCacheValues(theGraph, theFace, aResult, BRepGraph_FaceId(0)); // PCurves for edges in this face via CoEdge data. for (int anIdx = 1; anIdx <= anEdgeSet.Extent(); ++anIdx) @@ -612,7 +626,7 @@ BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, aNewPC, aCoEdge.ParamFirst, aCoEdge.ParamLast, - aCoEdge.Sense); + aCoEdge.Orientation); } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx index 4830efb152..265e52c6a6 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx @@ -66,11 +66,6 @@ public: const bool theReserveCache = true); private: - Standard_EXPORT static void transferCacheValues(const BRepGraph& theSrcGraph, - BRepGraph_NodeId theSrcNode, - BRepGraph& theDstGraph, - BRepGraph_NodeId theDstNode); - //! Pre-allocate transient cache for lock-free parallel access. Standard_EXPORT static void reserveTransientCache(BRepGraph& theGraph); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx index 65d12d7aa3..2790303fdb 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx @@ -83,6 +83,9 @@ struct BRepGraph_Data //! NodeIds accumulated during deferred mode. Processed by EndDeferredInvalidation(). NCollection_Vector myDeferredModified; + //! RefIds accumulated during deferred mode. Processed by EndDeferredInvalidation(). + NCollection_Vector myDeferredRefModified; + //! Gen-validated shape cache entry. struct CachedShape { diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx index 30d85d14ca..ac86c76753 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx @@ -499,7 +499,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG if (!aOldCE.Curve2DRepId.IsValid()) continue; - TopAbs_Orientation aTransferOri = aOldCE.Sense; + TopAbs_Orientation aTransferOri = aOldCE.Orientation; if (isReversed) { if (aTransferOri == TopAbs_FORWARD) @@ -514,7 +514,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { const BRepGraphInc::CoEdgeDef& aCanonCE = theGraph.Topo().CoEdges().Definition(aCanonCoEdges.Value(aCanonCEIdx)); - if (aCanonCE.FaceDefId == aOldCE.FaceDefId && aCanonCE.Sense == aTransferOri) + if (aCanonCE.FaceDefId == aOldCE.FaceDefId && aCanonCE.Orientation == aTransferOri) { aAlreadyHas = true; break; @@ -562,7 +562,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); size_t aEntryHash[2]; aEntryHash[0] = opencascade::hash(aCoEdge.EdgeDefId); - aEntryHash[1] = opencascade::hash(static_cast(aCoEdge.Sense)); + aEntryHash[1] = opencascade::hash(static_cast(aCoEdge.Orientation)); aHash ^= opencascade::hashBytes(aEntryHash, sizeof(aEntryHash)) + 0x9e3779b9 + (aHash << 6) + (aHash >> 2); }); @@ -588,7 +588,8 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG theGraph.Topo().CoEdges().Definition(aWireACoEdges.Value(anIdx)); const BRepGraphInc::CoEdgeDef& aCoEdgeB = theGraph.Topo().CoEdges().Definition(aWireBCoEdges.Value(anIdx)); - if (aCoEdgeA.EdgeDefId != aCoEdgeB.EdgeDefId || aCoEdgeA.Sense != aCoEdgeB.Sense) + if (aCoEdgeA.EdgeDefId != aCoEdgeB.EdgeDefId + || aCoEdgeA.Orientation != aCoEdgeB.Orientation) return false; } return true; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx index 46dba1de6e..b17a717f01 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx @@ -32,11 +32,16 @@ //! - removed child definitions namespace BRepGraph_DefsIterator { -template +template struct BaseTraits { using ParentId = ParentIdT; using RefId = RefIdT; + using RefEntry = RefEntryT; using ChildId = ChildIdT; using ChildDef = ChildDefT; }; @@ -56,6 +61,7 @@ inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph struct ShellOfSolidTraits : public BaseTraits { @@ -89,6 +95,7 @@ struct ShellOfSolidTraits : public BaseTraits { @@ -120,8 +127,43 @@ 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_Vector& 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); + } +}; + struct WireOfFaceTraits : public BaseTraits { @@ -155,6 +197,7 @@ struct WireOfFaceTraits : public BaseTraits { @@ -188,6 +231,7 @@ struct VertexOfFaceTraits : public BaseTraits { @@ -221,6 +265,7 @@ struct CoEdgeOfWireTraits : public BaseTraits { @@ -265,6 +310,7 @@ struct EdgeOfWireTraits : public BaseTraits { @@ -296,8 +342,43 @@ 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_Vector& 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); + } +}; + struct ChildOfCompoundTraits : public BaseTraits { @@ -331,6 +412,7 @@ struct ChildOfCompoundTraits : public BaseTraits { @@ -415,7 +497,7 @@ private: { while (myRefIds != nullptr && myIndex < myLength) { - const auto& aRef = TraitsT::Ref(myGraph, myRefIds->Value(myIndex)); + const typename TraitsT::RefEntry& aRef = TraitsT::Ref(myGraph, myRefIds->Value(myIndex)); if (!aRef.IsRemoved) { const BRepGraphInc::BaseDef* aChildDef = @@ -537,6 +619,8 @@ 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 = @@ -545,6 +629,8 @@ 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 = diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx index b81306f101..d2f57b4694 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx @@ -32,3 +32,25 @@ void BRepGraph_Layer::OnNodesModified( const NCollection_Vector& /*theModifiedNodes*/) noexcept { } + +//================================================================================================= + +int BRepGraph_Layer::SubscribedRefKinds() const +{ + return 0; +} + +//================================================================================================= + +void BRepGraph_Layer::OnRefRemoved(const BRepGraph_RefId /*theRef*/) noexcept {} + +//================================================================================================= + +void BRepGraph_Layer::OnRefModified(const BRepGraph_RefId /*theRef*/) noexcept {} + +//================================================================================================= + +void BRepGraph_Layer::OnRefsModified(const NCollection_Vector& /*theModifiedRefs*/, + const int /*theModifiedRefKindsMask*/) noexcept +{ +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx index 09341a1776..d13223c12b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx @@ -15,6 +15,7 @@ #define _BRepGraph_Layer_HeaderFile #include +#include #include #include #include @@ -23,27 +24,33 @@ //! @brief Abstract base class for named attribute layers. //! -//! A layer groups per-node metadata under a unique name with lifecycle callbacks. -//! Layers are registered on BRepGraph and automatically notified when nodes are -//! removed, replaced (sewing/deduplicate), remapped (compact), or modified. +//! A layer groups per-node and per-reference metadata under a unique name with +//! lifecycle callbacks. Layers are registered on BRepGraph and automatically +//! notified when nodes or references are removed, remapped (compact), or modified. //! //! Derived layers store domain-specific data (names, colors, materials, etc.) -//! in internal maps keyed by BRepGraph_NodeId. The lifecycle callbacks ensure -//! data consistency across all graph mutations. +//! in internal maps keyed by BRepGraph_NodeId or BRepGraph_RefId. The lifecycle +//! callbacks ensure data consistency across all graph mutations. //! -//! ## Modification Events -//! Layers can subscribe to modification events by overriding SubscribedKinds() +//! ## Node Modification Events +//! Layers subscribe to node modification events by overriding SubscribedKinds() //! to return a non-zero bitmask of Kind values. When a subscribed node kind is //! modified, OnNodeModified() (immediate mode) or OnNodesModified() (deferred //! batch mode) is called. Layers with SubscribedKinds() == 0 (default) incur //! zero dispatch overhead. //! +//! ## Reference Modification Events +//! Layers subscribe to reference modification events by overriding +//! SubscribedRefKinds() to return a non-zero bitmask of BRepGraph_RefId::Kind +//! values. When a subscribed ref kind is mutated, OnRefModified() (immediate +//! mode) or OnRefsModified() (deferred batch mode) is called. Removal is always +//! dispatched via OnRefRemoved() regardless of subscription. +//! //! ## Thread safety //! Callback dispatch is single-threaded (called from mutation paths). //! Layers that only provide read access can skip internal locking. //! -//! @warning All lifecycle callbacks (OnNodeRemoved, OnCompact, InvalidateAll, -//! Clear, OnNodeModified, OnNodesModified) are declared noexcept. Derived +//! @warning All lifecycle callbacks are declared noexcept. Derived //! implementations that throw will cause std::terminate. This is enforced //! by C++ language semantics for noexcept virtual overrides. class BRepGraph_Layer : public Standard_Transient @@ -113,6 +120,44 @@ 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. + [[nodiscard]] Standard_EXPORT virtual int SubscribedRefKinds() const; + + //! Called when a reference is soft-deleted via RemoveRef(). + //! No replacement concept - refs are simply removed (unlike nodes which can have + //! a replacement during sewing or deduplication). Dispatched to all layers + //! regardless of SubscribedRefKinds(). + //! Default: no-op. + //! @param[in] theRef the removed reference + Standard_EXPORT virtual void OnRefRemoved(const BRepGraph_RefId theRef) noexcept; + + //! Called in immediate (non-deferred) mode after a single ref is mutated. + //! Only dispatched if the ref's kind matches SubscribedRefKinds(). + //! Default: no-op. + //! @param[in] theRef the modified reference + Standard_EXPORT virtual void OnRefModified(const BRepGraph_RefId theRef) noexcept; + + //! 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 + //! 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 + Standard_EXPORT virtual void OnRefsModified( + const NCollection_Vector& theModifiedRefs, + const int theModifiedRefKindsMask) noexcept; + + //! Convenience: return bitmask bit for a given RefId::Kind. + static int RefKindBit(const BRepGraph_RefId::Kind theKind) + { + return 1 << static_cast(theKind); + } + DEFINE_STANDARD_RTTIEXT(BRepGraph_Layer, Standard_Transient) }; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx new file mode 100644 index 0000000000..eeac605c9a --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.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_LayerIterator_HeaderFile +#define _BRepGraph_LayerIterator_HeaderFile + +#include +#include + +//! @brief Iterator over registered layers in a BRepGraph_LayerRegistry. +//! +//! Provides zero-allocation iteration with OCCT More()/Next()/Value() pattern +//! and STL range-for via begin()/end(). +//! +//! @code +//! // Range-for: +//! for (const occ::handle& aLayer : +//! BRepGraph_LayerIterator(aGraph.LayerRegistry())) +//! doSomething(aLayer); +//! +//! // Traditional: +//! for (BRepGraph_LayerIterator anIt(aGraph.LayerRegistry()); anIt.More(); anIt.Next()) +//! doSomething(anIt.Value()); +//! @endcode +class BRepGraph_LayerIterator +{ +public: + //! Construct an iterator over all layers in the registry. + explicit BRepGraph_LayerIterator(const BRepGraph_LayerRegistry& theRegistry) + : myRegistry(&theRegistry), + myCount(theRegistry.NbLayers()), + myCurrent(0) + { + } + + //! True if the iterator has a current element. + [[nodiscard]] bool More() const { return myCurrent < myCount; } + + //! Advance to the next layer. + void Next() { ++myCurrent; } + + //! Return the current layer handle. + [[nodiscard]] const occ::handle& Value() const + { + return myRegistry->Layer(myCurrent); + } + + //! Return the current slot index in the registry. + [[nodiscard]] int Slot() const { return myCurrent; } + + //! Number of layers in the registry. + [[nodiscard]] int NbLayers() 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: + const BRepGraph_LayerRegistry* myRegistry; + int myCount; + int myCurrent; +}; + +#endif // _BRepGraph_LayerIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx index 415186c3a5..1523e51652 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx @@ -35,6 +35,7 @@ int BRepGraph_LayerRegistry::RegisterLayer(const occ::handle& t myLayers.Append(theLayer); myGuidToSlot.Bind(aGUID, aNewSlot); mySubscribedKindsMask |= theLayer->SubscribedKinds(); + mySubscribedRefKindsMask |= theLayer->SubscribedRefKinds(); return aNewSlot; } @@ -160,11 +161,54 @@ void BRepGraph_LayerRegistry::InvalidateAll() noexcept //================================================================================================= +void BRepGraph_LayerRegistry::DispatchOnRefRemoved(const BRepGraph_RefId theRef) noexcept +{ + for (const occ::handle& aLayer : myLayers) + { + aLayer->OnRefRemoved(theRef); + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchRefModified(const BRepGraph_RefId theRef) noexcept +{ + if (!HasRefModificationSubscribers()) + return; + + const int aRefKindBit = BRepGraph_Layer::RefKindBit(theRef.RefKind); + for (const occ::handle& aLayer : myLayers) + { + if ((aLayer->SubscribedRefKinds() & aRefKindBit) != 0) + aLayer->OnRefModified(theRef); + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchRefsModified( + const NCollection_Vector& theModifiedRefs, + const int theModifiedRefKindsMask) noexcept +{ + if (!HasRefModificationSubscribers() || theModifiedRefKindsMask == 0) + return; + + for (const occ::handle& aLayer : myLayers) + { + if ((aLayer->SubscribedRefKinds() & theModifiedRefKindsMask) != 0) + aLayer->OnRefsModified(theModifiedRefs, theModifiedRefKindsMask); + } +} + +//================================================================================================= + void BRepGraph_LayerRegistry::recomputeSubscribedKindsMask() { - mySubscribedKindsMask = 0; + mySubscribedKindsMask = 0; + mySubscribedRefKindsMask = 0; for (const occ::handle& aLayer : myLayers) { mySubscribedKindsMask |= aLayer->SubscribedKinds(); + mySubscribedRefKindsMask |= aLayer->SubscribedRefKinds(); } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx index d01720cd8e..fae641f982 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx @@ -15,6 +15,7 @@ #define _BRepGraph_LayerRegistry_HeaderFile #include +#include #include #include @@ -65,10 +66,10 @@ public: //! Number of registered layers. [[nodiscard]] int NbLayers() const { return myLayers.Length(); } - //! True if any registered layer subscribes to modification events. + //! True if any registered layer subscribes to node modification events. [[nodiscard]] bool HasModificationSubscribers() const { return mySubscribedKindsMask != 0; } - //! Bitwise OR of all registered layer subscription masks. + //! Bitwise OR of all registered layer node subscription masks. [[nodiscard]] int SubscribedKindsMask() const { return mySubscribedKindsMask; } //! Dispatch OnNodeRemoved to all registered layers. @@ -87,6 +88,25 @@ public: Standard_EXPORT void DispatchOnCompact( const NCollection_DataMap& theRemapMap) noexcept; + // --- Reference dispatch --- + + //! True if any registered layer subscribes to reference modification events. + [[nodiscard]] bool HasRefModificationSubscribers() const { return mySubscribedRefKindsMask != 0; } + + //! Bitwise OR of all registered layer reference subscription masks. + [[nodiscard]] int SubscribedRefKindsMask() const { return mySubscribedRefKindsMask; } + + //! Dispatch OnRefRemoved to all registered layers (unconditional - not filtered). + Standard_EXPORT void DispatchOnRefRemoved(const BRepGraph_RefId theRef) noexcept; + + //! Dispatch OnRefModified to subscribed layers (immediate mode). + Standard_EXPORT void DispatchRefModified(const BRepGraph_RefId theRef) noexcept; + + //! Dispatch OnRefsModified to subscribed layers (deferred/batch mode). + Standard_EXPORT void DispatchRefsModified( + const NCollection_Vector& theModifiedRefs, + const int theModifiedRefKindsMask) noexcept; + //! Clear all registered layer payloads without unregistering them. Standard_EXPORT void ClearAll() noexcept; @@ -99,7 +119,8 @@ private: private: NCollection_Vector> myLayers; NCollection_DataMap myGuidToSlot; - int mySubscribedKindsMask = 0; + int mySubscribedKindsMask = 0; + int mySubscribedRefKindsMask = 0; }; #endif // _BRepGraph_LayerRegistry_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx index 5324839c78..7fc8a3f7f4 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx @@ -43,25 +43,6 @@ static int parentExplorerKindDepth(const BRepGraph_NodeId::Kind theKind) return THE_DEPTH[static_cast(theKind)]; } -static BRepGraph_VertexRefId edgeVertexRefIdAt(const BRepGraphInc::EdgeDef& theEdge, - const int theRefIdx) -{ - if (theRefIdx == 0) - { - return theEdge.StartVertexRefId; - } - if (theRefIdx == 1) - { - return theEdge.EndVertexRefId; - } - - const int anInternalIdx = theRefIdx - 2; - if (anInternalIdx < 0 || anInternalIdx >= theEdge.InternalVertexRefIds.Length()) - { - return BRepGraph_VertexRefId(); - } - return theEdge.InternalVertexRefIds.Value(anInternalIdx); -} } // namespace //================================================================================================= @@ -178,6 +159,43 @@ void BRepGraph_ParentExplorer::Next() //================================================================================================= +BRepGraph_NodeId BRepGraph_ParentExplorer::CurrentChild() const +{ + if (!myHasMore || myCurrentFrame <= 0) + { + return BRepGraph_NodeId(); + } + + return myStack[myCurrentFrame - 1].Node; +} + +//================================================================================================= + +BRepGraph_ParentExplorer::LinkKind BRepGraph_ParentExplorer::CurrentLinkKind() const +{ + if (!myHasMore || myCurrentFrame < 0) + { + return LinkKind::None; + } + + return myStack[myCurrentFrame].StepToChild >= 0 ? LinkKind::Reference : LinkKind::Structural; +} + +//================================================================================================= + +BRepGraph_RefId BRepGraph_ParentExplorer::CurrentRef() const +{ + if (!myHasMore || myCurrentFrame < 0) + { + return BRepGraph_RefId(); + } + + return myGraph->Refs().RefAtStep(myStack[myCurrentFrame].Node, + myStack[myCurrentFrame].StepToChild); +} + +//================================================================================================= + const TopLoc_Location& BRepGraph_ParentExplorer::LeafLocation() const { static const TopLoc_Location THE_EMPTY_LOCATION; @@ -882,7 +900,7 @@ void BRepGraph_ParentExplorer::applyTransition(const BRepGraph_NodeId theParent, aTopo.CoEdges().Definition(BRepGraph_CoEdgeId(theParent.Index)); if (!aCoEdge.IsRemoved) { - theOrientation = TopAbs::Compose(theOrientation, aCoEdge.Sense); + theOrientation = TopAbs::Compose(theOrientation, aCoEdge.Orientation); } return; } @@ -969,9 +987,7 @@ int BRepGraph_ParentExplorer::findOccurrenceStep(const BRepGraph_ProductId th for (BRepGraph_RefsOccurrenceOfProduct aRefIt(*myGraph, theParentProduct); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::OccurrenceRef& aRef = - myGraph->Refs().Occurrences().Entry(aRefIt.CurrentId()); - if (aRef.OccurrenceDefId == theOccurrence) + if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theOccurrence)) { return aRefIt.Index(); } @@ -986,8 +1002,7 @@ int BRepGraph_ParentExplorer::findCompoundChildStep(const BRepGraph_CompoundId t { for (BRepGraph_RefsChildOfCompound aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::ChildRef& aRef = myGraph->Refs().Children().Entry(aRefIt.CurrentId()); - if (aRef.ChildDefId == theChild) + if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == theChild) { return aRefIt.Index(); } @@ -1002,8 +1017,7 @@ int BRepGraph_ParentExplorer::findCompSolidSolidStep(const BRepGraph_CompSolidId { for (BRepGraph_RefsSolidOfCompSolid aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::SolidRef& aRef = myGraph->Refs().Solids().Entry(aRefIt.CurrentId()); - if (aRef.SolidDefId == theChild) + if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) { return aRefIt.Index(); } @@ -1023,8 +1037,7 @@ int BRepGraph_ParentExplorer::findSolidChildStep(const BRepGraph_SolidId thePare { for (BRepGraph_RefsShellOfSolid aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::ShellRef& aRef = aRefs.Shells().Entry(aRefIt.CurrentId()); - if (aRef.ShellDefId == BRepGraph_ShellId(theChild.Index)) + if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) { return aRefIt.Index(); } @@ -1032,11 +1045,10 @@ int BRepGraph_ParentExplorer::findSolidChildStep(const BRepGraph_SolidId thePare return -1; } - for (int aRefIdx = 0; aRefIdx < aSolid.FreeChildRefIds.Length(); ++aRefIdx) + for (int aRefIdx = 0; aRefIdx < aSolid.AuxChildRefIds.Length(); ++aRefIdx) { - const BRepGraphInc::ChildRef& aRef = - aRefs.Children().Entry(aSolid.FreeChildRefIds.Value(aRefIdx)); - if (!aRef.IsRemoved && aRef.ChildDefId == theChild) + const BRepGraph_ChildRefId aRefId = aSolid.AuxChildRefIds.Value(aRefIdx); + if (!aRefs.IsRemoved(aRefId) && aRefs.ChildNode(aRefId) == theChild) { return aSolid.ShellRefIds.Length() + aRefIdx; } @@ -1056,8 +1068,7 @@ int BRepGraph_ParentExplorer::findShellChildStep(const BRepGraph_ShellId thePare { for (BRepGraph_RefsFaceOfShell aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::FaceRef& aRef = aRefs.Faces().Entry(aRefIt.CurrentId()); - if (aRef.FaceDefId == BRepGraph_FaceId(theChild.Index)) + if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) { return aRefIt.Index(); } @@ -1065,11 +1076,10 @@ int BRepGraph_ParentExplorer::findShellChildStep(const BRepGraph_ShellId thePare return -1; } - for (int aRefIdx = 0; aRefIdx < aShell.FreeChildRefIds.Length(); ++aRefIdx) + for (int aRefIdx = 0; aRefIdx < aShell.AuxChildRefIds.Length(); ++aRefIdx) { - const BRepGraphInc::ChildRef& aRef = - aRefs.Children().Entry(aShell.FreeChildRefIds.Value(aRefIdx)); - if (!aRef.IsRemoved && aRef.ChildDefId == theChild) + const BRepGraph_ChildRefId aRefId = aShell.AuxChildRefIds.Value(aRefIdx); + if (!aRefs.IsRemoved(aRefId) && aRefs.ChildNode(aRefId) == theChild) { return aShell.FaceRefIds.Length() + aRefIdx; } @@ -1089,8 +1099,7 @@ int BRepGraph_ParentExplorer::findFaceChildStep(const BRepGraph_FaceId theParent { for (BRepGraph_RefsWireOfFace aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aRefIt.CurrentId()); - if (aRef.WireDefId == BRepGraph_WireId(theChild.Index)) + if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) { return aRefIt.Index(); } @@ -1102,8 +1111,7 @@ int BRepGraph_ParentExplorer::findFaceChildStep(const BRepGraph_FaceId theParent { for (BRepGraph_RefsVertexOfFace aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::VertexRef& aRef = aRefs.Vertices().Entry(aRefIt.CurrentId()); - if (aRef.VertexDefId == BRepGraph_VertexId(theChild.Index)) + if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) { return aFace.WireRefIds.Length() + aRefIt.Index(); } @@ -1119,8 +1127,7 @@ int BRepGraph_ParentExplorer::findWireCoEdgeStep(const BRepGraph_WireId thePar { for (BRepGraph_RefsCoEdgeOfWire aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::CoEdgeRef& aRef = myGraph->Refs().CoEdges().Entry(aRefIt.CurrentId()); - if (aRef.CoEdgeDefId == theChild) + if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) { return aRefIt.Index(); } @@ -1135,8 +1142,7 @@ int BRepGraph_ParentExplorer::findEdgeVertexStep(const BRepGraph_EdgeId thePar { for (BRepGraph_RefsVertexOfEdge aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::VertexRef& aRef = myGraph->Refs().Vertices().Entry(aRefIt.CurrentId()); - if (aRef.VertexDefId == theChild) + if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) { return aRefIt.Index(); } @@ -1183,73 +1189,8 @@ void BRepGraph_ParentExplorer::popFrame() TopLoc_Location BRepGraph_ParentExplorer::stepLocation(const BRepGraph_NodeId theParent, const int theRefIdx) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); const BRepGraph::RefsView& aRefs = myGraph->Refs(); - - switch (theParent.NodeKind) - { - case BRepGraph_NodeId::Kind::Product: - return TopLoc_Location(); - case BRepGraph_NodeId::Kind::Compound: { - return aRefs.Children() - .Entry(aRefs.Children().IdsOf(BRepGraph_CompoundId(theParent.Index)).Value(theRefIdx)) - .LocalLocation; - } - case BRepGraph_NodeId::Kind::CompSolid: { - return aRefs.Solids() - .Entry(aRefs.Solids().IdsOf(BRepGraph_CompSolidId(theParent.Index)).Value(theRefIdx)) - .LocalLocation; - } - case BRepGraph_NodeId::Kind::Solid: { - const BRepGraphInc::SolidDef& aSolid = - aTopo.Solids().Definition(BRepGraph_SolidId(theParent.Index)); - if (theRefIdx < aSolid.ShellRefIds.Length()) - { - return aRefs.Shells().Entry(aSolid.ShellRefIds.Value(theRefIdx)).LocalLocation; - } - return aRefs.Children() - .Entry(aSolid.FreeChildRefIds.Value(theRefIdx - aSolid.ShellRefIds.Length())) - .LocalLocation; - } - case BRepGraph_NodeId::Kind::Shell: { - const BRepGraphInc::ShellDef& aShell = - aTopo.Shells().Definition(BRepGraph_ShellId(theParent.Index)); - if (theRefIdx < aShell.FaceRefIds.Length()) - { - return aRefs.Faces().Entry(aShell.FaceRefIds.Value(theRefIdx)).LocalLocation; - } - return aRefs.Children() - .Entry(aShell.FreeChildRefIds.Value(theRefIdx - aShell.FaceRefIds.Length())) - .LocalLocation; - } - case BRepGraph_NodeId::Kind::Face: { - const BRepGraphInc::FaceDef& aFace = - aTopo.Faces().Definition(BRepGraph_FaceId(theParent.Index)); - if (theRefIdx < aFace.WireRefIds.Length()) - { - return aRefs.Wires().Entry(aFace.WireRefIds.Value(theRefIdx)).LocalLocation; - } - return aRefs.Vertices() - .Entry(aFace.VertexRefIds.Value(theRefIdx - aFace.WireRefIds.Length())) - .LocalLocation; - } - case BRepGraph_NodeId::Kind::Wire: - return aRefs.CoEdges() - .Entry(aRefs.CoEdges().IdsOf(BRepGraph_WireId(theParent.Index)).Value(theRefIdx)) - .LocalLocation; - case BRepGraph_NodeId::Kind::Edge: { - const BRepGraphInc::EdgeDef& anEdge = - aTopo.Edges().Definition(BRepGraph_EdgeId(theParent.Index)); - const BRepGraph_VertexRefId aVertexRefId = edgeVertexRefIdAt(anEdge, theRefIdx); - if (!aVertexRefId.IsValid()) - { - return TopLoc_Location(); - } - return aRefs.Vertices().Entry(aVertexRefId).LocalLocation; - } - default: - return TopLoc_Location(); - } + return aRefs.LocalLocation(aRefs.RefAtStep(theParent, theRefIdx)); } //================================================================================================= @@ -1257,67 +1198,6 @@ TopLoc_Location BRepGraph_ParentExplorer::stepLocation(const BRepGraph_NodeId th TopAbs_Orientation BRepGraph_ParentExplorer::stepOrientation(const BRepGraph_NodeId theParent, const int theRefIdx) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); const BRepGraph::RefsView& aRefs = myGraph->Refs(); - - switch (theParent.NodeKind) - { - case BRepGraph_NodeId::Kind::Product: - return TopAbs_FORWARD; - case BRepGraph_NodeId::Kind::Compound: - return aRefs.Children() - .Entry(aRefs.Children().IdsOf(BRepGraph_CompoundId(theParent.Index)).Value(theRefIdx)) - .Orientation; - case BRepGraph_NodeId::Kind::CompSolid: - return aRefs.Solids() - .Entry(aRefs.Solids().IdsOf(BRepGraph_CompSolidId(theParent.Index)).Value(theRefIdx)) - .Orientation; - case BRepGraph_NodeId::Kind::Solid: { - const BRepGraphInc::SolidDef& aSolid = - aTopo.Solids().Definition(BRepGraph_SolidId(theParent.Index)); - if (theRefIdx < aSolid.ShellRefIds.Length()) - { - return aRefs.Shells().Entry(aSolid.ShellRefIds.Value(theRefIdx)).Orientation; - } - return aRefs.Children() - .Entry(aSolid.FreeChildRefIds.Value(theRefIdx - aSolid.ShellRefIds.Length())) - .Orientation; - } - case BRepGraph_NodeId::Kind::Shell: { - const BRepGraphInc::ShellDef& aShell = - aTopo.Shells().Definition(BRepGraph_ShellId(theParent.Index)); - if (theRefIdx < aShell.FaceRefIds.Length()) - { - return aRefs.Faces().Entry(aShell.FaceRefIds.Value(theRefIdx)).Orientation; - } - return aRefs.Children() - .Entry(aShell.FreeChildRefIds.Value(theRefIdx - aShell.FaceRefIds.Length())) - .Orientation; - } - case BRepGraph_NodeId::Kind::Face: { - const BRepGraphInc::FaceDef& aFace = - aTopo.Faces().Definition(BRepGraph_FaceId(theParent.Index)); - if (theRefIdx < aFace.WireRefIds.Length()) - { - return aRefs.Wires().Entry(aFace.WireRefIds.Value(theRefIdx)).Orientation; - } - return aRefs.Vertices() - .Entry(aFace.VertexRefIds.Value(theRefIdx - aFace.WireRefIds.Length())) - .Orientation; - } - case BRepGraph_NodeId::Kind::Wire: - return TopAbs_FORWARD; - case BRepGraph_NodeId::Kind::Edge: { - const BRepGraphInc::EdgeDef& anEdge = - aTopo.Edges().Definition(BRepGraph_EdgeId(theParent.Index)); - const BRepGraph_VertexRefId aVertexRefId = edgeVertexRefIdAt(anEdge, theRefIdx); - if (!aVertexRefId.IsValid()) - { - return TopAbs_FORWARD; - } - return aRefs.Vertices().Entry(aVertexRefId).Orientation; - } - default: - return TopAbs_FORWARD; - } + return aRefs.Orientation(aRefs.RefAtStep(theParent, theRefIdx)); } \ No newline at end of file diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx index 6f21fb252c..3c4abfce9d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx @@ -52,6 +52,14 @@ class BRepGraph_ParentExplorer public: DEFINE_STANDARD_ALLOC + //! Relationship kind between Current() and CurrentChild(). + enum class LinkKind + { + None, //!< No current branch step. + Reference, //!< Current() owns CurrentChild() through a RefId. + Structural, //!< Current() reaches CurrentChild() through a structural non-ref link. + }; + //! Upward traversal strategy. enum class TraversalMode { @@ -112,6 +120,22 @@ public: return {}; } + //! Returns the immediate child of Current() on the currently emitted branch. + //! Returns invalid NodeId when no current ancestor is available. + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId CurrentChild() const; + + //! Returns how Current() is linked to CurrentChild(). + [[nodiscard]] Standard_EXPORT LinkKind CurrentLinkKind() const; + + //! Returns the exact parent-owned RefId linking Current() to CurrentChild(), + //! when that branch step is represented by a reference entry. + //! + //! 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. + [[nodiscard]] Standard_EXPORT BRepGraph_RefId CurrentRef() const; + //! Accumulated location at the starting node of the current branch. [[nodiscard]] Standard_EXPORT const TopLoc_Location& LeafLocation() const; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx new file mode 100644 index 0000000000..6e28bd9bed --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx @@ -0,0 +1,297 @@ +// 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_Vector& aVec = + myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; + if (theRef.Index >= aVec.Length()) + { + return aVec.SetValue(theRef.Index, CacheSlot()); + } + return aVec.ChangeValue(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_Vector& aVec = + myKinds.Value(theKindSlot).myRefKinds[aRefKindIdx].mySlots; + if (theRef.Index >= aVec.Length()) + { + return nullptr; + } + return &aVec.Value(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_Vector& aVec = + myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; + if (theRef.Index < aVec.Length()) + { + CacheSlot& aSlot = aVec.ChangeValue(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_Vector& aVec = + myKinds.Value(theKindSlot).myRefKinds[aRefKindIdx].mySlots; + if (theRef.Index < aVec.Length()) + { + const CacheSlot& aSlot = aVec.Value(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_Vector& aVec = + myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; + if (theRef.Index >= aVec.Length()) + { + return false; + } + + CacheSlot& aSlot = aVec.ChangeValue(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 new file mode 100644 index 0000000000..329ebfab7e --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.hxx @@ -0,0 +1,177 @@ +// 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 Build() 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 Build(). + 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 Build() 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_Vector 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_Vector 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_RefsIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx index 5de37502c9..a1e0727a0c 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx @@ -27,11 +27,12 @@ //! definitions. namespace BRepGraph_RefsIterator { -template +template struct BaseTraits { using ParentId = ParentIdT; using RefId = RefIdT; + using RefEntry = RefEntryT; }; template @@ -47,7 +48,8 @@ inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph return theGraph.Topo().Gen().TopoEntity(theChildId); } -struct ShellOfSolidTraits : public BaseTraits +struct ShellOfSolidTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -66,13 +68,14 @@ struct ShellOfSolidTraits : public BaseTraits +struct FaceOfShellTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -91,13 +94,40 @@ struct FaceOfShellTraits : public BaseTraits +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_Vector& 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; + } +}; + +struct WireOfFaceTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -116,13 +146,14 @@ struct WireOfFaceTraits : public BaseTraits +struct VertexOfFaceTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -141,13 +172,14 @@ struct VertexOfFaceTraits : public BaseTraits +struct CoEdgeOfWireTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -166,13 +198,14 @@ struct CoEdgeOfWireTraits : public BaseTraits +struct SolidOfCompSolidTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -191,13 +224,40 @@ struct SolidOfCompSolidTraits : public BaseTraits +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_Vector& 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; + } +}; + +struct ChildOfCompoundTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -216,13 +276,14 @@ struct ChildOfCompoundTraits : public BaseTraits +struct OccurrenceOfProductTraits + : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { @@ -241,7 +302,8 @@ struct OccurrenceOfProductTraits : public BaseTraitsValue(myIndex)); + const typename TraitsT::RefEntry& aRef = TraitsT::Ref(myGraph, myRefIds->Value(myIndex)); if (!aRef.IsRemoved) { - const BRepGraphInc::BaseDef* aChildDef = childBaseDef(myGraph, TraitsT::ChildIdOf(aRef)); + const BRepGraphInc::BaseDef* aChildDef = + childBaseDef(myGraph, TraitsT::ChildIdOf(myGraph, aRef)); if (aChildDef != nullptr && !aChildDef->IsRemoved) { return; @@ -403,12 +466,16 @@ 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 = BRepGraph_RefsIterator::RefsOfParent; +using BRepGraph_RefsChildOfSolid = + BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsSolidOfCompSolid = BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsChildOfCompound = diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx index dbbb68759f..cd6b491887 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx @@ -268,6 +268,232 @@ const NCollection_Vector& BRepGraph::RefsView::Occurr //================================================================================================= +BRepGraph_RefId BRepGraph::RefsView::RefAtStep(const BRepGraph_NodeId theParent, + const int theStep) const +{ + if (!theParent.IsValid() || theStep < 0) + { + return BRepGraph_RefId(); + } + + 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(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); + } + case BRepGraph_NodeId::Kind::Edge: { + const BRepGraphInc::EdgeDef& anEdge = + myGraph->Topo().Edges().Definition(BRepGraph_EdgeId::FromNodeId(theParent)); + if (theStep == 0) + { + return anEdge.StartVertexRefId; + } + if (theStep == 1) + { + return anEdge.EndVertexRefId; + } + + const int anInternalIdx = theStep - 2; + return anInternalIdx < anEdge.InternalVertexRefIds.Length() + ? anEdge.InternalVertexRefIds.Value(anInternalIdx) + : 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(); + } + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Occurrence: + case BRepGraph_NodeId::Kind::Vertex: + return BRepGraph_RefId(); + } + + return BRepGraph_RefId(); +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraph::RefsView::ChildNode(const BRepGraph_RefId theRef) const +{ + if (!theRef.IsValid()) + { + return BRepGraph_NodeId(); + } + + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Shell: + return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).ShellDefId; + case BRepGraph_RefId::Kind::Face: + return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).FaceDefId; + 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; + case BRepGraph_RefId::Kind::Vertex: + return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).VertexDefId; + case BRepGraph_RefId::Kind::Solid: + return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).SolidDefId; + case BRepGraph_RefId::Kind::Child: + return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).ChildDefId; + case BRepGraph_RefId::Kind::Occurrence: + return Occurrences().Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)).OccurrenceDefId; + } + + return BRepGraph_NodeId(); +} + +//================================================================================================= + +bool BRepGraph::RefsView::IsRemoved(const BRepGraph_RefId theRef) const +{ + if (!theRef.IsValid()) + { + return true; + } + + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Shell: + return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).IsRemoved; + case BRepGraph_RefId::Kind::Face: + return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).IsRemoved; + 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; + case BRepGraph_RefId::Kind::Vertex: + return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).IsRemoved; + case BRepGraph_RefId::Kind::Solid: + return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).IsRemoved; + case BRepGraph_RefId::Kind::Child: + return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).IsRemoved; + case BRepGraph_RefId::Kind::Occurrence: + return Occurrences().Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)).IsRemoved; + } + + return true; +} + +//================================================================================================= + +TopLoc_Location BRepGraph::RefsView::LocalLocation(const BRepGraph_RefId theRef) const +{ + if (!theRef.IsValid()) + { + return TopLoc_Location(); + } + + 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(); + } + + return TopLoc_Location(); +} + +//================================================================================================= + +TopAbs_Orientation BRepGraph::RefsView::Orientation(const BRepGraph_RefId theRef) const +{ + if (!theRef.IsValid()) + { + return TopAbs_FORWARD; + } + + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Shell: + return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).Orientation; + case BRepGraph_RefId::Kind::Face: + return 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; + case BRepGraph_RefId::Kind::Vertex: + return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).Orientation; + case BRepGraph_RefId::Kind::Solid: + return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).Orientation; + case BRepGraph_RefId::Kind::Child: + return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).Orientation; + case BRepGraph_RefId::Kind::Occurrence: + return TopAbs_FORWARD; + } + + return TopAbs_FORWARD; +} + +//================================================================================================= + const NCollection_Vector& BRepGraph::RefsView::SolidOps::IdsOf( const BRepGraph_CompSolidId theCompSolid) const { diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx index 183533c97e..dbf15e9ba2 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx @@ -266,6 +266,26 @@ 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; + private: friend class BRepGraph; friend struct BRepGraph_Data; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx new file mode 100644 index 0000000000..26d09b41f3 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx @@ -0,0 +1,452 @@ +// 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_RelatedIterator_HeaderFile +#define _BRepGraph_RelatedIterator_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Single-level iterator over semantically related topology nodes. +//! +//! The iterator yields immediate related nodes for one source node together +//! with the relation kind explaining why each node is returned. Results are not +//! deduplicated; callers that need uniqueness should filter on top. +class BRepGraph_RelatedIterator +{ +public: + enum class RelationKind + { + ChildShell, + FreeChild, + ChildFace, + BoundaryEdge, + AdjacentFace, + OuterWire, + ReferencedByFace, + IncidentVertex, + WireCoEdge, + IncidentEdge, + ParentEdge, + OwningFace, + SeamPair, + ChildEntity, + ChildSolid, + ChildOccurrence, + ReferencedProduct, + ParentProduct, + ParentOccurrence, + }; + + enum class Stage + { + First, + Second, + Third, + Finished, + }; + + BRepGraph_RelatedIterator(const BRepGraph& theGraph, const BRepGraph_NodeId theNode) + : myGraph(&theGraph), + myNode(theNode) + { + advance(); + } + + [[nodiscard]] bool More() const { return myHasCurrent; } + + void Next() + { + if (!myHasCurrent) + { + return; + } + advance(); + } + + [[nodiscard]] const BRepGraph_NodeId& Current() const { return myCurrent; } + + [[nodiscard]] RelationKind CurrentRelation() const { return myRelation; } + +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; + } + + template + [[nodiscard]] bool advanceRefChildren(IteratorT theIterator, const RelationKind theRelation) + { + for (; theIterator.More(); theIterator.Next()) + { + if (theIterator.Index() < myIndex) + { + continue; + } + + myIndex = theIterator.Index() + 1; + const BRepGraph_NodeId aChildNode = myGraph->Refs().ChildNode(theIterator.CurrentId()); + return setCurrent(aChildNode, theRelation); + } + + return false; + } + + template + [[nodiscard]] bool advanceDefChildren(IteratorT theIterator, const RelationKind theRelation) + { + for (; theIterator.More(); theIterator.Next()) + { + if (theIterator.Index() < myIndex) + { + continue; + } + + myIndex = theIterator.Index() + 1; + return setCurrent(BRepGraph_NodeId(theIterator.CurrentId()), theRelation); + } + + 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; + } + + 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_Vector& aFaces = + myGraph->Topo().Edges().Faces(aCoEdgeIt.Current().EdgeDefId); + for (; myDeepIndex < aFaces.Length(); ++myDeepIndex) + { + const BRepGraph_FaceId anAdjacentFaceId = aFaces.Value(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 advanceEdgeVertex() + { + return advanceDefChildren( + BRepGraph_DefsVertexOfEdge(*myGraph, BRepGraph_EdgeId::FromNodeId(myNode)), + RelationKind::IncidentVertex); + } + + void advance() + { + myHasCurrent = false; + if (!myNode.IsValid() || myGraph->Topo().Gen().IsRemoved(myNode)) + { + return; + } + + for (;;) + { + switch (myNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Solid: { + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::FromNodeId(myNode); + if (myStage == Stage::First) + { + if (advanceRefChildren(BRepGraph_RefsShellOfSolid(*myGraph, aSolidId), + RelationKind::ChildShell)) + { + return; + } + myStage = Stage::Second; + myIndex = 0; + } + if (myStage == Stage::Second) + { + if (advanceDefChildren(BRepGraph_DefsChildOfSolid(*myGraph, aSolidId), + RelationKind::FreeChild)) + { + return; + } + } + return; + } + case BRepGraph_NodeId::Kind::Shell: { + const BRepGraph_ShellId aShellId = BRepGraph_ShellId::FromNodeId(myNode); + if (myStage == Stage::First) + { + if (advanceRefChildren(BRepGraph_RefsFaceOfShell(*myGraph, aShellId), + RelationKind::ChildFace)) + { + return; + } + myStage = Stage::Second; + myIndex = 0; + } + if (myStage == Stage::Second) + { + if (advanceDefChildren(BRepGraph_DefsChildOfShell(*myGraph, aShellId), + RelationKind::FreeChild)) + { + return; + } + } + 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: { + const NCollection_Vector& aFaces = + myGraph->Topo().Edges().Faces(BRepGraph_EdgeId::FromNodeId(myNode)); + if (myStage == Stage::First) + { + for (; myIndex < aFaces.Length(); ++myIndex) + { + const BRepGraph_NodeId aFaceNode(aFaces.Value(myIndex)); + if (setCurrent(aFaceNode, RelationKind::ReferencedByFace)) + { + ++myIndex; + return; + } + } + myStage = Stage::Second; + myIndex = 0; + } + if (myStage == Stage::Second) + { + if (advanceEdgeVertex()) + { + return; + } + } + return; + } + case BRepGraph_NodeId::Kind::Wire: { + if (advanceRefChildren( + BRepGraph_RefsCoEdgeOfWire(*myGraph, BRepGraph_WireId::FromNodeId(myNode)), + RelationKind::WireCoEdge)) + { + return; + } + return; + } + case BRepGraph_NodeId::Kind::Vertex: { + const NCollection_Vector& anEdges = + myGraph->Topo().Vertices().Edges(BRepGraph_VertexId::FromNodeId(myNode)); + for (; myIndex < anEdges.Length(); ++myIndex) + { + if (setCurrent(BRepGraph_NodeId(anEdges.Value(myIndex)), RelationKind::IncidentEdge)) + { + ++myIndex; + return; + } + } + return; + } + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraphInc::CoEdgeDef& aCoEdgeDef = + myGraph->Topo().CoEdges().Definition(BRepGraph_CoEdgeId::FromNodeId(myNode)); + if (myStage == Stage::First) + { + myStage = Stage::Second; + if (setCurrent(BRepGraph_NodeId(aCoEdgeDef.EdgeDefId), RelationKind::ParentEdge)) + { + return; + } + } + if (myStage == Stage::Second) + { + myStage = Stage::Third; + if (setCurrent(BRepGraph_NodeId(aCoEdgeDef.FaceDefId), RelationKind::OwningFace)) + { + return; + } + } + if (myStage == Stage::Third) + { + myStage = Stage::Finished; + if (setCurrent(BRepGraph_NodeId(aCoEdgeDef.SeamPairId), RelationKind::SeamPair)) + { + return; + } + } + return; + } + case BRepGraph_NodeId::Kind::Compound: + if (advanceDefChildren( + BRepGraph_DefsChildOfCompound(*myGraph, BRepGraph_CompoundId::FromNodeId(myNode)), + RelationKind::ChildEntity)) + { + return; + } + return; + case BRepGraph_NodeId::Kind::CompSolid: + if (advanceDefChildren( + BRepGraph_DefsSolidOfCompSolid(*myGraph, BRepGraph_CompSolidId::FromNodeId(myNode)), + RelationKind::ChildSolid)) + { + return; + } + return; + case BRepGraph_NodeId::Kind::Product: { + if (advanceDefChildren( + BRepGraph_DefsOccurrenceOfProduct(*myGraph, + BRepGraph_ProductId::FromNodeId(myNode)), + RelationKind::ChildOccurrence)) + { + return; + } + return; + } + case BRepGraph_NodeId::Kind::Occurrence: { + const BRepGraph_OccurrenceId anOccurrenceId = BRepGraph_OccurrenceId::FromNodeId(myNode); + if (myStage == Stage::First) + { + myStage = Stage::Second; + if (setCurrent(BRepGraph_NodeId(myGraph->Topo().Occurrences().Product(anOccurrenceId)), + RelationKind::ReferencedProduct)) + { + return; + } + } + if (myStage == Stage::Second) + { + myStage = Stage::Third; + if (setCurrent( + BRepGraph_NodeId(myGraph->Topo().Occurrences().ParentProduct(anOccurrenceId)), + RelationKind::ParentProduct)) + { + return; + } + } + if (myStage == Stage::Third) + { + myStage = Stage::Finished; + if (setCurrent( + BRepGraph_NodeId(myGraph->Topo().Occurrences().ParentOccurrence(anOccurrenceId)), + RelationKind::ParentOccurrence)) + { + return; + } + } + return; + } + } + } + } + +private: + const BRepGraph* myGraph; + BRepGraph_NodeId myNode; + BRepGraph_NodeId myCurrent; + RelationKind myRelation = RelationKind::ChildEntity; + Stage myStage = Stage::First; + int myIndex = 0; + int myInnerIndex = 0; + int myDeepIndex = 0; + bool myHasCurrent = false; +}; + +#endif // _BRepGraph_RelatedIterator_HeaderFile \ No newline at end of file diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx index 8398374555..6d3ff8abc3 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx @@ -28,8 +28,6 @@ #include #include -//================================================================================================= - gp_Pnt BRepGraph_Tool::Vertex::Pnt(const BRepGraph& theGraph, const BRepGraphInc::VertexUsage& theRef) { diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx index dc4dc0b945..ca9bcbeb52 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx @@ -488,7 +488,7 @@ const BRepGraphInc::CoEdgeDef* BRepGraph::TopoView::EdgeOps::FindPCurve( { aFirstMatch = &aCoEdge; } - if (aCoEdge.Sense == theOrientation) + if (aCoEdge.Orientation == theOrientation) { return &aCoEdge; } @@ -498,6 +498,65 @@ const BRepGraphInc::CoEdgeDef* BRepGraph::TopoView::EdgeOps::FindPCurve( //================================================================================================= +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_Vector& 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_Vector& 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 { return myGraph->myData->myIncStorage.NbVertices(); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx index db75079e48..8bf0d8b9ed 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx @@ -117,6 +117,24 @@ public: 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; + + //! 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; + private: friend class TopoView; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx index c589084b56..2f0c984ebc 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx @@ -160,7 +160,9 @@ BRepGraph_TransientCache::CacheSlot& BRepGraph_TransientCache::changeSlot( const int theKindSlot) { ensureKind(theKindSlot); - const int aKindIdx = static_cast(theNode.NodeKind); + const int aKindIdx = static_cast(theNode.NodeKind); + Standard_ASSERT_VOID(aKindIdx >= 0 && aKindIdx < THE_KIND_COUNT, + "BRepGraph_TransientCache: NodeKind out of range"); NCollection_Vector& aVec = myKinds.ChangeValue(theKindSlot).myNodeKinds[aKindIdx].mySlots; if (theNode.Index >= aVec.Length()) @@ -181,7 +183,9 @@ const BRepGraph_TransientCache::CacheSlot* BRepGraph_TransientCache::seekSlot( return nullptr; } - const int aKindIdx = static_cast(theNode.NodeKind); + const int aKindIdx = static_cast(theNode.NodeKind); + Standard_ASSERT_VOID(aKindIdx >= 0 && aKindIdx < THE_KIND_COUNT, + "BRepGraph_TransientCache: NodeKind out of range"); const NCollection_Vector& aVec = myKinds.Value(theKindSlot).myNodeKinds[aKindIdx].mySlots; if (theNode.Index >= aVec.Length()) @@ -392,76 +396,26 @@ bool BRepGraph_TransientCache::Remove(const BRepGraph_NodeId theNode, const int //================================================================================================= -bool BRepGraph_TransientCache::HasCacheValues(const BRepGraph_NodeId theNode) const +int BRepGraph_TransientCache::CollectCacheKindSlots(const BRepGraph_NodeId theNode, + const uint32_t theCurrentSubtreeGen, + int theSlots[]) const { + int aCount = 0; if (!theNode.IsValid()) { - return false; + return aCount; } for (int aKindSlot = 0; aKindSlot < myKinds.Length(); ++aKindSlot) { const CacheSlot* aSlot = seekSlot(theNode, aKindSlot); - if (aSlot != nullptr && !aSlot->Value.IsNull()) + if (aSlot != nullptr && !aSlot->Value.IsNull() + && aSlot->StoredSubtreeGen == theCurrentSubtreeGen) { - return true; + theSlots[aCount++] = aKindSlot; } } - return false; -} - -//================================================================================================= - -NCollection_Vector> BRepGraph_TransientCache::CacheKinds( - const BRepGraph_NodeId theNode) const -{ - NCollection_Vector> aKinds; - if (!theNode.IsValid()) - { - return aKinds; - } - - for (int aKindSlot = 0; aKindSlot < myKinds.Length(); ++aKindSlot) - { - const CacheSlot* aSlot = seekSlot(theNode, aKindSlot); - if (aSlot != nullptr && !aSlot->Value.IsNull()) - { - const occ::handle aKind = - BRepGraph_CacheKindRegistry::FindKind(aKindSlot); - if (!aKind.IsNull()) - { - aKinds.Append(aKind); - } - } - } - return aKinds; -} - -//================================================================================================= - -void BRepGraph_TransientCache::TransferCacheValues(const BRepGraph_TransientCache& theSrcCache, - const BRepGraph_NodeId theSrcNode, - const BRepGraph_NodeId theDstNode, - const uint32_t theDstSubtreeGen) -{ - if (!theSrcNode.IsValid() || !theDstNode.IsValid()) - { - return; - } - - for (int aKindSlot = 0; aKindSlot < theSrcCache.myKinds.Length(); ++aKindSlot) - { - const CacheSlot* aSrcSlot = theSrcCache.seekSlot(theSrcNode, aKindSlot); - if (aSrcSlot == nullptr || aSrcSlot->Value.IsNull()) - { - continue; - } - - std::unique_lock aLock(myMutex); - CacheSlot& aDstSlot = changeSlot(theDstNode, aKindSlot); - aDstSlot.Value = aSrcSlot->Value; - aDstSlot.StoredSubtreeGen = theDstSubtreeGen; - } + return aCount; } //================================================================================================= diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx index cb962b2d4b..1a6c3473ac 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx @@ -279,18 +279,16 @@ public: //! Remove a cached value using a pre-resolved cache-kind slot. [[nodiscard]] Standard_EXPORT bool Remove(const BRepGraph_NodeId theNode, const int theKindSlot); - //! True if any cached values are stored for this node (any cache kind). - [[nodiscard]] Standard_EXPORT bool HasCacheValues(const BRepGraph_NodeId theNode) const; - - //! Return all registered cache kinds that have non-null entries for this node. - [[nodiscard]] Standard_EXPORT NCollection_Vector> CacheKinds( - const BRepGraph_NodeId theNode) const; - - //! Transfer all cached values from a source cache to a destination node. - Standard_EXPORT void TransferCacheValues(const BRepGraph_TransientCache& theSrcCache, - const BRepGraph_NodeId theSrcNode, - const BRepGraph_NodeId theDstNode, - const uint32_t theDstSubtreeGen); + //! 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]); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx index 5903d6dfea..079fca2402 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx @@ -744,7 +744,7 @@ void checkWireConnectivity(const BRepGraph& theGra theGraph.Topo().Edges().Definition(aNextCoEdge.EdgeDefId); // Resolve oriented end vertex of current edge. - const BRepGraph_VertexRefId aCurrEndRefId = (aCurrCoEdge.Sense == TopAbs_FORWARD) + const BRepGraph_VertexRefId aCurrEndRefId = (aCurrCoEdge.Orientation == TopAbs_FORWARD) ? aCurrEdge.EndVertexRefId : aCurrEdge.StartVertexRefId; const BRepGraph_NodeId aCurrEnd = @@ -753,7 +753,7 @@ void checkWireConnectivity(const BRepGraph& theGra : BRepGraph_NodeId(); // Resolve oriented start vertex of next edge. - const BRepGraph_VertexRefId aNextStartRefId = (aNextCoEdge.Sense == TopAbs_FORWARD) + const BRepGraph_VertexRefId aNextStartRefId = (aNextCoEdge.Orientation == TopAbs_FORWARD) ? aNextEdge.StartVertexRefId : aNextEdge.EndVertexRefId; const BRepGraph_NodeId aNextStart = diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx index e7b53d75a8..e941992bc0 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx @@ -86,11 +86,11 @@ private: //! the start vertex def id, for REVERSED returns the end vertex def id. template static BRepGraph_NodeId orientedStartVertex(const BRepGraphInc::EdgeDef& theEdge, - const TopAbs_Orientation theSense, + const TopAbs_Orientation theOrientation, const VertexRefLookupT& theVtxRefLookup) { const BRepGraph_VertexRefId aRefId = - (theSense == TopAbs_FORWARD) ? theEdge.StartVertexRefId : theEdge.EndVertexRefId; + (theOrientation == TopAbs_FORWARD) ? theEdge.StartVertexRefId : theEdge.EndVertexRefId; if (!aRefId.IsValid()) return BRepGraph_NodeId(); return theVtxRefLookup(aRefId); @@ -100,11 +100,11 @@ private: //! the end vertex def id, for REVERSED returns the start vertex def id. template static BRepGraph_NodeId orientedEndVertex(const BRepGraphInc::EdgeDef& theEdge, - const TopAbs_Orientation theSense, + const TopAbs_Orientation theOrientation, const VertexRefLookupT& theVtxRefLookup) { const BRepGraph_VertexRefId aRefId = - (theSense == TopAbs_FORWARD) ? theEdge.EndVertexRefId : theEdge.StartVertexRefId; + (theOrientation == TopAbs_FORWARD) ? theEdge.EndVertexRefId : theEdge.StartVertexRefId; if (!aRefId.IsValid()) return BRepGraph_NodeId(); return theVtxRefLookup(aRefId); @@ -136,7 +136,7 @@ private: const BRepGraphInc::CoEdgeDef& aPrevCoEdge = theCoEdgeLookup(aPrevRef.DefId.Index); const BRepGraph_NodeId aPrevEnd = orientedEndVertex(theEdgeLookup(aPrevCoEdge.EdgeDefId.Index), - aPrevCoEdge.Sense, + aPrevCoEdge.Orientation, theVtxRefLookup); bool aFound = false; @@ -148,7 +148,7 @@ private: const BRepGraphInc::CoEdgeDef& aCandCoEdge = theCoEdgeLookup(aCandRef.DefId.Index); const BRepGraph_NodeId aCandStart = orientedStartVertex(theEdgeLookup(aCandCoEdge.EdgeDefId.Index), - aCandCoEdge.Sense, + aCandCoEdge.Orientation, theVtxRefLookup); if (aPrevEnd.IsValid() && aCandStart.IsValid() && aPrevEnd == aCandStart) diff --git a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake index 2cdcf59f8e..0af7229e46 100644 --- a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake +++ b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake @@ -3,6 +3,7 @@ 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 @@ -23,6 +24,7 @@ set(OCCT_BRepGraph_FILES BRepGraph_HistoryRecord.hxx BRepGraph_Layer.cxx BRepGraph_Layer.hxx + BRepGraph_LayerIterator.hxx BRepGraph_LayerRegistry.cxx BRepGraph_LayerRegistry.hxx BRepGraph_DeferredScope.hxx @@ -33,6 +35,7 @@ set(OCCT_BRepGraph_FILES BRepGraph_ParallelPolicy.hxx BRepGraph_RefId.hxx BRepGraph_RefUID.hxx + BRepGraph_RelatedIterator.hxx BRepGraph_RefsIterator.hxx BRepGraph_RefsView.cxx BRepGraph_RefsView.hxx @@ -43,6 +46,8 @@ set(OCCT_BRepGraph_FILES BRepGraph_ShapesView.hxx BRepGraph_Tool.cxx BRepGraph_Tool.hxx + BRepGraph_RefTransientCache.cxx + BRepGraph_RefTransientCache.hxx BRepGraph_TransientCache.cxx BRepGraph_TransientCache.hxx BRepGraph_WireExplorer.hxx diff --git a/src/ModelingData/TKBRep/BRepGraph/README.md b/src/ModelingData/TKBRep/BRepGraph/README.md index f2daefec63..2e498c97ae 100644 --- a/src/ModelingData/TKBRep/BRepGraph/README.md +++ b/src/ModelingData/TKBRep/BRepGraph/README.md @@ -15,7 +15,7 @@ BRepGraph provides a stable algorithm-facing API for: The goal is to make workflows like sewing, healing, compact, and deduplicate easier to implement and optimize. -## Current Model (March 2026) +## Current Model (April 2026) The runtime model is incidence-first: @@ -61,8 +61,8 @@ All queries and mutations go through lightweight view objects obtained from a `B | **TopoView** | `Topo()` | Const topology definition access, representation access, adjacency queries, and raw Product/Occurrence definition storage | | **UIDsView** | `UIDs()` | UID allocation, lookup, validity checking | | **ShapesView** | `Shapes()` | Cached `Shape()` access, fresh `Reconstruct()`, and FindNode/HasNode reverse lookup | -| **CacheView** | `Cache()` | Stable public transient cache access (Set/Get/Has/Remove per-node cached values). Low-level reserve, transfer, and explicit generation-aware access remain on `TransientCache()` for algorithm code. | -| **BuilderView** | `Builder()` | Mutations: AddProduct, AddOccurrence, RemoveNode, RemoveSubgraph, MutGuard accessors | +| **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. | +| **BuilderView** | `Builder()` | Mutations: AddProduct, AddOccurrence, RemoveNode, RemoveSubgraph, RemoveRef (with orphan pruning), SetCoEdgePCurve, ClearFaceMesh, ClearEdgePolygon3D, AppendFlattenedShape, AppendFullShape, rep creation (CreateTriangulationRep, CreatePolygon3DRep, CreatePolygonOnTriRep), ValidateMutationBoundary, MutGuard accessors | | **RefsView** | `Refs()` | Reference entry access, RefUID lookup, VersionStamp for refs | | **PathView** | `Paths()` | Assembly-aware queries (RootProducts, IsAssembly, IsPart, NbComponents, Component) and path traversal (GlobalLocation, GlobalOrientation, PathsTo, NodeLocations, CommonAncestor) | @@ -77,7 +77,7 @@ Keep `Refs()` as the home for APIs returning `RefId` vectors and reference-entry ### Non-View Helpers -Use `BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` directly for structural diagnostics and connected-component grouping. Lightweight one-off analysis helpers are expected to stay local to the consuming algorithm or test. +Use `BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` directly for structural diagnostics and connected-component grouping. `BRepGraph_RelatedIterator` provides single-level semantic traversal from any node, yielding immediate neighbours with a `RelationKind` tag (e.g. `AdjacentFace`, `BoundaryEdge`, `IncidentVertex`). `BRepGraph_LayerIterator` iterates over all registered layers in a `BRepGraph_LayerRegistry`. Lightweight one-off analysis helpers are expected to stay local to the consuming algorithm or test. ### Direct Subsystem Accessors @@ -85,6 +85,7 @@ Use `BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` directly for struct |----------|---------| | `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 | @@ -152,7 +153,7 @@ flowchart LR P5 --> D[IsDone] ``` -After topology population, `Build()` auto-creates a single root Product whose `ShapeRootId` points to the top-level topology node. This makes every BRepGraph intrinsically assembly-aware. +After topology population, `Build()` auto-creates a single root Product whose `ShapeRootId` points to the top-level topology node (controlled by `CreateAutoProduct` option, default true). This makes every BRepGraph intrinsically assembly-aware. When `CreateAutoProduct` is false (e.g. XCAF builder manages Products itself), the caller is responsible for creating Products. ### Reconstruct @@ -262,6 +263,21 @@ Can also start from a Product to descend through assembly occurrences into topol The vector-returning reverse lookup methods remain as convenience wrappers over the lazy enumeration layer. - `IsAncestorOf`, `AllNodesOnPath`, `DepthOfKind` +### RelatedIterator + +`BRepGraph_RelatedIterator` provides single-level semantic traversal from any node, yielding immediate neighbours with a `RelationKind` tag. Unlike ChildExplorer (which descends to a target kind) or ParentExplorer (which ascends), RelatedIterator stays at one level and returns all semantically related nodes: + +```cpp +for (BRepGraph_RelatedIterator anIt(aGraph, BRepGraph_NodeId(aFaceId)); anIt.More(); anIt.Next()) +{ + BRepGraph_NodeId aNeighbour = anIt.Current(); + BRepGraph_RelatedIterator::RelationKind aRel = anIt.CurrentRelation(); + // e.g. BoundaryEdge, AdjacentFace, OuterWire +} +``` + +Relation kinds include: `ChildShell`, `ChildFace`, `FreeChild`, `BoundaryEdge`, `AdjacentFace`, `OuterWire`, `ReferencedByFace`, `IncidentVertex`, `WireCoEdge`, `IncidentEdge`, `ParentEdge`, `OwningFace`, `SeamPair`, `ChildEntity`, `ChildSolid`, `ChildOccurrence`, `ReferencedProduct`, `ParentProduct`, `ParentOccurrence`. + ### Connected Components Disconnected topology is grouped on demand by walking from faces to their solid or shell roots with `BRepGraph_ParentExplorer`, or by descending from known roots with `BRepGraph_ChildExplorer`. There is no longer a packaged `SubGraph` container. @@ -291,7 +307,7 @@ layers are added explicitly via `LayerRegistry().RegisterLayer()`. - **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 mutation tracking +- **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) - **Survives mutations**: yes - **Examples**: `BRepGraph_ParamLayer`, `BRepGraph_RegularityLayer` @@ -306,16 +322,15 @@ const occ::handle aParamLayer = aGraph.LayerRegistry().FindLayer(); ``` -### TransientCache (`BRepGraph_TransientCache`) +### TransientCache (`BRepGraph_TransientCache`) and RefTransientCache (`BRepGraph_RefTransientCache`) -Centralized per-node cache for algorithm-computed attributes. Dense graph-local storage keyed by -registered cache-kind descriptors with O(1) slot access. NOT a Layer - cleared on Build() and Compact(). +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 Build() and Compact(). - **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_Vector` per cache kind, then per node kind, then per entity index - **Granularity**: one cached value per `(node, cache kind)` -- **Freshness**: SubtreeGen-validated. Each slot stores `StoredSubtreeGen`; on read, if it differs from the entity's current `SubtreeGen`, the attribute is marked dirty and recomputed lazily. +- **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) @@ -406,6 +421,11 @@ Use the explicit overload when the caller needs to override optional extraction - `ExtractRegularities` (default true): edge continuity across face pairs. - `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 + +`Builder().AppendFlattenedShape(shape)` appends faces without container nodes. `Builder().AppendFullShape(shape)` preserves the full hierarchy (Solid/Shell/Compound/CompSolid). Both accept `BRepGraphInc_Populate::Options` for controlling auto-Product creation and extraction passes. `Builder::AppendFull()` is the lower-level static API. ## Debug Validation @@ -443,11 +463,11 @@ if (!aResult.IsValid()) | **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_BuilderView.hxx/.cxx`, `BRepGraph_PathView.hxx/.cxx` | | **Refs** | `BRepGraph_VersionStamp.hxx/.cxx` | -| **Traversal** | `BRepGraph_ChildExplorer.hxx/.cxx`, `BRepGraph_ParentExplorer.hxx/.cxx`, `BRepGraph_TopologyPath.hxx`, `BRepGraph_PCurveContext.hxx` | +| **Traversal** | `BRepGraph_ChildExplorer.hxx/.cxx`, `BRepGraph_ParentExplorer.hxx/.cxx`, `BRepGraph_RelatedIterator.hxx`, `BRepGraph_TopologyPath.hxx`, `BRepGraph_PCurveContext.hxx` | | **Geometry** | `BRepGraph_Tool.hxx/.cxx` | | **Mutation** | `BRepGraph_MutGuard.hxx`, `BRepGraph_DeferredScope.hxx` | -| **Layers** | `BRepGraph_Layer.hxx/.cxx`, `BRepGraph_ParamLayer.hxx/.cxx`, `BRepGraph_RegularityLayer.hxx/.cxx` | -| **Transient Cache** | `BRepGraph_TransientCache.hxx/.cxx` | +| **Layers** | `BRepGraph_Layer.hxx/.cxx`, `BRepGraph_LayerIterator.hxx`, `BRepGraph_LayerRegistry.hxx/.cxx`, `BRepGraph_ParamLayer.hxx/.cxx`, `BRepGraph_RegularityLayer.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` | diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx index 0749ccc7f6..7a93690408 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx @@ -132,13 +132,13 @@ struct EdgeDef : public BaseDef //! //! Each coedge represents one edge-face binding with its parametric curve. //! Wires reference coedges rather than edges directly. -//! For seam edges, two coedges exist on the same face with opposite Sense, +//! For seam edges, two coedges exist on the same face with opposite Orientation, //! linked by SeamPairIdx. struct CoEdgeDef : public BaseDef { BRepGraph_EdgeId EdgeDefId; //!< Parent edge definition id BRepGraph_FaceId FaceDefId; //!< Face this coedge belongs to (invalid for free wires) - TopAbs_Orientation Sense = TopAbs_FORWARD; //!< Orientation relative to parent edge + TopAbs_Orientation Orientation = TopAbs_FORWARD; //!< Orientation relative to parent edge //! Typed representation id into Storage::myCurves2D (invalid for free-wire coedges). BRepGraph_Curve2DRepId Curve2DRepId; @@ -215,27 +215,26 @@ struct FaceDef : public BaseDef struct ShellDef : public BaseDef { bool IsClosed = false; //!< True if shell forms a watertight (closed) boundary. - NCollection_Vector FaceRefIds; //!< Face ref indices - NCollection_Vector FreeChildRefIds; //!< Non-face children (wires, edges) + NCollection_Vector FaceRefIds; //!< Face ref indices + NCollection_Vector AuxChildRefIds; //!< Non-face children (wires, edges) void InitVectors(const occ::handle& theAlloc) { - InitVec(FaceRefIds, theAlloc, 8); // typically 4-8 faces per shell - InitVec(FreeChildRefIds, theAlloc, 2); // typically 0 + InitVec(FaceRefIds, theAlloc, 8); // typically 4-8 faces per shell + InitVec(AuxChildRefIds, theAlloc, 2); // typically 0 } }; //! Solid entity: ordered shell references with local locations. struct SolidDef : public BaseDef { - NCollection_Vector ShellRefIds; //!< Shell ref indices - NCollection_Vector - FreeChildRefIds; //!< Non-shell children (edges, vertices) + NCollection_Vector ShellRefIds; //!< Shell ref indices + NCollection_Vector AuxChildRefIds; //!< Non-shell children (edges, vertices) void InitVectors(const occ::handle& theAlloc) { - InitVec(ShellRefIds, theAlloc, 2); // typically 1 - InitVec(FreeChildRefIds, theAlloc, 2); // typically 0 + InitVec(ShellRefIds, theAlloc, 2); // typically 1 + InitVec(AuxChildRefIds, theAlloc, 2); // typically 0 } }; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx index 5b0e4d2906..c44d275942 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx @@ -771,9 +771,9 @@ int registerExtractedEdge(BRepGraphInc_Storage& theStorage, //! Create a NodeUsage from a child shape, resolving its index via TShape lookup. //! Returns true if the ref was created successfully (child was found in storage). -bool makeFreeChildRef(const BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theChild, - BRepGraphInc::NodeUsage& theRef) +bool makeAuxChildRef(const BRepGraphInc_Storage& theStorage, + const TopoDS_Shape& theChild, + BRepGraphInc::NodeUsage& theRef) { const BRepGraph_NodeId* aChildNodeId = theStorage.FindNodeByTShape(theChild.TShape().get()); if (aChildNodeId == nullptr) @@ -1185,7 +1185,7 @@ void registerFaceData(BRepGraphInc_Storage& theStorage, aCoEdge.Id = BRepGraph_CoEdgeId(aCoEdgeIdx); aCoEdge.EdgeDefId = BRepGraph_EdgeId(anEdgeIdx); aCoEdge.FaceDefId = BRepGraph_FaceId(aFaceIdx); - aCoEdge.Sense = anEdgeData.OrientationInWire; + aCoEdge.Orientation = anEdgeData.OrientationInWire; // Populate CoEdge with PCurve and polygon data for this face context. if (!anEdgeData.PCurve2d.IsNull()) @@ -1209,7 +1209,7 @@ void registerFaceData(BRepGraphInc_Storage& theStorage, aSeamCoEdge.Id = BRepGraph_CoEdgeId(aSeamCoEdgeIdx); aSeamCoEdge.EdgeDefId = BRepGraph_EdgeId(anEdgeIdx); aSeamCoEdge.FaceDefId = BRepGraph_FaceId(aFaceIdx); - aSeamCoEdge.Sense = TopAbs_REVERSED; + aSeamCoEdge.Orientation = TopAbs_REVERSED; aSeamCoEdge.Curve2DRepId = BRepGraph_Curve2DRepId( getOrCreateCurve2DRep(theStorage, theRepDedup, anEdgeData.PCurve2dReversed)); aSeamCoEdge.ParamFirst = anEdgeData.PCFirstReversed; @@ -1461,12 +1461,12 @@ void traverseHierarchy(BRepGraphInc_Storage& theStorage, else if (aChild.ShapeType() == TopAbs_EDGE || aChild.ShapeType() == TopAbs_VERTEX) { BRepGraphInc::NodeUsage aCR; - if (makeFreeChildRef(theStorage, aChild, aCR)) + if (makeAuxChildRef(theStorage, aChild, aCR)) { const BRepGraph_SolidId aSolidId(aSolidIdx); const BRepGraph_ChildRefId aChildRefId = appendChildRef(theStorage, BRepGraph_SolidId(aSolidId.Index), aCR); - theStorage.ChangeSolid(aSolidId).FreeChildRefIds.Append(aChildRefId); + theStorage.ChangeSolid(aSolidId).AuxChildRefIds.Append(aChildRefId); } } } @@ -1502,12 +1502,12 @@ void traverseHierarchy(BRepGraphInc_Storage& theStorage, traverseHierarchy(theStorage, theFaceData, theRepDedup, aChild, aGlobalLoc); BRepGraphInc::NodeUsage aCR; - if (makeFreeChildRef(theStorage, aChild, aCR)) + if (makeAuxChildRef(theStorage, aChild, aCR)) { const BRepGraph_ShellId aShellId(aShellIdx); const BRepGraph_ChildRefId aChildRefId = appendChildRef(theStorage, BRepGraph_ShellId(aShellId.Index), aCR); - theStorage.ChangeShell(aShellId).FreeChildRefIds.Append(aChildRefId); + theStorage.ChangeShell(aShellId).AuxChildRefIds.Append(aChildRefId); } } } @@ -1552,7 +1552,7 @@ void traverseHierarchy(BRepGraphInc_Storage& theStorage, const int aCoEdgeIdx = theStorage.NbCoEdges() - 1; aCoEdge.Id = BRepGraph_CoEdgeId(aCoEdgeIdx); aCoEdge.EdgeDefId = BRepGraph_EdgeId::FromNodeId(*anEdgeNodeId); - aCoEdge.Sense = anEdge.Orientation(); + aCoEdge.Orientation = anEdge.Orientation(); // FaceDefId left invalid for free wires. // Curve2d left null for free wires. @@ -2062,7 +2062,8 @@ void BRepGraphInc_Populate::Perform(BRepGraphInc_Storage& theStorage, { const BRepGraph_NodeId* aNodeId = theStorage.myTShapeToNodeId.Seek(aChildIt.Value().TShape().get()); - if (aNodeId != nullptr && aNodeId->NodeKind == aRef->ChildDefId.NodeKind) + if (aNodeId != nullptr + && aNodeId->NodeKind == shapeTypeToNodeKind(aChildIt.Value().ShapeType())) aRef->ChildDefId = *aNodeId; } ++aRefOrd; @@ -2164,3 +2165,113 @@ void BRepGraphInc_Populate::AppendFlattened( theStorage.myIsDone = true; } + +//================================================================================================= + +void BRepGraphInc_Populate::Append(BRepGraphInc_Storage& theStorage, + const TopoDS_Shape& theShape, + const bool theParallel, + const Options& theOptions, + BRepGraph_ParamLayer* theParamLayer, + BRepGraph_RegularityLayer* theRegularityLayer, + const occ::handle& theTmpAlloc) +{ + if (theShape.IsNull()) + return; + + 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 int anOldNbEdges = theStorage.NbEdges(); + const int anOldNbWires = theStorage.NbWires(); + const int anOldNbFaces = theStorage.NbFaces(); + const int anOldNbShells = theStorage.NbShells(); + const int anOldNbSolids = theStorage.NbSolids(); + const int anOldNbVertices = theStorage.NbVertices(); + const int anOldNbCompounds = theStorage.NbCompounds(); + + // Phase 1 (sequential): Traverse the full hierarchy. + // Existing shapes are deduplicated via findExistingNode; only new shapes are added. + NCollection_Vector 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. + const int aNbCompounds = theStorage.myCompounds.Nb(); + for (int aCompIdx = anOldNbCompounds; aCompIdx < aNbCompounds; ++aCompIdx) + { + BRepGraphInc::CompoundDef& aComp = theStorage.myCompounds.Change(aCompIdx); + const TopoDS_Shape* aCompOrig = theStorage.myOriginalShapes.Seek(aComp.Id); + if (aCompOrig == nullptr) + continue; + + int aRefOrd = 0; + for (TopoDS_Iterator aChildIt(*aCompOrig, false, false); aChildIt.More(); aChildIt.Next()) + { + const BRepGraph_NodeId aParentId = aComp.Id; + BRepGraphInc::ChildRef* aRef = nullptr; + int aCurrentOrd = 0; + const int aNbChildRefs = theStorage.NbChildRefs(); + for (BRepGraph_ChildRefId aChildRefId(0); 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, + anOldNbEdges, + anOldNbVertices, + aTmpAlloc); + + // Incrementally update reverse indices for newly appended entities only. + theStorage.BuildDeltaReverseIndex(anOldNbEdges, + anOldNbWires, + anOldNbFaces, + anOldNbShells, + anOldNbSolids); + + theStorage.myIsDone = true; +} diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx index 71b240c6b7..4679f694fe 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx @@ -51,10 +51,12 @@ public: { bool ExtractRegularities; //!< Phase 3b: edge regularities bool ExtractVertexPointReps; //!< Phase 3c: vertex point representations + bool CreateAutoProduct; //!< Auto-create a root Product wrapping the top-level topology Options() : ExtractRegularities(true), - ExtractVertexPointReps(true) + ExtractVertexPointReps(true), + CreateAutoProduct(true) { } }; @@ -94,6 +96,24 @@ public: const occ::handle& theTmpAlloc = occ::handle()); + //! 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(), + BRepGraph_ParamLayer* theParamLayer = nullptr, + BRepGraph_RegularityLayer* theRegularityLayer = nullptr, + const occ::handle& theTmpAlloc = + occ::handle()); + private: BRepGraphInc_Populate() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx index 9ef93e8d07..c78d46dc9d 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx @@ -257,7 +257,7 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the TopoDS_Shape anEdge = Node(theStorage, aCoEdge.EdgeDefId, theCache); if (!anEdge.IsNull()) { - anEdge.Orientation(aCoEdge.Sense); + anEdge.Orientation(aCoEdge.Orientation); if (!aCoEdgeRef.LocalLocation.IsIdentity()) anEdge.Location(aCoEdgeRef.LocalLocation); aBB.Add(aNewWire, anEdge); @@ -294,7 +294,7 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } } // Reconstruct free children (wires, edges) attached directly to the shell. - for (const BRepGraph_ChildRefId& aChildRefId : aShell.FreeChildRefIds) + for (const BRepGraph_ChildRefId& aChildRefId : aShell.AuxChildRefIds) { const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aChildRefId); TopoDS_Shape aChild = @@ -333,7 +333,7 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } } // Free children of the solid (edges, vertices). - for (const BRepGraph_ChildRefId& aChildRefId : aSolid.FreeChildRefIds) + for (const BRepGraph_ChildRefId& aChildRefId : aSolid.AuxChildRefIds) { const BRepGraphInc::ChildRef& aCR = theStorage.ChildRef(aChildRefId); TopoDS_Shape aChild = Node(theStorage, aCR.ChildDefId, theCache); @@ -598,7 +598,7 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid(theStorage.NbEdges())) continue; TopoDS_Edge anEdge = aGetOrBuildEdge(aCoEdge.EdgeDefId.Index); - anEdge.Orientation(aCoEdge.Sense); + anEdge.Orientation(aCoEdge.Orientation); if (!aCoEdgeRef.LocalLocation.IsIdentity()) anEdge.Location(aCoEdgeRef.LocalLocation); aBB.Add(aNewWire, anEdge); diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx index e1c8169bf0..be18eced7b 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx @@ -71,7 +71,7 @@ struct WireRef : public BaseRef }; //! CoEdge reference storage entry. -//! No Orientation field: CoEdgeDef::Sense already owns the edge-on-face sense, +//! 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 diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx index c91c53747b..1beb1cfc7c 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx @@ -218,7 +218,7 @@ void BRepGraphInc_ReverseIndex::Build( myNbIndexedCoEdges = theCoEdges.Length(); // Edge -> Faces: derive from CoEdge.FaceDefId (replaces legacy PCurve-based derivation). - // Seam edges have two CoEdges with same FaceDefId but opposite Sense. + // 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) { @@ -527,6 +527,28 @@ void BRepGraphInc_ReverseIndex::BuildDelta( } myNbIndexedCoEdges = theCoEdges.Length(); + // CoEdge -> Wires: iterate only new wire entities and their CoEdgeRefIds. + ensureSize(myCoEdgeToWires, theCoEdges.Length(), myAllocator); + for (BRepGraph_WireId aWireId(theOldNbWires); aWireId.IsValid(aNbWires); ++aWireId) + { + const BRepGraphInc::WireDef& aWire = theWires.Value(aWireId.Index); + if (aWire.IsRemoved) + continue; + for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) + { + const int aRefIdx = aCoEdgeRefId.Index; + if (aRefIdx < 0 || aRefIdx >= theCoEdgeRefs.Length()) + continue; + const BRepGraphInc::CoEdgeRef& aRef = theCoEdgeRefs.Value(aRefIdx); + if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid() || aRef.CoEdgeDefId.Index < 0 + || aRef.CoEdgeDefId.Index >= theCoEdges.Length()) + continue; + if (theCoEdges.Value(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) { diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx index 49c65f1189..341e721b37 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx @@ -724,6 +724,11 @@ void BRepGraphInc_Storage::BuildDeltaReverseIndex(const int theOldNbEdges, const int theOldNbShells, const int theOldNbSolids) { + // 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 Build()), the allocator has not been set yet. + myReverseIdx.SetAllocator(myAllocator); myReverseIdx.BuildDelta(myEdges.Entities, myCoEdges.Entities, myWires.Entities, diff --git a/src/ModelingData/TKBRep/BRepGraphInc/README.md b/src/ModelingData/TKBRep/BRepGraphInc/README.md index a25f92a14a..3f75dddf2b 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/README.md +++ b/src/ModelingData/TKBRep/BRepGraphInc/README.md @@ -95,14 +95,14 @@ graph TD Compound["CompoundDef
ChildRefIds[]"] CompSolid["CompSolidDef
SolidRefIds[]"] - Solid["SolidDef
ShellRefIds[], FreeChildRefIds[]"] - Shell["ShellDef
IsClosed, FaceRefIds[], FreeChildRefIds[]"] + Solid["SolidDef
ShellRefIds[], AuxChildRefIds[]"] + Shell["ShellDef
IsClosed, FaceRefIds[], AuxChildRefIds[]"] Face["FaceDef
SurfaceRepId, TriangulationRepIds,
ActiveTriangulationIndex, WireRefIds[],
VertexRefIds[], Tolerance, NaturalRestriction
"] Wire["WireDef
CoEdgeRefIds[], IsClosed"] - CoEdge["CoEdgeDef
EdgeDefId, FaceDefId, Sense,
Curve2DRepId, Polygon2DRepId,
ParamFirst/Last, UV1/UV2,
SeamPairId, SeamContinuity
"] + CoEdge["CoEdgeDef
EdgeDefId, FaceDefId, Orientation,
Curve2DRepId, Polygon2DRepId,
ParamFirst/Last, UV1/UV2,
SeamPairId, SeamContinuity
"] Edge["EdgeDef
Curve3DRepId, Polygon3DRepId,
StartVertexRefId, EndVertexRefId,
InternalVertexRefIds[],
ParamFirst/Last, Tolerance,
SameParameter, SameRange,
IsDegenerate, IsClosed,
Regularities[]
"] @@ -155,8 +155,8 @@ Concrete ref entry types extend BaseRef with context data: Entities store typed RefId vectors instead of inline ref arrays: -- **SolidDef**: `ShellRefIds[]`, `FreeChildRefIds[]` -- **ShellDef**: `FaceRefIds[]`, `FreeChildRefIds[]` +- **SolidDef**: `ShellRefIds[]`, `AuxChildRefIds[]` +- **ShellDef**: `FaceRefIds[]`, `AuxChildRefIds[]` - **FaceDef**: `WireRefIds[]`, `VertexRefIds[]` - **WireDef**: `CoEdgeRefIds[]` - **EdgeDef**: `StartVertexRefId`, `EndVertexRefId`, `InternalVertexRefIds[]` @@ -194,6 +194,10 @@ flowchart LR Backend entry point: `BRepGraphInc_Populate::Perform()`. +`Options.CreateAutoProduct` (default true) controls whether a root Product is auto-created wrapping the top-level topology node. Set to false when a higher-level builder (e.g. XCAF) manages Products itself. + +`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). + For normal graph construction, use `BRepGraph::Build()` 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 @@ -270,7 +274,7 @@ anEdge.Location(Identity); // Reset after attachment ### Special Cases -- **Seam edges**: Two CoEdges with opposite Sense, linked by `SeamPairId`. Both PCurves attached via `UpdateEdge(E, PC1, PC2, S, L, tol)`. +- **Seam edges**: Two CoEdges with opposite Orientation, linked by `SeamPairId`. Both PCurves attached via `UpdateEdge(E, PC1, PC2, S, L, tol)`. - **Degenerate edges**: `MakeEdge()` + `Degenerated(true)`, no 3D curve. - **IsClosed/NaturalRestriction**: Set AFTER sub-shapes are added (Add can reset flags). @@ -302,7 +306,7 @@ anEdge.Location(Identity); // Reset after attachment 3. **Reverse-index**: required reverse rows must exist for forward refs used by query paths 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 -6. **Assembly**: every Build produces at least one root Product; occurrence cross-references valid; self-referencing rejected; ParentOccurrenceDefId forms a tree +6. **Assembly**: Build produces a root Product when `CreateAutoProduct` is true (default); occurrence cross-references valid; self-referencing rejected; ParentOccurrenceDefId forms a tree ## Memory and Performance diff --git a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx index 6413c40155..7c62643a7d 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx @@ -492,7 +492,7 @@ TEST(BRepGraphIncTest, Cylinder_HasSeamEdges) for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdgeIdxs) { const BRepGraphInc::CoEdgeDef& aCE = aStorage.CoEdge(aCoEdgeId); - if (aCE.Sense == TopAbs_FORWARD && aCE.SeamPairId.IsValid()) + if (aCE.Orientation == TopAbs_FORWARD && aCE.SeamPairId.IsValid()) { ++aSeamPairCount; } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx index b425ee8d76..22902a891b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx @@ -801,6 +801,19 @@ TEST(BRepGraph_BuildTest, AppendFlattenedShape_OnEmptyGraph_BuildsFlattenedGraph const BRepGraph_FaceId aFaceId(anIdx); EXPECT_EQ(aRoots.Value(anIdx), aFaceId); } + + EXPECT_TRUE(aGraph.Builder().ValidateMutationBoundary()); +} + +TEST(BRepGraph_BuildTest, Build_MutationBoundary_IsValid) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + + BRepGraph aGraph; + aGraph.Build(aBoxMaker.Shape()); + + ASSERT_TRUE(aGraph.IsDone()); + EXPECT_TRUE(aGraph.Builder().ValidateMutationBoundary()); } TEST(BRepGraph_BuildTest, AppendFlattenedShape_SameFaceTwice_DedupsDefinition) @@ -838,6 +851,22 @@ TEST(BRepGraph_BuildTest, AppendFlattenedShape_AfterBuild_DoesNotCreateNewSolidD EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsBefore); EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore + 6); EXPECT_EQ(aGraph.RootNodeIds().Length(), 7); + EXPECT_TRUE(aGraph.Builder().ValidateMutationBoundary()); +} + +TEST(BRepGraph_BuildTest, AppendFullShape_MutationBoundary_IsValid) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepPrimAPI_MakeSphere aSphereMaker(5.0); + + BRepGraph aGraph; + aGraph.Build(aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + aGraph.Builder().AppendFullShape(aSphereMaker.Shape()); + + ASSERT_TRUE(aGraph.IsDone()); + EXPECT_TRUE(aGraph.Builder().ValidateMutationBoundary()); } TEST(BRepGraph_BuildTest, AppendFlattenedShape_AppendedFaceHasNoParentShell) diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx index 7f9b07d894..507d6d9f9d 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx @@ -501,6 +501,96 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ShellFaces_CountAndOrder) EXPECT_EQ(anActualFaceIds.Value(i), anExpectedFaceIds.Value(i)); } +TEST(BRepGraph_ChildExplorerTest, DirectChildren_ShellFaces_ExposeParentAndRef) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ShellId aShellId(0); + int anOrdinal = 0; + for (BRepGraph_ChildExplorer anIt = + makeDirectChildExplorer(aGraph, aShellId, BRepGraph_NodeId::Kind::Face); + anIt.More(); + anIt.Next(), ++anOrdinal) + { + const BRepGraph_FaceRefId aFaceRefId = aGraph.Refs().Faces().IdsOf(aShellId).Value(anOrdinal); + EXPECT_EQ(anIt.CurrentParent(), BRepGraph_NodeId(aShellId)); + EXPECT_EQ(anIt.CurrentLinkKind(), BRepGraph_ChildExplorer::LinkKind::Reference); + EXPECT_EQ(anIt.CurrentRef(), BRepGraph_RefId(aFaceRefId)); + } +} + +TEST(BRepGraph_ChildExplorerTest, DirectChildren_ProductShapeRoot_HasNoRef) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ProductId aProductId = aGraph.Builder().AddProduct(BRepGraph_SolidId(0)); + ASSERT_TRUE(aProductId.IsValid()); + + BRepGraph_ChildExplorer anIt(aGraph, + aProductId, + BRepGraph_ChildExplorer::TraversalMode::DirectChildren); + ASSERT_TRUE(anIt.More()); + EXPECT_EQ(anIt.Current().DefId, BRepGraph_NodeId(BRepGraph_SolidId(0))); + EXPECT_EQ(anIt.CurrentParent(), BRepGraph_NodeId(aProductId)); + EXPECT_EQ(anIt.CurrentLinkKind(), BRepGraph_ChildExplorer::LinkKind::Structural); + EXPECT_FALSE(anIt.CurrentRef().IsValid()); + + anIt.Next(); + EXPECT_FALSE(anIt.More()); +} + +TEST(BRepGraph_ChildExplorerTest, RootEqualsTarget_LinkKindNone) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_FaceId(0), BRepGraph_NodeId::Kind::Face); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ChildExplorer::LinkKind::None); + EXPECT_FALSE(anExp.CurrentParent().IsValid()); + EXPECT_FALSE(anExp.CurrentRef().IsValid()); +} + +TEST(BRepGraph_ChildExplorerTest, DirectChildren_ProductOccurrences_ExposeOccurrenceRefs) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ProductId aPart = aGraph.Builder().AddProduct(BRepGraph_SolidId(0)); + const BRepGraph_ProductId anAssembly = aGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(anAssembly.IsValid()); + + const BRepGraph_OccurrenceId anOcc0 = + aGraph.Builder().AddOccurrence(anAssembly, aPart, TopLoc_Location()); + const BRepGraph_OccurrenceId anOcc1 = + aGraph.Builder().AddOccurrence(anAssembly, aPart, TopLoc_Location()); + ASSERT_TRUE(anOcc0.IsValid()); + ASSERT_TRUE(anOcc1.IsValid()); + + const NCollection_Vector& anOccurrenceRefs = + aGraph.Refs().Occurrences().IdsOf(anAssembly); + ASSERT_EQ(anOccurrenceRefs.Length(), 2); + + int anOrdinal = 0; + for (BRepGraph_ChildExplorer anIt(aGraph, + anAssembly, + BRepGraph_ChildExplorer::TraversalMode::DirectChildren); + anIt.More(); + anIt.Next(), ++anOrdinal) + { + EXPECT_EQ(anIt.CurrentParent(), BRepGraph_NodeId(anAssembly)); + EXPECT_EQ(anIt.CurrentRef(), BRepGraph_RefId(anOccurrenceRefs.Value(anOrdinal))); + } + EXPECT_EQ(anOrdinal, 2); +} + TEST(BRepGraph_ChildExplorerTest, DirectChildren_RemovedFaceRef_IsSkipped) { BRepGraph aGraph; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx index a28ad6db73..86d4f88dbd 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx @@ -15,11 +15,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -36,6 +40,25 @@ #include +namespace +{ + +class CopyTestCacheValue : public BRepGraph_CacheValue +{ +public: + DEFINE_STANDARD_RTTI_INLINE(CopyTestCacheValue, BRepGraph_CacheValue) + CopyTestCacheValue() = default; +}; + +const occ::handle& copyTestCacheKind() +{ + static const occ::handle THE_KIND = + new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10026"), "CopyTestAttr"); + return THE_KIND; +} + +} // namespace + TEST(BRepGraph_CopyTest, CopyBox_FaceCount) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -131,6 +154,107 @@ TEST(BRepGraph_CopyTest, CopyBox_SharedGeometry) BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId(0)).get()); } +TEST(BRepGraph_CopyTest, CopyBox_PreservesFreshNodeCache) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + + BRepGraph aGraph; + aGraph.Build(aBox); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_FaceId aFaceId(0); + const occ::handle anAttr = new CopyTestCacheValue(); + aGraph.Cache().Set(aFaceId, copyTestCacheKind(), anAttr); + ASSERT_TRUE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); + + BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); + ASSERT_TRUE(aCopyGraph.IsDone()); + + EXPECT_TRUE(aCopyGraph.Cache().Has(aFaceId, copyTestCacheKind())); + EXPECT_FALSE(aCopyGraph.Cache().Get(aFaceId, copyTestCacheKind()).IsNull()); + EXPECT_TRUE(aCopyGraph.Cache().CacheKindIter(aFaceId).More()); +} + +TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleNodeCache) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + + BRepGraph aGraph; + aGraph.Build(aBox); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_FaceId aFaceId(0); + aGraph.Cache().Set(aFaceId, copyTestCacheKind(), new CopyTestCacheValue()); + ASSERT_TRUE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); + + { + BRepGraph_MutGuard aFace = aGraph.Builder().MutFace(aFaceId); + aFace->Tolerance += 0.1; + } + + 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()); +} + +TEST(BRepGraph_CopyTest, CopyBox_PreservesFreshFaceRefCache) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + + BRepGraph aGraph; + aGraph.Build(aBox); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); + + const BRepGraph_FaceRefId aFaceRef(0); + aGraph.Cache().Set(aFaceRef, copyTestCacheKind(), new CopyTestCacheValue()); + ASSERT_TRUE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + + BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); + ASSERT_TRUE(aCopyGraph.IsDone()); + + EXPECT_TRUE(aCopyGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + EXPECT_FALSE(aCopyGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); + EXPECT_TRUE(aCopyGraph.Cache().CacheKindIter(aFaceRef).More()); +} + +TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleFaceRefCache) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + + BRepGraph aGraph; + aGraph.Build(aBox); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); + + const BRepGraph_FaceRefId aFaceRef(0); + aGraph.Cache().Set(aFaceRef, copyTestCacheKind(), new CopyTestCacheValue()); + ASSERT_TRUE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + + { + BRepGraph_MutGuard aRef = aGraph.Builder().MutFaceRef(aFaceRef); + aRef->Orientation = TopAbs::Reverse(aRef->Orientation); + } + + ASSERT_FALSE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + ASSERT_TRUE(aGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); + ASSERT_FALSE(aGraph.Cache().CacheKindIter(aFaceRef).More()); + + BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); + ASSERT_TRUE(aCopyGraph.IsDone()); + + EXPECT_FALSE(aCopyGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + EXPECT_TRUE(aCopyGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); + EXPECT_FALSE(aCopyGraph.Cache().CacheKindIter(aFaceRef).More()); +} + TEST(BRepGraph_CopyTest, CopyCylinder_FaceCount) { BRepPrimAPI_MakeCylinder aCylMaker(5.0, 20.0); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx index 8476f707d4..78f5ccc10e 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx @@ -278,7 +278,7 @@ int addDuplicatePCurvesToAllEdges(BRepGraph& theGraph) aDupPCurve, aCE.ParamFirst, aCE.ParamLast, - aCE.Sense); + aCE.Orientation); ++aDupCount; } return aDupCount; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx index c49800ce7b..ead225a245 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx @@ -226,6 +226,55 @@ TEST_F(BRepGraph_DefsIteratorTest, OccurrenceOfProduct_EnumeratesDirectOccurrenc EXPECT_EQ(countIterator(BRepGraph_DefsOccurrenceOfProduct(myGraph, anAssembly)), 2); } +TEST_F(BRepGraph_DefsIteratorTest, AuxChildrenOfShellAndSolid_EnumerateInjectedChildren) +{ + NCollection_Vector aShellChildren; + aShellChildren.Append(BRepGraph_WireId(0)); + aShellChildren.Append(BRepGraph_EdgeId(0)); + const BRepGraph_CompoundId aShellSeed = myGraph.Builder().AddCompound(aShellChildren); + ASSERT_TRUE(aShellSeed.IsValid()); + + { + BRepGraph_MutGuard aShell = + myGraph.Builder().MutShell(BRepGraph_ShellId(0)); + for (const BRepGraph_ChildRefId& aRefId : + myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) + { + aShell->AuxChildRefIds.Append(aRefId); + } + } + + BRepGraph_DefsChildOfShell aShellIt(myGraph, BRepGraph_ShellId(0)); + 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_Vector aSolidChildren; + aSolidChildren.Append(BRepGraph_EdgeId(1)); + aSolidChildren.Append(BRepGraph_VertexId(0)); + const BRepGraph_CompoundId aSolidSeed = myGraph.Builder().AddCompound(aSolidChildren); + ASSERT_TRUE(aSolidSeed.IsValid()); + + { + BRepGraph_MutGuard aSolid = + myGraph.Builder().MutSolid(BRepGraph_SolidId(0)); + for (const BRepGraph_ChildRefId& aRefId : + myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) + { + aSolid->AuxChildRefIds.Append(aRefId); + } + } + + BRepGraph_DefsChildOfSolid aSolidIt(myGraph, BRepGraph_SolidId(0)); + 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_Vector& aWireRefs = diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx index c33d058327..c23628854f 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -30,9 +31,11 @@ public: BRepGraph_ModTrackingLayer( const TCollection_AsciiString& theName, const int theSubscribedKinds, - const Standard_GUID& theId = Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10004")) + const Standard_GUID& theId = Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10004"), + const int theSubscribedRefKinds = 0) : myName(theName), mySubscribedKinds(theSubscribedKinds), + mySubscribedRefKinds(theSubscribedRefKinds), myId(theId) { } @@ -43,6 +46,26 @@ public: int SubscribedKinds() const override { return mySubscribedKinds; } + int SubscribedRefKinds() const override { return mySubscribedRefKinds; } + + void OnRefRemoved(const BRepGraph_RefId theRef) noexcept override + { + myRefRemovedEvents.Append(theRef); + ++myRefRemoveCallCount; + } + + void OnRefModified(const BRepGraph_RefId theRef) noexcept override + { + myRefImmediateEvents.Append(theRef); + } + + void OnRefsModified(const NCollection_Vector& theRefs, + const int /*theModifiedRefKindsMask*/) noexcept override + { + myRefBatchEvents = theRefs; + ++myRefBatchCallCount; + } + void OnNodeModified(const BRepGraph_NodeId theNode) noexcept override { myImmediateEvents.Append(theNode); @@ -85,6 +108,11 @@ public: myLastRemovedNode = BRepGraph_NodeId(); myLastReplacement = BRepGraph_NodeId(); myLastRemapMap.Clear(); + myRefRemovedEvents.Clear(); + myRefImmediateEvents.Clear(); + myRefBatchEvents.Clear(); + myRefBatchCallCount = 0; + myRefRemoveCallCount = 0; } bool HasImmediateEventFor(BRepGraph_NodeId theNode) const @@ -121,11 +149,18 @@ public: BRepGraph_NodeId myLastReplacement; NCollection_DataMap myLastRemapMap; + NCollection_Vector myRefRemovedEvents; + NCollection_Vector myRefImmediateEvents; + NCollection_Vector myRefBatchEvents; + int myRefBatchCallCount = 0; + int myRefRemoveCallCount = 0; + DEFINE_STANDARD_RTTIEXT(BRepGraph_ModTrackingLayer, BRepGraph_Layer) private: TCollection_AsciiString myName; int mySubscribedKinds; + int mySubscribedRefKinds; Standard_GUID myId; }; @@ -494,6 +529,76 @@ TEST_F(BRepGraph_EventBusTest, KindBit_Helpers) EXPECT_EQ(aBitCount, 8); } +TEST_F(BRepGraph_EventBusTest, RefKindBit_Helpers) +{ + using Kind = BRepGraph_RefId::Kind; + 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. + 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); + int aBitCount = 0; + for (int v = aAll; v != 0; v >>= 1) + aBitCount += (v & 1); + EXPECT_EQ(aBitCount, 8); +} + +TEST_F(BRepGraph_EventBusTest, DefaultSubscribedRefKinds_Zero) +{ + occ::handle aDefaultLayer = new BRepGraph_DefaultLayer; + EXPECT_EQ(aDefaultLayer->SubscribedRefKinds(), 0); +} + +TEST_F(BRepGraph_EventBusTest, OnRefRemoved_DispatchedToAllLayers) +{ + occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + myGraph.LayerRegistry().RegisterLayer(aLayer); + + // A box has FaceRef entries in its shell. Remove one. + const BRepGraph_FaceRefId aFaceRef(0); + ASSERT_TRUE(myGraph.Builder().RemoveRef(aFaceRef)); + + // OnRefRemoved must be dispatched regardless of SubscribedRefKinds. + EXPECT_EQ(aLayer->myRefRemoveCallCount, 1); + EXPECT_EQ(aLayer->myRefRemovedEvents.Length(), 1); + EXPECT_EQ(aLayer->myRefRemovedEvents.Value(0), BRepGraph_RefId(aFaceRef)); +} + +TEST_F(BRepGraph_EventBusTest, OnRefRemoved_MultipleRefs) +{ + occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + myGraph.LayerRegistry().RegisterLayer(aLayer); + + ASSERT_TRUE(myGraph.Builder().RemoveRef(BRepGraph_FaceRefId(0))); + ASSERT_TRUE(myGraph.Builder().RemoveRef(BRepGraph_FaceRefId(1))); + + EXPECT_EQ(aLayer->myRefRemoveCallCount, 2); + EXPECT_EQ(aLayer->myRefRemovedEvents.Length(), 2); +} + +TEST_F(BRepGraph_EventBusTest, OnRefRemoved_AlreadyRemoved_NotDispatched) +{ + occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + myGraph.LayerRegistry().RegisterLayer(aLayer); + + ASSERT_TRUE(myGraph.Builder().RemoveRef(BRepGraph_FaceRefId(0))); + EXPECT_EQ(aLayer->myRefRemoveCallCount, 1); + + // Second removal returns false and must NOT dispatch again. + EXPECT_FALSE(myGraph.Builder().RemoveRef(BRepGraph_FaceRefId(0))); + EXPECT_EQ(aLayer->myRefRemoveCallCount, 1); +} + TEST_F(BRepGraph_EventBusTest, OverlappingSubscription_EdgeAndFace) { // Layer subscribes to both Edge and Face - should receive events for both kinds. @@ -513,3 +618,61 @@ TEST_F(BRepGraph_EventBusTest, OverlappingSubscription_EdgeAndFace) EXPECT_EQ(aLayer->CountImmediateEventsOfKind(BRepGraph_NodeId::Kind::Face), 0); EXPECT_EQ(aLayer->CountImmediateEventsOfKind(BRepGraph_NodeId::Kind::Wire), 0); } + +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")); + myGraph.LayerRegistry().RegisterLayer(aLayer1); + myGraph.LayerRegistry().RegisterLayer(aLayer2); + + int aCount = 0; + for (BRepGraph_LayerIterator anIt(myGraph.LayerRegistry()); anIt.More(); anIt.Next()) + { + EXPECT_FALSE(anIt.Value().IsNull()); + // Slot() must match iteration index and Layer(slot) must return the same value. + EXPECT_EQ(myGraph.LayerRegistry().Layer(anIt.Slot()), anIt.Value()); + ++aCount; + } + EXPECT_EQ(aCount, 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")); + myGraph.LayerRegistry().RegisterLayer(aLayer1); + myGraph.LayerRegistry().RegisterLayer(aLayer2); + + int aCount = 0; + bool hasL1 = false; + bool hasL2 = false; + for (const occ::handle& aLayer : + BRepGraph_LayerIterator(myGraph.LayerRegistry())) + { + if (aLayer == aLayer1) + hasL1 = true; + if (aLayer == aLayer2) + hasL2 = true; + ++aCount; + } + EXPECT_EQ(aCount, 2); + EXPECT_TRUE(hasL1); + EXPECT_TRUE(hasL2); +} + +TEST_F(BRepGraph_EventBusTest, LayerIterator_RangeFor_Empty) +{ + int aCount = 0; + for (const occ::handle& aLayer : + BRepGraph_LayerIterator(myGraph.LayerRegistry())) + { + (void)aLayer; + ++aCount; + } + EXPECT_EQ(aCount, 0); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx index c147b80aed..bb0eeb9ece 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx @@ -648,7 +648,8 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_HasTwoCoEdges) { // Verify the paired coedge has opposite orientation. const BRepGraphInc::CoEdgeDef& aPair = aGraph.Topo().CoEdges().Definition(aCE.SeamPairId); - EXPECT_NE(aCE.Sense, aPair.Sense) << "Seam coedges should have opposite orientations"; + 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"; aFoundSeam = true; break; @@ -709,12 +710,12 @@ TEST(BRepGraph_GeometryTest, Box_FindPCurve_MatchesToolOverload) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); const BRepGraphInc::CoEdgeDef* aFromDefs = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aCE.FaceDefId, aCE.Sense); + BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aCE.FaceDefId, aCE.Orientation); const BRepGraphInc::CoEdgeDef* aFromTool = BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, BRepGraph_FaceId(aCE.FaceDefId.Index), - aCE.Sense); + aCE.Orientation); EXPECT_EQ(aFromDefs, aFromTool); EXPECT_NE(aFromDefs, nullptr); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx index d55fe5ca5a..3d912fdef5 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -74,6 +75,24 @@ TEST(BRepGraph_ParentExplorerTest, FaceParents_DirectParents_StopsAtImmediateShe EXPECT_FALSE(anExp.More()); } +TEST(BRepGraph_ParentExplorerTest, FaceParents_DirectParents_ExposeChildAndRef) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + BRepGraph_ParentExplorer anExp(aGraph, + BRepGraph_FaceId(0), + BRepGraph_ParentExplorer::TraversalMode::DirectParents); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(BRepGraph_ShellId(0))); + EXPECT_EQ(anExp.CurrentChild(), BRepGraph_NodeId(BRepGraph_FaceId(0))); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Reference); + + const BRepGraph_FaceRefId aFaceRefId = aGraph.Refs().Faces().IdsOf(BRepGraph_ShellId(0)).Value(0); + EXPECT_EQ(anExp.CurrentRef(), BRepGraph_RefId(aFaceRefId)); +} + TEST(BRepGraph_ParentExplorerTest, AvoidKind_Solid_PrunesProducts) { BRepGraph aGraph; @@ -202,6 +221,85 @@ TEST(BRepGraph_ParentExplorerTest, SharedProduct_ProductParentsKeepDistinctConte EXPECT_FALSE(aLoc1.IsEqual(aLoc2)); } +TEST(BRepGraph_ParentExplorerTest, ShapeRootProductParent_HasChildButNoRef) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + // Build() 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); + + BRepGraph_ParentExplorer anExp(aGraph, + BRepGraph_SolidId(0), + BRepGraph_NodeId::Kind::Product, + BRepGraph_ParentExplorer::TraversalMode::DirectParents); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(aPart)); + EXPECT_EQ(anExp.CurrentChild(), BRepGraph_NodeId(BRepGraph_SolidId(0))); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Structural); + EXPECT_FALSE(anExp.CurrentRef().IsValid()); + + anExp.Next(); + EXPECT_FALSE(anExp.More()); +} + +TEST(BRepGraph_ParentExplorerTest, OccurrenceParent_ExposeOccurrenceRef) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ProductId aPart = aGraph.Builder().AddProduct(BRepGraph_SolidId(0)); + const BRepGraph_ProductId anAssembly = aGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(anAssembly.IsValid()); + + const BRepGraph_OccurrenceId anOccurrence = + aGraph.Builder().AddOccurrence(anAssembly, aPart, TopLoc_Location()); + ASSERT_TRUE(anOccurrence.IsValid()); + + BRepGraph_ParentExplorer anExp(aGraph, + anOccurrence, + BRepGraph_NodeId::Kind::Product, + BRepGraph_ParentExplorer::TraversalMode::DirectParents); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(anAssembly)); + EXPECT_EQ(anExp.CurrentChild(), BRepGraph_NodeId(anOccurrence)); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Reference); + + const BRepGraph_OccurrenceRefId anOccurrenceRefId = + aGraph.Refs().Occurrences().IdsOf(anAssembly).Value(0); + EXPECT_EQ(anExp.CurrentRef(), BRepGraph_RefId(anOccurrenceRefId)); +} + +TEST(BRepGraph_ParentExplorerTest, ProductParents_ImmediateOccurrence_IsStructural) +{ + BRepGraph aGraph; + aGraph.Build(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ProductId aPart = aGraph.Builder().AddProduct(BRepGraph_SolidId(0)); + const BRepGraph_ProductId anAssembly = aGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(anAssembly.IsValid()); + + const BRepGraph_OccurrenceId anOccurrence = + aGraph.Builder().AddOccurrence(anAssembly, aPart, TopLoc_Location()); + ASSERT_TRUE(anOccurrence.IsValid()); + + BRepGraph_ParentExplorer anExp(aGraph, + aPart, + BRepGraph_ParentExplorer::TraversalMode::DirectParents); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(anOccurrence)); + EXPECT_EQ(anExp.CurrentChild(), BRepGraph_NodeId(aPart)); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Structural); + EXPECT_FALSE(anExp.CurrentRef().IsValid()); +} + TEST(BRepGraph_ParentExplorerTest, CoEdgeParents_ImmediateWireIsVisible) { BRepGraph aGraph; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx index 2572ae7770..16e2898a6b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx @@ -201,6 +201,59 @@ TEST_F(BRepGraph_RefsIteratorTest, OccurrenceOfProduct_EnumeratesOccurrenceRefs) EXPECT_EQ(countIterator(BRepGraph_RefsOccurrenceOfProduct(myGraph, anAssembly)), 2); } +TEST_F(BRepGraph_RefsIteratorTest, AuxChildRefsOfShellAndSolid_EnumerateInjectedChildRefs) +{ + NCollection_Vector aShellChildren; + aShellChildren.Append(BRepGraph_WireId(0)); + aShellChildren.Append(BRepGraph_EdgeId(0)); + const BRepGraph_CompoundId aShellSeed = myGraph.Builder().AddCompound(aShellChildren); + ASSERT_TRUE(aShellSeed.IsValid()); + + { + BRepGraph_MutGuard aShell = + myGraph.Builder().MutShell(BRepGraph_ShellId(0)); + for (const BRepGraph_ChildRefId& aRefId : + myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) + { + aShell->AuxChildRefIds.Append(aRefId); + } + } + + BRepGraph_RefsChildOfShell aShellIt(myGraph, BRepGraph_ShellId(0)); + 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_Vector aSolidChildren; + aSolidChildren.Append(BRepGraph_EdgeId(1)); + aSolidChildren.Append(BRepGraph_VertexId(0)); + const BRepGraph_CompoundId aSolidSeed = myGraph.Builder().AddCompound(aSolidChildren); + ASSERT_TRUE(aSolidSeed.IsValid()); + + { + BRepGraph_MutGuard aSolid = + myGraph.Builder().MutSolid(BRepGraph_SolidId(0)); + for (const BRepGraph_ChildRefId& aRefId : + myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) + { + aSolid->AuxChildRefIds.Append(aRefId); + } + } + + BRepGraph_RefsChildOfSolid aSolidIt(myGraph, BRepGraph_SolidId(0)); + 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_Vector& aWireRefs = diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx new file mode 100644 index 0000000000..8fcbe2dbb6 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx @@ -0,0 +1,183 @@ +// 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 + +namespace +{ +static bool hasRelatedNode(const BRepGraph& theGraph, + const BRepGraph_NodeId theSource, + const BRepGraph_NodeId& theNode, + const BRepGraph_RelatedIterator::RelationKind theRelation) +{ + for (BRepGraph_RelatedIterator anIt(theGraph, theSource); anIt.More(); anIt.Next()) + { + if (anIt.Current() == theNode && anIt.CurrentRelation() == theRelation) + { + return true; + } + } + return false; +} + +static int countRelations(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode, + const BRepGraph_RelatedIterator::RelationKind theRelation) +{ + int aCount = 0; + for (BRepGraph_RelatedIterator anIt(theGraph, theNode); anIt.More(); anIt.Next()) + { + if (anIt.CurrentRelation() == theRelation) + { + ++aCount; + } + } + return aCount; +} +} // namespace + +class BRepGraph_RelatedIteratorTest : public testing::Test +{ +protected: + void SetUp() override + { + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + myGraph.Build(aBoxMaker.Shape()); + } + + BRepGraph myGraph; +}; + +TEST_F(BRepGraph_RelatedIteratorTest, FaceOfBox_ReturnsBoundaryEdgesAndOuterWire) +{ + const BRepGraph_FaceId aFaceId(0); + + const int aBoundaryEdgeCount = + countRelations(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::BoundaryEdge); + const int anAdjacentFaceCount = + countRelations(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace); + const int anOuterWireCount = countRelations(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::OuterWire); + + EXPECT_EQ(aBoundaryEdgeCount, 4); + EXPECT_EQ(anAdjacentFaceCount, 4); + EXPECT_EQ(anOuterWireCount, 1); + EXPECT_TRUE(hasRelatedNode(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_NodeId(BRepGraph_WireId(0)), + BRepGraph_RelatedIterator::RelationKind::OuterWire)); +} + +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); + + EXPECT_EQ(aVertexCount, 2); + EXPECT_EQ(aFaceCount, 2); +} + +TEST_F(BRepGraph_RelatedIteratorTest, ProductAndOccurrence_ReturnExpectedAssemblyLinks) +{ + const BRepGraph_ProductId aPartProduct = + myGraph.Builder().AddProduct(BRepGraph_NodeId(BRepGraph_SolidId(0))); + const BRepGraph_ProductId aRootAssembly = myGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPartProduct.IsValid()); + ASSERT_TRUE(aRootAssembly.IsValid()); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); + const BRepGraph_OccurrenceId anOccurrenceId = + myGraph.Builder().AddOccurrence(aRootAssembly, aPartProduct, TopLoc_Location(aTrsf)); + ASSERT_TRUE(anOccurrenceId.IsValid()); + + EXPECT_EQ(countRelations(myGraph, + BRepGraph_NodeId(aRootAssembly), + BRepGraph_RelatedIterator::RelationKind::ChildOccurrence), + 1); + EXPECT_TRUE(hasRelatedNode(myGraph, + BRepGraph_NodeId(aRootAssembly), + BRepGraph_NodeId(anOccurrenceId), + BRepGraph_RelatedIterator::RelationKind::ChildOccurrence)); + + EXPECT_EQ(countRelations(myGraph, + BRepGraph_NodeId(anOccurrenceId), + BRepGraph_RelatedIterator::RelationKind::ReferencedProduct), + 1); + EXPECT_TRUE(hasRelatedNode(myGraph, + BRepGraph_NodeId(anOccurrenceId), + BRepGraph_NodeId(aPartProduct), + BRepGraph_RelatedIterator::RelationKind::ReferencedProduct)); + + EXPECT_EQ(countRelations(myGraph, + BRepGraph_NodeId(anOccurrenceId), + BRepGraph_RelatedIterator::RelationKind::ParentProduct), + 1); + EXPECT_TRUE(hasRelatedNode(myGraph, + BRepGraph_NodeId(anOccurrenceId), + BRepGraph_NodeId(aRootAssembly), + BRepGraph_RelatedIterator::RelationKind::ParentProduct)); +} + +TEST_F(BRepGraph_RelatedIteratorTest, InvalidNode_ReturnsEmpty) +{ + BRepGraph_RelatedIterator anIt(myGraph, BRepGraph_NodeId()); + EXPECT_FALSE(anIt.More()); +} + +TEST_F(BRepGraph_RelatedIteratorTest, EdgeReferencedByFace_RemovedFaceIsSkipped) +{ + // A box edge is shared by exactly 2 faces. Removing one face should leave + // exactly 1 ReferencedByFace relation for the edge. This specifically + // tests that removed faces are not skipped incorrectly (no double-increment). + const BRepGraph_EdgeId anEdgeId(0); + + // Find the two faces that reference this edge. + const NCollection_Vector& aFaces = myGraph.Topo().Edges().Faces(anEdgeId); + ASSERT_EQ(aFaces.Length(), 2); + + // Remove the first face. + const BRepGraph_FaceId aRemovedFace = aFaces.Value(0); + const BRepGraph_FaceId aSurvivor = aFaces.Value(1); + myGraph.Builder().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); + EXPECT_EQ(aFaceCount, 1); + EXPECT_TRUE(hasRelatedNode(myGraph, + BRepGraph_NodeId(anEdgeId), + BRepGraph_NodeId(aSurvivor), + BRepGraph_RelatedIterator::RelationKind::ReferencedByFace)); +} \ No newline at end of file diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx index fbe39c3651..8de027d1d8 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx @@ -1470,7 +1470,7 @@ TEST_F(BRepGraphTest, ReplaceEdgeInWire_Reversed_OrientationFlipped) const BRepGraphInc::CoEdgeDef& anOrigCoEdge = myGraph.Topo().CoEdges().Definition(anOrigCR.CoEdgeDefId); const BRepGraph_EdgeId anOldEdgeId = anOrigCoEdge.EdgeDefId; - TopAbs_Orientation anOrigOrientation = anOrigCoEdge.Sense; + TopAbs_Orientation anOrigOrientation = anOrigCoEdge.Orientation; // Pick a different edge. const int aNewIdx = (anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb(); @@ -1489,7 +1489,7 @@ TEST_F(BRepGraphTest, ReplaceEdgeInWire_Reversed_OrientationFlipped) // Orientation should be flipped relative to original. TopAbs_Orientation anExpected = (anOrigOrientation == TopAbs_FORWARD) ? TopAbs_REVERSED : TopAbs_FORWARD; - EXPECT_EQ(aNewCoEdge.Sense, anExpected); + EXPECT_EQ(aNewCoEdge.Orientation, anExpected); } TEST_F(BRepGraphTest, ReplaceEdgeInWire_UpdatesEdgeToCoEdgeReverseIndex) diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx new file mode 100644 index 0000000000..57a90455f8 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx @@ -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. + +#include +#include + +#include +#include +#include + +#include + +namespace +{ +static bool hasRelatedNode(const NCollection_Sequence& theItems, + const BRepGraph_NodeId& theNode, + const char* theRelation) +{ + for (NCollection_Sequence::Iterator anIt(theItems); anIt.More(); + anIt.Next()) + { + if (anIt.Value().Node == theNode && anIt.Value().Relation == theRelation) + { + return true; + } + } + return false; +} +} // namespace + +class BRepGraph_ToolTest : public testing::Test +{ +protected: + void SetUp() override + { + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + myGraph.Build(aBoxMaker.Shape()); + } + + BRepGraph myGraph; +}; + +TEST_F(BRepGraph_ToolTest, Related_FaceOfBox_ReturnsBoundaryEdgesAndOuterWire) +{ + const BRepGraph_FaceId aFaceId(0); + + const NCollection_Sequence aItems = + BRepGraph_Tool::Related(myGraph, BRepGraph_NodeId(aFaceId)); + + EXPECT_TRUE(hasRelatedNode(aItems, BRepGraph_NodeId(BRepGraph_WireId(0)), "Outer wire")); + + int aBoundaryEdgeCount = 0; + int anAdjacentFaceCount = 0; + for (NCollection_Sequence::Iterator anIt(aItems); anIt.More(); + anIt.Next()) + { + if (anIt.Value().Relation == "Boundary edge") + { + ++aBoundaryEdgeCount; + } + if (anIt.Value().Relation == "Adjacent face") + { + ++anAdjacentFaceCount; + } + } + + EXPECT_EQ(aBoundaryEdgeCount, 4); + EXPECT_EQ(anAdjacentFaceCount, 4); +} + +TEST_F(BRepGraph_ToolTest, Related_EdgeOfBox_ReturnsIncidentVerticesAndFaces) +{ + const BRepGraph_EdgeId anEdgeId(0); + + const NCollection_Sequence aItems = + BRepGraph_Tool::Related(myGraph, BRepGraph_NodeId(anEdgeId)); + + int aVertexCount = 0; + int aFaceCount = 0; + for (NCollection_Sequence::Iterator anIt(aItems); anIt.More(); + anIt.Next()) + { + if (anIt.Value().Relation == "Incident vertex") + { + ++aVertexCount; + } + if (anIt.Value().Relation == "Referenced by face") + { + ++aFaceCount; + } + } + + EXPECT_EQ(aVertexCount, 2); + EXPECT_EQ(aFaceCount, 2); +} + +TEST_F(BRepGraph_ToolTest, Related_ProductAndOccurrence_ReturnExpectedAssemblyLinks) +{ + const BRepGraph_ProductId aPartProduct = + myGraph.Builder().AddProduct(BRepGraph_NodeId(BRepGraph_SolidId(0))); + const BRepGraph_ProductId aRootAssembly = myGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPartProduct.IsValid()); + ASSERT_TRUE(aRootAssembly.IsValid()); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); + const BRepGraph_OccurrenceId anOccurrenceId = + myGraph.Builder().AddOccurrence(aRootAssembly, aPartProduct, TopLoc_Location(aTrsf)); + ASSERT_TRUE(anOccurrenceId.IsValid()); + + const NCollection_Sequence aProductItems = + BRepGraph_Tool::Related(myGraph, BRepGraph_NodeId(aRootAssembly)); + EXPECT_TRUE(hasRelatedNode(aProductItems, BRepGraph_NodeId(anOccurrenceId), "Child occurrence")); + + const NCollection_Sequence anOccurrenceItems = + BRepGraph_Tool::Related(myGraph, BRepGraph_NodeId(anOccurrenceId)); + EXPECT_TRUE( + hasRelatedNode(anOccurrenceItems, BRepGraph_NodeId(aPartProduct), "Referenced product")); + EXPECT_TRUE(hasRelatedNode(anOccurrenceItems, BRepGraph_NodeId(aRootAssembly), "Parent product")); +} + +TEST_F(BRepGraph_ToolTest, Related_InvalidNode_ReturnsEmpty) +{ + const NCollection_Sequence aItems = + BRepGraph_Tool::Related(myGraph, BRepGraph_NodeId()); + EXPECT_TRUE(aItems.IsEmpty()); +} \ No newline at end of file diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx index fb9ede4f11..ad5548e48c 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -462,15 +464,143 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKinds_ReportsStoredKind) occ::handle anAttr = new TestCacheValue(); myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - const NCollection_Vector> aKinds = - myGraph.Cache().CacheKinds(aFaceId); + BRepGraph_CacheKindIterator anIt = myGraph.Cache().CacheKindIter(aFaceId); - ASSERT_EQ(aKinds.Length(), 1); - ASSERT_FALSE(aKinds.Value(0).IsNull()); - EXPECT_EQ(aKinds.Value(0)->ID(), testUserAttrKind()->ID()); + 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)) + { + if (!aKind.IsNull() && aKind->ID() == testUserAttrKind()->ID()) + hasUserKind = true; + ++aCount; + } + EXPECT_EQ(aCount, 1); + EXPECT_TRUE(hasUserKind); +} + +TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RangeFor_Empty) +{ + // No cache entries set - range-for should produce zero iterations. + int aCount = 0; + for (const occ::handle& aKind : + myGraph.Cache().CacheKindIter(BRepGraph_FaceId(0))) + { + (void)aKind; + ++aCount; + } + EXPECT_EQ(aCount, 0); +} + +TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_KindSlot) +{ + BRepGraph_FaceId aFaceId(0); + myGraph.Cache().Set(aFaceId, testUserAttrKind(), new TestCacheValue()); + + 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); +} + +TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RefId_RangeFor) +{ + // Set a cache value on a FaceRef. + const BRepGraph_FaceRefId aRef(0); + myGraph.Cache().Set(aRef, testUserAttrKind(), new TestCacheValue()); + + 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); +} + +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(0)); + EXPECT_FALSE(anIt.More()); + EXPECT_EQ(anIt.NbKinds(), 0); +} + +TEST_F(BRepGraph_ViewsTest, EdgeOps_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; + + // Use reverse index to get a face for this edge. + const NCollection_Vector& aFaces = myGraph.Topo().Edges().Faces(anEdge); + ASSERT_GT(aFaces.Length(), 0); + const BRepGraph_FaceId aFace = aFaces.Value(0); + + const BRepGraph_CoEdgeId aCoEdgeId = myGraph.Topo().Edges().FindCoEdgeId(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); +} + +TEST_F(BRepGraph_ViewsTest, EdgeOps_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 int aNbFaces = myGraph.Topo().Faces().Nb(); + const BRepGraph_EdgeId anEdge(0); + const NCollection_Vector& aEdgeFaces = myGraph.Topo().Edges().Faces(anEdge); + + // Find a face NOT adjacent to edge 0. + BRepGraph_FaceId aNonAdjacentFace; + for (int aFaceIdx = 0; aFaceIdx < aNbFaces; ++aFaceIdx) + { + bool isAdjacent = false; + for (const BRepGraph_FaceId& aFace : aEdgeFaces) + { + if (aFace.Index == aFaceIdx) + { + isAdjacent = true; + break; + } + } + if (!isAdjacent) + { + aNonAdjacentFace = BRepGraph_FaceId(aFaceIdx); + break; + } + } + if (aNonAdjacentFace.IsValid()) + { + const BRepGraph_CoEdgeId aCoEdgeId = + myGraph.Topo().Edges().FindCoEdgeId(anEdge, aNonAdjacentFace); + EXPECT_FALSE(aCoEdgeId.IsValid()); + } +} + TEST_F(BRepGraph_ViewsTest, AttrsView_MutFace_InvalidatesEntry) { BRepGraph_FaceId aFaceId(0); @@ -486,6 +616,7 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_MutFace_InvalidatesEntry) 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) @@ -499,6 +630,39 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_RemoveNode_InvalidatesEntry) 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.Builder().MutFaceRef(aRef); + aFaceRef->Orientation = 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.Builder().MutFaceRef(aRef); + aFaceRef->IsRemoved = true; + } + + EXPECT_FALSE(myGraph.Cache().Has(aRef, testUserAttrKind())); + EXPECT_FALSE(myGraph.Cache().CacheKindIter(aRef).More()); } TEST_F(BRepGraph_ViewsTest, RefsView_ActiveCounts_MatchFreshBuild) @@ -581,6 +745,97 @@ TEST_F(BRepGraph_ViewsTest, RefsView_VertexRefIdsOfEdge_ContainsBoundaryVertices } } +TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_RoundTripForTypedRef) +{ + const NCollection_Vector& aFaceRefs = + myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId(0)); + ASSERT_GT(aFaceRefs.Length(), 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.Builder().MutFaceRef(aFaceRefId); + aFaceRef->LocalLocation = TopLoc_Location(aTrsf); + aFaceRef->Orientation = 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)); + + const BRepGraph_CoEdgeRefId aCoEdgeRefId = + myGraph.Topo().Wires().Definition(BRepGraph_WireId(0)).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); +} + +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)); + + 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)); + + EXPECT_FALSE(myGraph.Refs().RefAtStep(BRepGraph_NodeId(aWireId), 100).IsValid()); +} + +TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_OccurrenceDefaults) +{ + const BRepGraph_ProductId aPartProduct = + myGraph.Builder().AddProduct(BRepGraph_NodeId(BRepGraph_SolidId(0))); + const BRepGraph_ProductId anAssembly = myGraph.Builder().AddAssemblyProduct(); + ASSERT_TRUE(aPartProduct.IsValid()); + ASSERT_TRUE(anAssembly.IsValid()); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(4.0, 5.0, 6.0)); + ASSERT_TRUE( + myGraph.Builder().AddOccurrence(anAssembly, aPartProduct, TopLoc_Location(aTrsf)).IsValid()); + + const NCollection_Vector& anOccurrenceRefs = + myGraph.Refs().Occurrences().IdsOf(anAssembly); + ASSERT_EQ(anOccurrenceRefs.Length(), 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)); +} + +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())); + + const NCollection_Vector& aFaceRefs = + myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId(0)); + ASSERT_GT(aFaceRefs.Length(), 0); + const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); + + { + BRepGraph_MutGuard aFaceRef = myGraph.Builder().MutFaceRef(aFaceRefId); + aFaceRef->IsRemoved = true; + } + + EXPECT_TRUE(myGraph.Refs().IsRemoved(aFaceRefId)); +} + // ---------- ShapesView ---------- TEST_F(BRepGraph_ViewsTest, ShapesView_Shape_NonNull) diff --git a/src/ModelingData/TKBRep/GTests/FILES.cmake b/src/ModelingData/TKBRep/GTests/FILES.cmake index 4ab52d1905..1cdf46b27f 100644 --- a/src/ModelingData/TKBRep/GTests/FILES.cmake +++ b/src/ModelingData/TKBRep/GTests/FILES.cmake @@ -17,6 +17,7 @@ set(OCCT_TKBRep_GTests_FILES BRepGraph_DeferredInvalidation_Test.cxx BRepGraph_MutationGen_Test.cxx BRepGraph_Convenience_Test.cxx + BRepGraph_RelatedIterator_Test.cxx BRepGraph_EdgeCases_Test.cxx BRepGraph_ChildExplorer_Test.cxx BRepGraph_ParentExplorer_Test.cxx