diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx index c19664274f..ed78a99784 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -33,7 +34,7 @@ namespace { -static Standard_GUID generateRandomGUID() +Standard_GUID generateRandomGUID() { std::random_device aRD; MathUtils::RandomGenerator aRNG(aRD()); @@ -268,6 +269,13 @@ bool BRepGraph::IsDone() const //================================================================================================= +bool BRepGraph::ValidateReverseIndex() const +{ + return myData->myIncStorage.ValidateReverseIndex(); +} + +//================================================================================================= + const NCollection_DynamicArray& BRepGraph::RootProductIds() const { return myData->myRootProductIds; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx index d1382d72f4..1d728dc027 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx @@ -146,6 +146,11 @@ public: //! Return true if the graph was successfully built. [[nodiscard]] Standard_EXPORT bool IsDone() const; + //! Verify reverse-index consistency against forward entity / reference-entry tables. + //! Intended for debug builds and regression tests of incremental mutation paths. + //! @return true when every forward ref has a matching reverse entry. + [[nodiscard]] Standard_EXPORT bool ValidateReverseIndex() const; + //! Return root product identifiers (products not referenced by any active occurrence). //! Maintained incrementally by Editor/EditorView mutations. //! Returns empty vector if the graph has not been built. diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx index 24f0768d96..7cd214f0c4 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx @@ -82,6 +82,10 @@ BRepGraph_NodeId BRepGraph_Builder::detectTopologyRoot(const BRepGraph& th if (aNewCount <= theOldCountOfShapeKind) return BRepGraph_NodeId(); + // BRepGraphInc_Populate::Append appends entities in declaration order, so the first entity + // appended for a given shape type is always the shape root (index == pre-append count). + // This assumption holds as long as no intermediate entities of the same type are inserted + // before the root node during population. switch (theShapeType) { case TopAbs_COMPOUND: @@ -371,15 +375,19 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph if (!aChildProduct.IsValid()) return aResult; + BRepGraph_OccurrenceRefId anOccRefId; const BRepGraph_OccurrenceId anOccId = theGraph.Editor().Products().LinkProducts(BRepGraph_ProductId(theParent), aChildProduct, - theShape.Location()); + theShape.Location(), + BRepGraph_OccurrenceId(), + &anOccRefId); if (!anOccId.IsValid()) return aResult; - aResult.Product = aChildProduct; - aResult.Occurrence = anOccId; - aResult.Ok = true; + aResult.Product = aChildProduct; + aResult.Occurrence = anOccId; + aResult.InsertedRef = anOccRefId; + aResult.Ok = true; return aResult; } case BRepGraph_NodeId::Kind::Compound: { @@ -389,7 +397,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } @@ -403,7 +411,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } @@ -411,7 +419,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theGraph.Editor().Shells().AddChild(aShell, aResult.TopologyRoot, theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } @@ -425,7 +433,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } @@ -433,7 +441,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theGraph.Editor().Solids().AddChild(aSolid, aResult.TopologyRoot, theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } @@ -446,7 +454,7 @@ BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph theShape.Orientation()); if (!aRid.IsValid()) return aResult; - aResult.InsertedRef = BRepGraph_RefId(aRid); + aResult.InsertedRef = aRid; aResult.Ok = true; return aResult; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx index a011053a6c..523630332f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx @@ -75,12 +75,13 @@ public: //! - Shell: appends a Face as a FaceRef; other shapes via AddChild. //! - Solid: appends a Shell as a ShellRef; other shapes via AddChild. //! - CompSolid: appends a Solid as a SolidRef. - //! Other parent kinds yield an invalid Result. + //! Other parent kinds (Wire, Edge, Vertex, Occurrence) are not supported and yield + //! an invalid Result (Result::Ok == false) without modification to the graph. //! @param[in,out] theGraph graph to populate //! @param[in] theShape shape to ingest //! @param[in] theParent parent node receiving the topology - //! @return Result with TopologyRoot set, plus (Product, Occurrence) for Product parents - //! or InsertedRef for topology container parents. + //! @return Result with TopologyRoot set, plus (Product, Occurrence, InsertedRef) for Product + //! parents or InsertedRef for topology container parents. [[nodiscard]] static Standard_EXPORT Result Add(BRepGraph& theGraph, const TopoDS_Shape& theShape, const BRepGraph_NodeId theParent); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx index eca0a86750..ebb19b624c 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx @@ -422,19 +422,22 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const // Copy edge properties. BRepGraph_MutGuard aNewEdge = aNewGraph.Editor().Edges().Mut(aNewEdgeId); - aNewEdge->IsDegenerate = anOldEdge.IsDegenerate; - aNewEdge->IsClosed = anOldEdge.IsClosed; - aNewEdge->SameParameter = anOldEdge.SameParameter; - aNewEdge->SameRange = anOldEdge.SameRange; + aNewGraph.Editor().Edges().SetDegenerate(aNewEdge, anOldEdge.IsDegenerate); + aNewGraph.Editor().Edges().SetIsClosed(aNewEdge, anOldEdge.IsClosed); + + aNewGraph.Editor().Edges().SetSameParameter(aNewEdge, anOldEdge.SameParameter); + + aNewGraph.Editor().Edges().SetSameRange(aNewEdge, anOldEdge.SameRange); if (anOldEdge.StartVertexRefId.IsValid() && aNewEdge->StartVertexRefId.IsValid()) { const BRepGraphInc::VertexRef& anOldStartRef = theGraph.Refs().Vertices().Entry(anOldEdge.StartVertexRefId); BRepGraph_MutGuard aNewStartRef = aNewGraph.Editor().Vertices().MutRef(aNewEdge->StartVertexRefId); - aNewStartRef->Orientation = anOldStartRef.Orientation; - aNewStartRef->LocalLocation = anOldStartRef.LocalLocation; + aNewGraph.Editor().Vertices().SetRefOrientation(aNewStartRef, anOldStartRef.Orientation); + + aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewStartRef, anOldStartRef.LocalLocation); aVertexRefMap.Bind(anOldEdge.StartVertexRefId, aNewEdge->StartVertexRefId); } @@ -444,8 +447,9 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const theGraph.Refs().Vertices().Entry(anOldEdge.EndVertexRefId); BRepGraph_MutGuard aNewEndRef = aNewGraph.Editor().Vertices().MutRef(aNewEdge->EndVertexRefId); - aNewEndRef->Orientation = anOldEndRef.Orientation; - aNewEndRef->LocalLocation = anOldEndRef.LocalLocation; + aNewGraph.Editor().Vertices().SetRefOrientation(aNewEndRef, anOldEndRef.Orientation); + + aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewEndRef, anOldEndRef.LocalLocation); aVertexRefMap.Bind(anOldEdge.EndVertexRefId, aNewEdge->EndVertexRefId); } @@ -471,7 +475,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_MutGuard aNewInternalRef = aNewGraph.Editor().Vertices().MutRef(aNewInternalRefId); - aNewInternalRef->LocalLocation = anOldInternalRef.LocalLocation; + aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewInternalRef, + anOldInternalRef.LocalLocation); aVertexRefMap.Bind(anOldInternalRefId, aNewInternalRefId); } } @@ -516,8 +521,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const theGraph.Refs().CoEdges().Entry(anOldCoEdgeRefs.Value(aRefIdx)); BRepGraph_MutGuard aNewRef = aNewGraph.Editor().CoEdges().MutRef(aNewCoEdgeRefs.Value(aRefIdx)); - aNewRef->LocalLocation = anOldRef.LocalLocation; - + aNewGraph.Editor().CoEdges().SetRefLocalLocation(aNewRef, anOldRef.LocalLocation); // Build aCoEdgeMap from actual CoEdgeDef IDs returned by the builder, // not from a predicted Nb()-based offset which can be wrong. const BRepGraphInc::CoEdgeRef& aNewCoEdgeRef = @@ -528,7 +532,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } BRepGraph_MutGuard aNewWire = aNewGraph.Editor().Wires().Mut(aNewWireId); - aNewWire->IsClosed = isWireClosedByIncidence(aNewGraph, aNewWireId); + aNewGraph.Editor().Wires().SetIsClosed(aNewWire, + isWireClosedByIncidence(aNewGraph, aNewWireId)); } // Faces. @@ -571,17 +576,18 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aNewGraph.Editor().Faces().Add(aSurf, aNewOuterWire, aNewInnerWires, anOldFace.Tolerance); BRepGraph_MutGuard aNewFace = aNewGraph.Editor().Faces().Mut(aNewFaceId); - aNewFace->NaturalRestriction = anOldFace.NaturalRestriction; - + aNewGraph.Editor().Faces().SetNaturalRestriction(aNewFace, anOldFace.NaturalRestriction); const NCollection_DynamicArray& aNewWireRefs = aNewFace->WireRefIds; if (anOldOuterWireRef.IsValid() && !aNewWireRefs.IsEmpty()) { const BRepGraphInc::WireRef& anOldOuterRef = theGraph.Refs().Wires().Entry(anOldOuterWireRef); BRepGraph_MutGuard aNewOuterRef = aNewGraph.Editor().Wires().MutRef(aNewWireRefs.First()); - aNewOuterRef->IsOuter = anOldOuterRef.IsOuter; - aNewOuterRef->Orientation = anOldOuterRef.Orientation; - aNewOuterRef->LocalLocation = anOldOuterRef.LocalLocation; + aNewGraph.Editor().Wires().SetRefIsOuter(aNewOuterRef, anOldOuterRef.IsOuter); + + aNewGraph.Editor().Wires().SetRefOrientation(aNewOuterRef, anOldOuterRef.Orientation); + + aNewGraph.Editor().Wires().SetRefLocalLocation(aNewOuterRef, anOldOuterRef.LocalLocation); aWireRefMap.Bind(anOldOuterWireRef, aNewWireRefs.First()); } @@ -593,9 +599,11 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const theGraph.Refs().Wires().Entry(anOldInnerWireRefs.Value(anInnerIdx)); BRepGraph_MutGuard aNewInnerRef = aNewGraph.Editor().Wires().MutRef(aNewWireRefs.Value(anInnerIdx + 1)); - aNewInnerRef->IsOuter = anOldInnerRef.IsOuter; - aNewInnerRef->Orientation = anOldInnerRef.Orientation; - aNewInnerRef->LocalLocation = anOldInnerRef.LocalLocation; + aNewGraph.Editor().Wires().SetRefIsOuter(aNewInnerRef, anOldInnerRef.IsOuter); + + aNewGraph.Editor().Wires().SetRefOrientation(aNewInnerRef, anOldInnerRef.Orientation); + + aNewGraph.Editor().Wires().SetRefLocalLocation(aNewInnerRef, anOldInnerRef.LocalLocation); aWireRefMap.Bind(anOldInnerWireRefs.Value(anInnerIdx), aNewWireRefs.Value(anInnerIdx + 1)); } @@ -619,7 +627,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_MutGuard aNewVertexRef = aNewGraph.Editor().Vertices().MutRef(aNewVertexRefId); - aNewVertexRef->LocalLocation = anOldVertexRef.LocalLocation; + aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewVertexRef, + anOldVertexRef.LocalLocation); aVertexRefMap.Bind(anOldVertexRefId, aNewVertexRefId); } } @@ -638,26 +647,27 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_MutGuard aNewCoEdge = aNewGraph.Editor().CoEdges().Mut(*aNewCoEdgeId); - aNewCoEdge->FaceDefId = BRepGraph_FaceId::FromNodeId(remapId(anOldCoEdge.FaceDefId)); - aNewCoEdge->Orientation = anOldCoEdge.Orientation; - aNewCoEdge->ParamFirst = anOldCoEdge.ParamFirst; - aNewCoEdge->ParamLast = anOldCoEdge.ParamLast; - aNewCoEdge->Continuity = anOldCoEdge.Continuity; - aNewCoEdge->UV1 = anOldCoEdge.UV1; - aNewCoEdge->UV2 = anOldCoEdge.UV2; - aNewCoEdge->SeamContinuity = anOldCoEdge.SeamContinuity; + aNewGraph.Editor().CoEdges().SetFaceDefId( + aNewCoEdge, + BRepGraph_FaceId::FromNodeId(remapId(anOldCoEdge.FaceDefId))); + aNewGraph.Editor().CoEdges().SetOrientation(aNewCoEdge, anOldCoEdge.Orientation); + + aNewGraph.Editor().CoEdges().SetParamRange(aNewCoEdge, + anOldCoEdge.ParamFirst, + anOldCoEdge.ParamLast); + + aNewGraph.Editor().CoEdges().SetContinuity(aNewCoEdge, anOldCoEdge.Continuity); + + aNewGraph.Editor().CoEdges().SetUVBox(aNewCoEdge, anOldCoEdge.UV1, anOldCoEdge.UV2); + + aNewGraph.Editor().CoEdges().SetSeamContinuity(aNewCoEdge, anOldCoEdge.SeamContinuity); // If the owning face was removed, keep the coedge as free-wire usage only. // Drop face-bound parametric payload (PCurve/UV/continuity) to avoid stale // face references causing reconstruction/meshing corruption. if (!aNewCoEdge->FaceDefId.IsValid()) { - aNewCoEdge->Curve2DRepId = BRepGraph_Curve2DRepId(); - aNewCoEdge->ParamFirst = 0.0; - aNewCoEdge->ParamLast = 0.0; - aNewCoEdge->Continuity = GeomAbs_C0; - aNewCoEdge->UV1 = gp_Pnt2d(); - aNewCoEdge->UV2 = gp_Pnt2d(); + aNewGraph.Editor().CoEdges().ClearPCurveBinding(aNewCoEdge); continue; } @@ -672,9 +682,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const const BRepGraph_Curve2DRepId aNewRepId = aNewGraph.Editor().CoEdges().CreateCurve2DRep(anOldPCurve); if (aNewRepId.IsValid()) - { - aNewCoEdge->Curve2DRepId = aNewRepId; - } + aNewGraph.Editor().CoEdges().SetCurve2DRepId(aNewCoEdge, aNewRepId); } } } @@ -703,7 +711,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_MutGuard aNewCoEdge = aNewGraph.Editor().CoEdges().Mut(*aNewCoEdgeId); - aNewCoEdge->SeamPairId = *aNewSeamPairId; + aNewGraph.Editor().CoEdges().SetSeamPairId(aNewCoEdge, *aNewSeamPairId); } // Shells. @@ -730,7 +738,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { BRepGraph_MutGuard aNewFaceRef = aNewGraph.Editor().Faces().MutRef(aNewFaceRefId); - aNewFaceRef->LocalLocation = aFR.LocalLocation; + aNewGraph.Editor().Faces().SetRefLocalLocation(aNewFaceRef, aFR.LocalLocation); aFaceRefMap.Bind(aRefIt.CurrentId(), aNewFaceRefId); } } @@ -752,13 +760,14 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } BRepGraph_MutGuard aNewChildRef = aNewGraph.Editor().Gen().MutChildRef(aNewChildRefId); - aNewChildRef->LocalLocation = aCR.LocalLocation; + aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewChildRef, aCR.LocalLocation); aChildRefMap.Bind(aRefIt.CurrentId(), aNewChildRefId); } BRepGraph_MutGuard aNewShell = aNewGraph.Editor().Shells().Mut(aNewShellId); - aNewShell->IsClosed = isShellClosedByIncidence(aNewGraph, aNewShellId); + aNewGraph.Editor().Shells().SetIsClosed(aNewShell, + isShellClosedByIncidence(aNewGraph, aNewShellId)); } // Solids. @@ -784,7 +793,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { BRepGraph_MutGuard aNewShellRef = aNewGraph.Editor().Shells().MutRef(aNewShellRefId); - aNewShellRef->LocalLocation = aSR.LocalLocation; + aNewGraph.Editor().Shells().SetRefLocalLocation(aNewShellRef, aSR.LocalLocation); aShellRefMap.Bind(aRefIt.CurrentId(), aNewShellRefId); } } @@ -806,7 +815,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } BRepGraph_MutGuard aNewChildRef = aNewGraph.Editor().Gen().MutChildRef(aNewChildRefId); - aNewChildRef->LocalLocation = aCR.LocalLocation; + aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewChildRef, aCR.LocalLocation); aChildRefMap.Bind(aRefIt.CurrentId(), aNewChildRefId); } } @@ -845,8 +854,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const theGraph.Refs().Children().Entry(anOldChildRefs.Value(aRefIdx)); BRepGraph_MutGuard aNewRef = aNewGraph.Editor().Gen().MutChildRef(aNewChildRefs.Value(aRefIdx)); - aNewRef->Orientation = anOldRef.Orientation; - aNewRef->LocalLocation = anOldRef.LocalLocation; + aNewGraph.Editor().Gen().SetChildRefOrientation(aNewRef, anOldRef.Orientation); + aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewRef, anOldRef.LocalLocation); aChildRefMap.Bind(anOldChildRefs.Value(aRefIdx), aNewChildRefs.Value(aRefIdx)); } } @@ -886,8 +895,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const theGraph.Refs().Solids().Entry(anOldSolidRefs.Value(aRefIdx)); BRepGraph_MutGuard aNewRef = aNewGraph.Editor().Solids().MutRef(aNewSolidRefs.Value(aRefIdx)); - aNewRef->Orientation = anOldRef.Orientation; - aNewRef->LocalLocation = anOldRef.LocalLocation; + aNewGraph.Editor().Solids().SetRefOrientation(aNewRef, anOldRef.Orientation); + aNewGraph.Editor().Solids().SetRefLocalLocation(aNewRef, anOldRef.LocalLocation); aSolidRefMap.Bind(anOldSolidRefs.Value(aRefIdx), aNewSolidRefs.Value(aRefIdx)); } } @@ -1109,22 +1118,22 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { BRepGraph_MutGuard aFace = aNewGraph.Editor().Faces().Mut(anIt.CurrentId()); - aFace->TriangulationRepId = BRepGraph_TriangulationRepId(); + aNewGraph.Editor().Faces().SetTriangulationRep(aFace, BRepGraph_TriangulationRepId()); } for (BRepGraph_Iterator anIt(aNewGraph); anIt.More(); anIt.Next()) { BRepGraph_MutGuard anEdge = aNewGraph.Editor().Edges().Mut(anIt.CurrentId()); - anEdge->Polygon3DRepId = BRepGraph_Polygon3DRepId(); + aNewGraph.Editor().Edges().SetPolygon3DRepId(anEdge, BRepGraph_Polygon3DRepId()); } for (BRepGraph_Iterator anIt(aNewGraph); anIt.More(); anIt.Next()) { BRepGraph_MutGuard aCoEdge = aNewGraph.Editor().CoEdges().Mut(anIt.CurrentId()); - aCoEdge->Polygon2DRepId = BRepGraph_Polygon2DRepId(); - aCoEdge->PolygonOnTriRepId = BRepGraph_PolygonOnTriRepId(); + aNewGraph.Editor().CoEdges().SetPolygon2DRepId(aCoEdge, BRepGraph_Polygon2DRepId()); + aNewGraph.Editor().CoEdges().SetPolygonOnTriRepId(aCoEdge, BRepGraph_PolygonOnTriRepId()); } aResult.NbNodesAfter = static_cast(aNewGraph.Topo().Gen().NbNodes()); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx index b3e54273e4..868de47b3f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx @@ -77,6 +77,552 @@ void transferFreshCacheValues(const BRepGraph& theSrcGraph, } } +//! Deferred cache-transfer queue. +//! +//! Pairs are recorded only for entities that actually carry at least one fresh cache +//! value on the source graph; entities with no cache are skipped, so the queue size is +//! O(cached entities), not O(graph size). +//! +//! We defer instead of transferring eagerly because the destination graph's SubtreeGen +//! continues to advance during construction (every Mut/AddPCurve propagates via the +//! reverse index). A Set() during construction captures an intermediate SubtreeGen +//! that Get() later mismatches. Draining after all mutations means Set() captures the +//! final SubtreeGen and the cache survives the copy. +struct DeferredCacheTransfers +{ + using NodePair = std::pair; + using RefPair = std::pair; + + NCollection_DynamicArray NodePairs; + NCollection_DynamicArray RefPairs; + + template + static bool srcHasAnyCache(const BRepGraph& theSrc, const TKeyId theKey) + { + return theSrc.Cache().CacheKindIter(theKey).More(); + } + + void DeferNode(const BRepGraph& theSrc, + const BRepGraph_NodeId theSrcNode, + const BRepGraph_NodeId theDstNode) + { + if (srcHasAnyCache(theSrc, theSrcNode)) + NodePairs.Append({theSrcNode, theDstNode}); + } + + void DeferRef(const BRepGraph& theSrc, + const BRepGraph_RefId theSrcRef, + const BRepGraph_RefId theDstRef) + { + if (srcHasAnyCache(theSrc, theSrcRef)) + RefPairs.Append({theSrcRef, theDstRef}); + } + + void Drain(const BRepGraph& theSrc, BRepGraph& theDst) const + { + for (const auto& aPair : NodePairs) + transferFreshCacheValues(theSrc, aPair.first, theDst, aPair.second); + for (const auto& aPair : RefPairs) + transferFreshCacheValues(theSrc, aPair.first, theDst, aPair.second); + } +}; + +//! Memoised copy context shared across all ensure* free functions. +//! +//! Source/result pointers that require friend access (incStorage, meshCache, data) +//! are pre-extracted inside the friend methods Perform / CopyNode before any +//! ensure* function is called. +struct GraphCopyContext +{ + const BRepGraph& Source; + BRepGraph Result; + bool CopyGeom; + bool CopyMesh; + bool ReserveCache; + DeferredCacheTransfers Deferred; + const BRepGraphInc_Storage* SrcStorage = nullptr; + BRepGraphInc_Storage* DstStorage = nullptr; + BRepGraph_MeshCacheStorage* DstMesh = nullptr; + BRepGraph_Data* DstData = nullptr; + + NCollection_DataMap Vertices; + NCollection_DataMap Edges; + NCollection_DataMap Wires; + NCollection_DataMap Faces; + NCollection_DataMap Shells; + NCollection_DataMap Solids; + NCollection_DataMap Compounds; + NCollection_DataMap CompSolids; + NCollection_DataMap Products; + NCollection_DataMap Occurrences; + NCollection_DataMap OccurrenceRefs; + + explicit GraphCopyContext(const BRepGraph& theSrc, + bool theCopyGeom, + bool theCopyMesh, + bool theReserveCache) + : Source(theSrc), + CopyGeom(theCopyGeom), + CopyMesh(theCopyMesh), + ReserveCache(theReserveCache) + { + } +}; + +// Forward declarations - needed for mutual recursion between ensure* functions. +BRepGraph_VertexId ensureVertex(GraphCopyContext& ctx, BRepGraph_VertexId srcId); +BRepGraph_EdgeId ensureEdge(GraphCopyContext& ctx, BRepGraph_EdgeId srcId); +BRepGraph_WireId ensureWire(GraphCopyContext& ctx, BRepGraph_WireId srcId); +BRepGraph_FaceId ensureFace(GraphCopyContext& ctx, BRepGraph_FaceId srcId); +BRepGraph_ShellId ensureShell(GraphCopyContext& ctx, BRepGraph_ShellId srcId); +BRepGraph_SolidId ensureSolid(GraphCopyContext& ctx, BRepGraph_SolidId srcId); +BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId srcId); +BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& ctx, BRepGraph_CompSolidId srcId); +BRepGraph_ProductId ensureProduct(GraphCopyContext& ctx, BRepGraph_ProductId srcId); +BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& ctx, BRepGraph_OccurrenceId srcId); +BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& ctx, + BRepGraph_OccurrenceRefId srcRefId); +void ensureNode(GraphCopyContext& ctx, BRepGraph_NodeId srcNodeId); +BRepGraph_NodeId mappedNode(const GraphCopyContext& ctx, BRepGraph_NodeId srcId); + +//================================================================================================= + +BRepGraph_NodeId mappedNode(const GraphCopyContext& ctx, BRepGraph_NodeId srcId) +{ + using Kind = BRepGraph_NodeId::Kind; + switch (srcId.NodeKind) + { + case Kind::Vertex: { + const BRepGraph_VertexId* p = ctx.Vertices.Seek(BRepGraph_VertexId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Edge: { + const BRepGraph_EdgeId* p = ctx.Edges.Seek(BRepGraph_EdgeId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Wire: { + const BRepGraph_WireId* p = ctx.Wires.Seek(BRepGraph_WireId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Face: { + const BRepGraph_FaceId* p = ctx.Faces.Seek(BRepGraph_FaceId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Shell: { + const BRepGraph_ShellId* p = ctx.Shells.Seek(BRepGraph_ShellId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Solid: { + const BRepGraph_SolidId* p = ctx.Solids.Seek(BRepGraph_SolidId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Compound: { + const BRepGraph_CompoundId* p = ctx.Compounds.Seek(BRepGraph_CompoundId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::CompSolid: { + const BRepGraph_CompSolidId* p = ctx.CompSolids.Seek(BRepGraph_CompSolidId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Product: { + const BRepGraph_ProductId* p = ctx.Products.Seek(BRepGraph_ProductId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::Occurrence: { + const BRepGraph_OccurrenceId* p = ctx.Occurrences.Seek(BRepGraph_OccurrenceId(srcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + default: + return BRepGraph_NodeId(); + } +} + +//================================================================================================= + +BRepGraph_VertexId ensureVertex(GraphCopyContext& ctx, BRepGraph_VertexId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Vertices())) + return BRepGraph_VertexId(); + const BRepGraph_VertexId* anExisting = ctx.Vertices.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::VertexDef& aVtx = ctx.Source.Topo().Vertices().Definition(srcId); + const BRepGraph_VertexId aNewId = ctx.Result.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); + ctx.Vertices.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + return aNewId; +} + +//================================================================================================= + +BRepGraph_EdgeId ensureEdge(GraphCopyContext& ctx, BRepGraph_EdgeId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Edges())) + return BRepGraph_EdgeId(); + const BRepGraph_EdgeId* anExisting = ctx.Edges.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::EdgeDef& anEdge = ctx.Source.Topo().Edges().Definition(srcId); + const BRepGraph_VertexId aNewStart = + ensureVertex(ctx, BRepGraph_Tool::Edge::StartVertexId(ctx.Source, srcId)); + const BRepGraph_VertexId aNewEnd = + ensureVertex(ctx, BRepGraph_Tool::Edge::EndVertexId(ctx.Source, srcId)); + + const occ::handle& aSrcCurve = BRepGraph_Tool::Edge::Curve(ctx.Source, srcId); + occ::handle aCurve = copyCurve(aSrcCurve, ctx.CopyGeom); + + const BRepGraph_EdgeId aNewId = ctx.Result.Editor().Edges().Add(aNewStart, + aNewEnd, + aCurve, + anEdge.ParamFirst, + anEdge.ParamLast, + anEdge.Tolerance); + { + BRepGraph_MutGuard aG = ctx.Result.Editor().Edges().Mut(aNewId); + ctx.Result.Editor().Edges().SetDegenerate(aG, anEdge.IsDegenerate); + ctx.Result.Editor().Edges().SetSameParameter(aG, anEdge.SameParameter); + ctx.Result.Editor().Edges().SetSameRange(aG, anEdge.SameRange); + } + ctx.Edges.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + return aNewId; +} + +//================================================================================================= + +BRepGraph_WireId ensureWire(GraphCopyContext& ctx, BRepGraph_WireId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Wires())) + return BRepGraph_WireId(); + const BRepGraph_WireId* anExisting = ctx.Wires.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + NCollection_DynamicArray> aWireEdges; + for (BRepGraph_RefsCoEdgeOfWire aCEIt(ctx.Source, srcId); aCEIt.More(); aCEIt.Next()) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = ctx.Source.Topo().CoEdges().Definition( + ctx.Source.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); + const BRepGraph_EdgeId aNewEdgeId = ensureEdge(ctx, aCoEdge.EdgeDefId); + aWireEdges.Append(std::make_pair(aNewEdgeId, aCoEdge.Orientation)); + } + const BRepGraph_WireId aNewId = ctx.Result.Editor().Wires().Add(aWireEdges); + ctx.Wires.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + return aNewId; +} + +//================================================================================================= + +void ensurePCurvesForFace(GraphCopyContext& ctx, + BRepGraph_FaceId srcFaceId, + BRepGraph_FaceId newFaceId) +{ + for (BRepGraph_RefsWireOfFace aWIt(ctx.Source, srcFaceId); aWIt.More(); aWIt.Next()) + { + const BRepGraph_WireId aSrcWireId = ctx.Source.Refs().Wires().Entry(aWIt.CurrentId()).WireDefId; + for (BRepGraph_RefsCoEdgeOfWire aCEIt(ctx.Source, aSrcWireId); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aSrcCoEdgeId = + ctx.Source.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId; + const BRepGraphInc::CoEdgeDef& aCoEdge = ctx.Source.Topo().CoEdges().Definition(aSrcCoEdgeId); + if (!aCoEdge.Curve2DRepId.IsValid()) + continue; + const BRepGraph_EdgeId* aNewEdge = ctx.Edges.Seek(aCoEdge.EdgeDefId); + if (aNewEdge == nullptr) + continue; + const occ::handle& aSrcPC = + BRepGraph_Tool::CoEdge::PCurve(ctx.Source, aSrcCoEdgeId); + occ::handle aNewPC = copyPCurve(aSrcPC, ctx.CopyGeom); + ctx.Result.Editor().CoEdges().AddPCurve(*aNewEdge, + newFaceId, + aNewPC, + aCoEdge.ParamFirst, + aCoEdge.ParamLast, + aCoEdge.Orientation); + } + } +} + +//================================================================================================= + +BRepGraph_FaceId ensureFace(GraphCopyContext& ctx, BRepGraph_FaceId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Faces())) + return BRepGraph_FaceId(); + const BRepGraph_FaceId* anExisting = ctx.Faces.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::FaceDef& aFace = ctx.Source.Topo().Faces().Definition(srcId); + + BRepGraph_WireId anOuterWire; + NCollection_DynamicArray anInnerWires; + for (BRepGraph_RefsWireOfFace aWRIt(ctx.Source, srcId); aWRIt.More(); aWRIt.Next()) + { + const BRepGraphInc::WireRef& aWR = ctx.Source.Refs().Wires().Entry(aWRIt.CurrentId()); + const BRepGraph_WireId aNewWire = ensureWire(ctx, aWR.WireDefId); + if (aWR.IsOuter) + anOuterWire = aNewWire; + else + anInnerWires.Append(aNewWire); + } + + const occ::handle& aSrcSurf = BRepGraph_Tool::Face::Surface(ctx.Source, srcId); + occ::handle aSurf = copySurface(aSrcSurf, ctx.CopyGeom); + + const BRepGraph_FaceId aNewId = + ctx.Result.Editor().Faces().Add(aSurf, anOuterWire, anInnerWires, aFace.Tolerance); + { + BRepGraph_MutGuard aG = ctx.Result.Editor().Faces().Mut(aNewId); + ctx.Result.Editor().Faces().SetNaturalRestriction(aG, aFace.NaturalRestriction); + if (ctx.CopyMesh) + ctx.Result.Editor().Faces().SetTriangulationRep(aG, aFace.TriangulationRepId); + } + + if (ctx.CopyMesh) + { + const BRepGraph_MeshCache::FaceMeshEntry* aCached = ctx.Source.Mesh().Faces().CachedMesh(srcId); + if (aCached != nullptr) + { + BRepGraph_MeshCache::FaceMeshEntry& aNewEntry = ctx.DstMesh->ChangeFaceMesh(aNewId); + aNewEntry = *aCached; + // Update StoredOwnGen to the destination face's generation so that freshness checks pass. + // The raw copy carries the source OwnGen, which differs from the newly created face. + aNewEntry.StoredOwnGen = ctx.DstStorage->Face(aNewId).OwnGen; + } + } + + ctx.Faces.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + + // PCurves require both the face and its edges to exist in the result. + ensurePCurvesForFace(ctx, srcId, aNewId); + return aNewId; +} + +//================================================================================================= + +BRepGraph_ShellId ensureShell(GraphCopyContext& ctx, BRepGraph_ShellId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Shells())) + return BRepGraph_ShellId(); + const BRepGraph_ShellId* anExisting = ctx.Shells.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::ShellDef& aShellDef = ctx.Source.Topo().Shells().Definition(srcId); + const BRepGraph_ShellId aNewId = ctx.Result.Editor().Shells().Add(); + { + BRepGraph_MutGuard aG = ctx.Result.Editor().Shells().Mut(aNewId); + ctx.Result.Editor().Shells().SetIsClosed(aG, aShellDef.IsClosed); + } + ctx.Shells.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + + for (BRepGraph_RefsFaceOfShell aFRIt(ctx.Source, srcId); aFRIt.More(); aFRIt.Next()) + { + const BRepGraphInc::FaceRef& aFR = ctx.Source.Refs().Faces().Entry(aFRIt.CurrentId()); + const BRepGraph_FaceId aNewFace = ensureFace(ctx, aFR.FaceDefId); + const BRepGraph_FaceRefId aNewFaceRefId = + ctx.Result.Editor().Shells().AddFace(aNewId, aNewFace, aFR.Orientation); + ctx.Deferred.DeferRef(ctx.Source, aFRIt.CurrentId(), aNewFaceRefId); + } + return aNewId; +} + +//================================================================================================= + +BRepGraph_SolidId ensureSolid(GraphCopyContext& ctx, BRepGraph_SolidId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Solids())) + return BRepGraph_SolidId(); + const BRepGraph_SolidId* anExisting = ctx.Solids.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraph_SolidId aNewId = ctx.Result.Editor().Solids().Add(); + ctx.Solids.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + + for (BRepGraph_RefsShellOfSolid aSRIt(ctx.Source, srcId); aSRIt.More(); aSRIt.Next()) + { + const BRepGraphInc::ShellRef& aSR = ctx.Source.Refs().Shells().Entry(aSRIt.CurrentId()); + const BRepGraph_ShellId aNewShell = ensureShell(ctx, aSR.ShellDefId); + const BRepGraph_ShellRefId aNewShellRefId = + ctx.Result.Editor().Solids().AddShell(aNewId, aNewShell, aSR.Orientation); + ctx.Deferred.DeferRef(ctx.Source, aSRIt.CurrentId(), aNewShellRefId); + } + return aNewId; +} + +//================================================================================================= + +void ensureNode(GraphCopyContext& ctx, BRepGraph_NodeId srcNodeId) +{ + using Kind = BRepGraph_NodeId::Kind; + switch (srcNodeId.NodeKind) + { + case Kind::Vertex: + ensureVertex(ctx, BRepGraph_VertexId(srcNodeId.Index)); + break; + case Kind::Edge: + ensureEdge(ctx, BRepGraph_EdgeId(srcNodeId.Index)); + break; + case Kind::Wire: + ensureWire(ctx, BRepGraph_WireId(srcNodeId.Index)); + break; + case Kind::Face: + ensureFace(ctx, BRepGraph_FaceId(srcNodeId.Index)); + break; + case Kind::Shell: + ensureShell(ctx, BRepGraph_ShellId(srcNodeId.Index)); + break; + case Kind::Solid: + ensureSolid(ctx, BRepGraph_SolidId(srcNodeId.Index)); + break; + case Kind::Compound: + ensureCompound(ctx, BRepGraph_CompoundId(srcNodeId.Index)); + break; + case Kind::CompSolid: + ensureCompSolid(ctx, BRepGraph_CompSolidId(srcNodeId.Index)); + break; + case Kind::Product: + ensureProduct(ctx, BRepGraph_ProductId(srcNodeId.Index)); + break; + case Kind::Occurrence: + ensureOccurrence(ctx, BRepGraph_OccurrenceId(srcNodeId.Index)); + break; + default: + break; + } +} + +//================================================================================================= + +BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().Compounds())) + return BRepGraph_CompoundId(); + const BRepGraph_CompoundId* anExisting = ctx.Compounds.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + // Pre-allocate an empty compound and bind it BEFORE recursing into children so a + // self-referencing compound chain (A->B->A or A->A) terminates instead of recursing + // infinitely. Children are appended one at a time after recursion resolves them. + const NCollection_DynamicArray aEmpty; + const BRepGraph_CompoundId aNewId = ctx.Result.Editor().Compounds().Add(aEmpty); + ctx.Compounds.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + + for (BRepGraph_RefsChildOfCompound aCRIt(ctx.Source, srcId); aCRIt.More(); aCRIt.Next()) + { + const BRepGraphInc::ChildRef& aChildRef = ctx.Source.Refs().Children().Entry(aCRIt.CurrentId()); + const BRepGraph_NodeId aSrcChild = aChildRef.ChildDefId; + ensureNode(ctx, aSrcChild); + const BRepGraph_NodeId aMapped = mappedNode(ctx, aSrcChild); + if (aMapped.IsValid()) + { + (void)ctx.Result.Editor().Compounds().AddChild(aNewId, aMapped, aChildRef.Orientation); + } + } + return aNewId; +} + +//================================================================================================= + +BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& ctx, BRepGraph_CompSolidId srcId) +{ + if (!srcId.IsValidIn(ctx.Source.Topo().CompSolids())) + return BRepGraph_CompSolidId(); + const BRepGraph_CompSolidId* anExisting = ctx.CompSolids.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + NCollection_DynamicArray aSolidIds; + for (BRepGraph_RefsSolidOfCompSolid aSRIt(ctx.Source, srcId); aSRIt.More(); aSRIt.Next()) + { + const BRepGraph_SolidId aSrcSolid = + ctx.Source.Refs().Solids().Entry(aSRIt.CurrentId()).SolidDefId; + aSolidIds.Append(ensureSolid(ctx, aSrcSolid)); + } + + const BRepGraph_CompSolidId aNewId = ctx.Result.Editor().CompSolids().Add(aSolidIds); + ctx.CompSolids.Bind(srcId, aNewId); + ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + return aNewId; +} + +//================================================================================================= + +BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& ctx, + BRepGraph_OccurrenceRefId srcRefId) +{ + const BRepGraph_OccurrenceRefId* anExisting = ctx.OccurrenceRefs.Seek(srcRefId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::OccurrenceRef& aSrcRef = ctx.SrcStorage->OccurrenceRef(srcRefId); + const BRepGraph_OccurrenceRefId aNewId = ctx.DstStorage->AppendOccurrenceRef(); + ctx.OccurrenceRefs.Bind(srcRefId, aNewId); + + BRepGraphInc::OccurrenceRef& aNewRef = ctx.DstStorage->ChangeOccurrenceRef(aNewId); + aNewRef.IsRemoved = aSrcRef.IsRemoved; + aNewRef.LocalLocation = aSrcRef.LocalLocation; + if (aSrcRef.OccurrenceDefId.IsValid()) + aNewRef.OccurrenceDefId = ensureOccurrence(ctx, aSrcRef.OccurrenceDefId); + // ParentId is set by the caller (ensureProduct) after the ref is created. + return aNewId; +} + +//================================================================================================= + +BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& ctx, BRepGraph_OccurrenceId srcId) +{ + const BRepGraph_OccurrenceId* anExisting = ctx.Occurrences.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + const BRepGraphInc::OccurrenceDef& aSrcOcc = ctx.SrcStorage->Occurrence(srcId); + const BRepGraph_OccurrenceId aNewId = ctx.DstStorage->AppendOccurrence(); + ctx.Occurrences.Bind(srcId, aNewId); + + BRepGraphInc::OccurrenceDef& aNewOcc = ctx.DstStorage->ChangeOccurrence(aNewId); + aNewOcc.IsRemoved = aSrcOcc.IsRemoved; + if (aSrcOcc.ChildDefId.IsValid()) + { + ensureNode(ctx, aSrcOcc.ChildDefId); + aNewOcc.ChildDefId = mappedNode(ctx, aSrcOcc.ChildDefId); + } + return aNewId; +} + +//================================================================================================= + +BRepGraph_ProductId ensureProduct(GraphCopyContext& ctx, BRepGraph_ProductId srcId) +{ + const BRepGraph_ProductId* anExisting = ctx.Products.Seek(srcId); + if (anExisting != nullptr) + return *anExisting; + + // Bind before iterating refs so re-entrant calls (assembly cycles) short-circuit. + const BRepGraph_ProductId aNewId = ctx.DstStorage->AppendProduct(); + ctx.Products.Bind(srcId, aNewId); + + const BRepGraphInc::ProductDef& aSrcProd = ctx.SrcStorage->Product(srcId); + for (const BRepGraph_OccurrenceRefId& aSrcRefId : aSrcProd.OccurrenceRefIds) + { + const BRepGraph_OccurrenceRefId aNewRefId = ensureOccurrenceRef(ctx, aSrcRefId); + ctx.DstStorage->ChangeProduct(aNewId).OccurrenceRefIds.Append(aNewRefId); + // Set ParentId: the owning product is the parent of this occurrence reference. + ctx.DstStorage->ChangeOccurrenceRef(aNewRefId).ParentId = BRepGraph_NodeId(aNewId); + } + return aNewId; +} + } // namespace //================================================================================================= @@ -114,6 +660,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG return aResult; const BRepGraph::RefsView& aRefs = theGraph.Refs(); + DeferredCacheTransfers aDeferred; // Bottom-up graph rebuild via EditorView. // Since this is a full copy, old index == new index (identity mapping). @@ -124,7 +671,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(aVertexId); (void)aResult.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); - transferFreshCacheValues(theGraph, aVertexId, aResult, aVertexId); + aDeferred.DeferNode(theGraph, aVertexId, aVertexId); } // Edges. @@ -149,11 +696,11 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG { BRepGraph_MutGuard aNewEdge = aResult.Editor().Edges().Mut(anEdgeId); - aNewEdge->IsDegenerate = anEdge.IsDegenerate; - aNewEdge->SameParameter = anEdge.SameParameter; - aNewEdge->SameRange = anEdge.SameRange; + aResult.Editor().Edges().SetDegenerate(aNewEdge, anEdge.IsDegenerate); + aResult.Editor().Edges().SetSameParameter(aNewEdge, anEdge.SameParameter); + aResult.Editor().Edges().SetSameRange(aNewEdge, anEdge.SameRange); } - transferFreshCacheValues(theGraph, anEdgeId, aResult, anEdgeId); + aDeferred.DeferNode(theGraph, anEdgeId, anEdgeId); } // Wires. @@ -168,7 +715,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aWireEdges.Append(std::make_pair(aCoEdge.EdgeDefId, aCoEdge.Orientation)); } (void)aResult.Editor().Wires().Add(aWireEdges); - transferFreshCacheValues(theGraph, aWireId, aResult, aWireId); + aDeferred.DeferNode(theGraph, aWireId, aWireId); } // Faces. @@ -202,8 +749,8 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG { BRepGraph_MutGuard aNewFace = aResult.Editor().Faces().Mut(aFaceId); - aNewFace->NaturalRestriction = aFace.NaturalRestriction; - aNewFace->TriangulationRepId = aFace.TriangulationRepId; + aResult.Editor().Faces().SetNaturalRestriction(aNewFace, aFace.NaturalRestriction); + aResult.Editor().Faces().SetTriangulationRep(aNewFace, aFace.TriangulationRepId); } // Copy cached mesh data if present. const BRepGraph_MeshCache::FaceMeshEntry* aCachedFace = @@ -213,7 +760,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG BRepGraph_MeshCache::FaceMeshEntry& aNewEntry = aResult.meshCache().ChangeFaceMesh(aFaceId); aNewEntry = *aCachedFace; } - transferFreshCacheValues(theGraph, aFaceId, aResult, aFaceId); + aDeferred.DeferNode(theGraph, aFaceId, aFaceId); } // PCurves via CoEdge data (after edges and faces are created). @@ -245,14 +792,14 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG { const BRepGraph_ShellId aShellId = aShellIt.CurrentId(); BRepGraph_ShellId aNewShellId = aResult.Editor().Shells().Add(); - transferFreshCacheValues(theGraph, aShellId, aResult, aShellId); + aDeferred.DeferNode(theGraph, aShellId, aShellId); for (BRepGraph_RefsFaceOfShell aFRIt(theGraph, aShellId); aFRIt.More(); aFRIt.Next()) { const BRepGraphInc::FaceRef& aFR = aRefs.Faces().Entry(aFRIt.CurrentId()); const BRepGraph_FaceRefId aNewFaceRefId = aResult.Editor().Shells().AddFace(aNewShellId, aFR.FaceDefId, aFR.Orientation); - transferFreshCacheValues(theGraph, aFRIt.CurrentId(), aResult, aNewFaceRefId); + aDeferred.DeferRef(theGraph, aFRIt.CurrentId(), aNewFaceRefId); } } @@ -261,14 +808,14 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG { const BRepGraph_SolidId aSolidId = aSolidIt.CurrentId(); BRepGraph_SolidId aNewSolidId = aResult.Editor().Solids().Add(); - transferFreshCacheValues(theGraph, aSolidId, aResult, aSolidId); + aDeferred.DeferNode(theGraph, aSolidId, aSolidId); for (BRepGraph_RefsShellOfSolid aSRIt(theGraph, aSolidId); aSRIt.More(); aSRIt.Next()) { const BRepGraphInc::ShellRef& aSR = aRefs.Shells().Entry(aSRIt.CurrentId()); const BRepGraph_ShellRefId aNewShellRefId = aResult.Editor().Solids().AddShell(aNewSolidId, aSR.ShellDefId, aSR.Orientation); - transferFreshCacheValues(theGraph, aSRIt.CurrentId(), aResult, aNewShellRefId); + aDeferred.DeferRef(theGraph, aSRIt.CurrentId(), aNewShellRefId); } } @@ -282,7 +829,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aChildNodeIds.Append(aRefs.Children().Entry(aCRIt.CurrentId()).ChildDefId); } (void)aResult.Editor().Compounds().Add(aChildNodeIds); - transferFreshCacheValues(theGraph, aCompoundId, aResult, aCompoundId); + aDeferred.DeferNode(theGraph, aCompoundId, aCompoundId); } // CompSolids. @@ -296,7 +843,7 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG aSolidNodeIds.Append(aRefs.Solids().Entry(aSRIt.CurrentId()).SolidDefId); } (void)aResult.Editor().CompSolids().Add(aSolidNodeIds); - transferFreshCacheValues(theGraph, aCompSolidId, aResult, aCompSolidId); + aDeferred.DeferNode(theGraph, aCompSolidId, aCompSolidId); } // Products. @@ -402,216 +949,78 @@ BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyG // Pre-allocate transient cache for lock-free parallel access on the copied graph. reserveTransientCache(aResult); + // Drain deferred cache transfers AFTER all mutations: Set captures the final SubtreeGen, + // so subsequent Get calls match. + aDeferred.Drain(theGraph, aResult); + return aResult; } //================================================================================================= -BRepGraph BRepGraph_Copy::CopyFace(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace, +BRepGraph BRepGraph_Copy::CopyNode(const BRepGraph& theGraph, + const BRepGraph_NodeId theNodeId, const bool theCopyGeom, + const bool theCopyMesh, const bool theReserveCache) { - BRepGraph aResult; - if (!theGraph.IsDone() || !theFace.IsValidIn(theGraph.Topo().Faces())) - return aResult; + if (!theGraph.IsDone()) + return BRepGraph(); - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraphInc::FaceDef& aFaceDef = theGraph.Topo().Faces().Definition(theFace); + GraphCopyContext ctx(theGraph, theCopyGeom, theCopyMesh, theReserveCache); + ctx.SrcStorage = &theGraph.incStorage(); + ctx.DstStorage = &ctx.Result.incStorage(); + ctx.DstMesh = &ctx.Result.meshCache(); + ctx.DstData = ctx.Result.data(); - // Check that the face has at least one active wire via typed iterator. - BRepGraph_RefsWireOfFace aHasWiresIt(theGraph, theFace); - if (!aHasWiresIt.More()) - return aResult; + ensureNode(ctx, theNodeId); - // Use NCollection_IndexedMap to collect old indices in deterministic insertion order. - NCollection_IndexedMap aVertexSet; - NCollection_IndexedMap anEdgeSet; - NCollection_IndexedMap aWireSet; - - // Collect all edges/vertices/wires from the face's wire refs. - for (BRepGraph_RefsWireOfFace aWireIt(theGraph, theFace); aWireIt.More(); aWireIt.Next()) + // Post-pass: remap OccurrenceRef.ParentId for any assembly entities that were copied. + // ensureProduct sets ParentId inline, but this guard handles other edge cases. + if (!ctx.OccurrenceRefs.IsEmpty()) { - const BRepGraphInc::WireRef& aWR = aRefs.Wires().Entry(aWireIt.CurrentId()); - aWireSet.Add(aWR.WireDefId); - - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, aWR.WireDefId); aCEIt.More(); aCEIt.Next()) + for (BRepGraph_FullOccurrenceRefIterator aRefIt(theGraph); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aRefs.CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); - anEdgeSet.Add(aCoEdge.EdgeDefId); - - const BRepGraph_EdgeId anEdgeId = aCoEdge.EdgeDefId; - const BRepGraph_VertexId aStartVtx = BRepGraph_Tool::Edge::StartVertexId(theGraph, anEdgeId); - if (aStartVtx.IsValid(theGraph.Topo().Vertices().Nb())) - aVertexSet.Add(aStartVtx); - const BRepGraph_VertexId anEndVtx = BRepGraph_Tool::Edge::EndVertexId(theGraph, anEdgeId); - if (anEndVtx.IsValid(theGraph.Topo().Vertices().Nb())) - aVertexSet.Add(anEndVtx); - } - } - - // Build old->new index maps from the ordered sets. - NCollection_DataMap aVertexMap(aVertexSet.Extent()); - for (int anIdx = 1; anIdx <= aVertexSet.Extent(); ++anIdx) - aVertexMap.Bind(aVertexSet.FindKey(anIdx), BRepGraph_VertexId(anIdx - 1)); - - NCollection_DataMap anEdgeMap(anEdgeSet.Extent()); - for (int anIdx = 1; anIdx <= anEdgeSet.Extent(); ++anIdx) - anEdgeMap.Bind(anEdgeSet.FindKey(anIdx), BRepGraph_EdgeId(anIdx - 1)); - - NCollection_DataMap aWireMap(aWireSet.Extent()); - for (int anIdx = 1; anIdx <= aWireSet.Extent(); ++anIdx) - aWireMap.Bind(aWireSet.FindKey(anIdx), BRepGraph_WireId(anIdx - 1)); - - // Add vertices in deterministic order. - for (int anIdx = 1; anIdx <= aVertexSet.Extent(); ++anIdx) - { - const BRepGraph_VertexId anOldVtxId = aVertexSet.FindKey(anIdx); - const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(anOldVtxId); - const BRepGraph_VertexId aNewVtxId(anIdx - 1); - (void)aResult.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); - transferFreshCacheValues(theGraph, anOldVtxId, aResult, aNewVtxId); - } - - // Add edges in deterministic order. - for (int anIdx = 1; anIdx <= anEdgeSet.Extent(); ++anIdx) - { - const BRepGraph_EdgeId anOldEdgeId = anEdgeSet.FindKey(anIdx); - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(anOldEdgeId); - - BRepGraph_VertexId aNewStart, aNewEnd; - const BRepGraph_VertexId aStartVtx = BRepGraph_Tool::Edge::StartVertexId(theGraph, anOldEdgeId); - if (aStartVtx.IsValid()) - { - const BRepGraph_VertexId* aNewVtxId = aVertexMap.Seek(aStartVtx); - if (aNewVtxId != nullptr) - aNewStart = *aNewVtxId; - } - const BRepGraph_VertexId anEndVtx = BRepGraph_Tool::Edge::EndVertexId(theGraph, anOldEdgeId); - if (anEndVtx.IsValid()) - { - const BRepGraph_VertexId* aNewVtxId = aVertexMap.Seek(anEndVtx); - if (aNewVtxId != nullptr) - aNewEnd = *aNewVtxId; - } - - const occ::handle& anEdgeSrcCurve = - BRepGraph_Tool::Edge::Curve(theGraph, anOldEdgeId); - occ::handle aCurve = copyCurve(anEdgeSrcCurve, theCopyGeom); - - const BRepGraph_EdgeId aNewEdgeId(anIdx - 1); - (void)aResult.Editor().Edges().Add(aNewStart, - aNewEnd, - aCurve, - anEdge.ParamFirst, - anEdge.ParamLast, - anEdge.Tolerance); - - { - BRepGraph_MutGuard aNewEdge = aResult.Editor().Edges().Mut(aNewEdgeId); - aNewEdge->IsDegenerate = anEdge.IsDegenerate; - aNewEdge->SameParameter = anEdge.SameParameter; - aNewEdge->SameRange = anEdge.SameRange; - } - transferFreshCacheValues(theGraph, anOldEdgeId, aResult, aNewEdgeId); - } - - // Add wires in deterministic order. - for (int anIdx = 1; anIdx <= aWireSet.Extent(); ++anIdx) - { - const BRepGraph_WireId anOldWireId = aWireSet.FindKey(anIdx); - NCollection_DynamicArray> aNewEntries; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, anOldWireId); aCEIt.More(); aCEIt.Next()) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aRefs.CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); - const BRepGraph_EdgeId* aNewEdgeId = anEdgeMap.Seek(aCoEdge.EdgeDefId); - if (aNewEdgeId == nullptr) + const BRepGraph_OccurrenceRefId aSrcRefId = aRefIt.CurrentId(); + const BRepGraph_OccurrenceRefId* aDstRefPtr = ctx.OccurrenceRefs.Seek(aSrcRefId); + if (aDstRefPtr == nullptr) continue; - aNewEntries.Append(std::make_pair(*aNewEdgeId, aCoEdge.Orientation)); - } - (void)aResult.Editor().Wires().Add(aNewEntries); - transferFreshCacheValues(theGraph, anOldWireId, aResult, BRepGraph_WireId(anIdx - 1)); - } - - // Add the face. - const occ::handle& aFaceSrcSurf = BRepGraph_Tool::Face::Surface(theGraph, theFace); - occ::handle aSurf = copySurface(aFaceSrcSurf, theCopyGeom); - - BRepGraph_WireId anOuterWire; - NCollection_DynamicArray anInnerWires; - - for (BRepGraph_RefsWireOfFace aWRIt(theGraph, theFace); aWRIt.More(); aWRIt.Next()) - { - const BRepGraphInc::WireRef& aWR = aRefs.Wires().Entry(aWRIt.CurrentId()); - const BRepGraph_WireId* aNewWireId = aWireMap.Seek(aWR.WireDefId); - if (aNewWireId == nullptr) - continue; - if (aWR.IsOuter) - { - anOuterWire = *aNewWireId; - } - else - { - anInnerWires.Append(*aNewWireId); - } - } - - (void)aResult.Editor().Faces().Add(aSurf, anOuterWire, anInnerWires, aFaceDef.Tolerance); - { - BRepGraph_MutGuard aNewFace = - aResult.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aNewFace->NaturalRestriction = aFaceDef.NaturalRestriction; - aNewFace->TriangulationRepId = aFaceDef.TriangulationRepId; - } - // Copy cached mesh data if present. - const BRepGraph_MeshCache::FaceMeshEntry* aCachedFace = - theGraph.Mesh().Faces().CachedMesh(theFace); - if (aCachedFace != nullptr) - { - BRepGraph_MeshCache::FaceMeshEntry& aNewEntry = - aResult.meshCache().ChangeFaceMesh(BRepGraph_FaceId::Start()); - aNewEntry = *aCachedFace; - } - transferFreshCacheValues(theGraph, theFace, aResult, BRepGraph_FaceId::Start()); - - // PCurves for edges in this face via CoEdge data. - for (int anIdx = 1; anIdx <= anEdgeSet.Extent(); ++anIdx) - { - const BRepGraph_EdgeId anOldEdgeId = anEdgeSet.FindKey(anIdx); - const BRepGraph_EdgeId aNewEdgeId(anIdx - 1); - const NCollection_DynamicArray& aCoEdgeIds = - theGraph.Topo().Edges().CoEdges(anOldEdgeId); - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIds) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); - // Only copy CoEdges belonging to this face. - if (aCoEdge.FaceDefId != theFace) + const BRepGraphInc::OccurrenceRef& aSrcRef = ctx.SrcStorage->OccurrenceRef(aSrcRefId); + if (!aSrcRef.ParentId.IsValid()) continue; - if (!aCoEdge.Curve2DRepId.IsValid()) - continue; - - const occ::handle& aCoEdgeSrcPC = - BRepGraph_Tool::CoEdge::PCurve(theGraph, aCoEdgeId); - occ::handle aNewPC = copyPCurve(aCoEdgeSrcPC, theCopyGeom); - aResult.Editor().CoEdges().AddPCurve(aNewEdgeId, - BRepGraph_FaceId::Start(), - aNewPC, - aCoEdge.ParamFirst, - aCoEdge.ParamLast, - aCoEdge.Orientation); + const BRepGraph_NodeId aMapped = mappedNode(ctx, aSrcRef.ParentId); + if (aMapped.IsValid()) + ctx.DstStorage->ChangeOccurrenceRef(*aDstRefPtr).ParentId = aMapped; } } - aResult.data()->myIsDone = true; - - // Pre-allocate transient cache for lock-free parallel access on the copied graph. - // Skip for short-lived temporary graphs where cache is never queried. - if (theReserveCache) + // Build root product list when product entities were copied. + if (!ctx.Products.IsEmpty()) { - reserveTransientCache(aResult); + NCollection_Map aReferencedProducts; + for (BRepGraph_FullOccurrenceIterator anOccIt(ctx.Result); anOccIt.More(); anOccIt.Next()) + { + const BRepGraph_OccurrenceId anOccId = anOccIt.CurrentId(); + const BRepGraphInc::OccurrenceDef& anOcc = ctx.DstStorage->Occurrence(anOccId); + if (!anOcc.IsRemoved && anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + { + const BRepGraph_ProductId aChildProdId = BRepGraph_ProductId::FromNodeId(anOcc.ChildDefId); + if (aChildProdId.IsValidIn(ctx.Result.Topo().Products())) + aReferencedProducts.Add(aChildProdId); + } + } + for (BRepGraph_FullProductIterator aProdIt(ctx.Result); aProdIt.More(); aProdIt.Next()) + { + const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); + const BRepGraphInc::ProductDef& aProd = ctx.DstStorage->Product(aProdId); + if (!aProd.IsRemoved && !aReferencedProducts.Contains(aProdId)) + ctx.DstData->myRootProductIds.Append(aProdId); + } } - return aResult; + ctx.DstData->myIsDone = true; + if (ctx.ReserveCache) + reserveTransientCache(ctx.Result); + ctx.Deferred.Drain(theGraph, ctx.Result); + return std::move(ctx.Result); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx index 27173afbaf..edf8d5e2de 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx @@ -54,21 +54,24 @@ public: [[nodiscard]] Standard_EXPORT static BRepGraph Perform(const BRepGraph& theGraph, const bool theCopyGeom = true); - //! Copy a single face sub-graph. + //! Copy a single node sub-graph of any kind (Face, Shell, Solid, Wire, Edge, Vertex, etc.). + //! The new graph contains only the specified node and all entities it references. //! @param[in] theGraph a pre-built BRepGraph - //! @param[in] theFace face definition identifier in the graph - //! @param[in] theCopyGeom if true, geometry is deep-copied - //! @param[in] theReserveCache if true (default), pre-allocates transient cache; - //! pass false for short-lived temporary graphs - //! @return a new BRepGraph containing only the specified face and its dependencies - [[nodiscard]] Standard_EXPORT static BRepGraph CopyFace(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace, + //! @param[in] theNodeId node identifier (any kind) + //! @param[in] theCopyGeom if true, geometry handles are deep-copied + //! @param[in] theCopyMesh if true, cached mesh entries are propagated to the result; + //! if false, mesh references are dropped on copied faces + //! @param[in] theReserveCache if true, pre-allocates transient cache + //! @return a new BRepGraph containing only the specified sub-graph + [[nodiscard]] Standard_EXPORT static BRepGraph CopyNode(const BRepGraph& theGraph, + const BRepGraph_NodeId theNodeId, const bool theCopyGeom = true, - const bool theReserveCache = true); + const bool theCopyMesh = true, + const bool theReserveCache = false); private: //! Pre-allocate transient cache for lock-free parallel access. - Standard_EXPORT static void reserveTransientCache(BRepGraph& theGraph); + static void reserveTransientCache(BRepGraph& theGraph); BRepGraph_Copy() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx index c4105fb897..162a448441 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx @@ -82,6 +82,11 @@ struct BRepGraph_Data //! parents in the same propagation wave (O(1) re-visit guard). std::atomic myPropagationWave{0}; + //! Recursion depth of EditorView::GenOps::RemoveSubgraph. Outermost call (depth==0) + //! triggers a single reverse-index rebuild after cascade so individual cascade-prune + //! steps avoid maintaining per-kind unbinds for every removed node. + uint32_t myRemoveSubgraphDepth = 0; + //! NodeIds accumulated during deferred mode. Processed by EndDeferredInvalidation(). NCollection_DynamicArray myDeferredModified; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx index 56e31672ff..c6b9e6d8ce 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx @@ -157,7 +157,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG continue; BRepGraph_MutGuard aFaceDef = theGraph.Editor().Faces().Mut(aFaceId); - aFaceDef->SurfaceRepId = aCanonSurfRepId; + theGraph.Editor().Faces().SetSurfaceRepId(aFaceDef, aCanonSurfRepId); ++aResult.NbSurfaceRewrites; aResult.AffectedFaces.Append(aFaceId); @@ -186,7 +186,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG continue; BRepGraph_MutGuard anEdgeDef = theGraph.Editor().Edges().Mut(anEdgeId); - anEdgeDef->Curve3DRepId = aCanonCurveRepId; + theGraph.Editor().Edges().SetCurve3DRepId(anEdgeDef, aCanonCurveRepId); ++aResult.NbCurveRewrites; aResult.AffectedEdges.Append(anEdgeId); @@ -295,21 +295,27 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG BRepGraph_MutGuard aStartRef = theGraph.Editor().Vertices().MutRef(anEdge->StartVertexRefId); if (aStartRef->VertexDefId == anOldVertexId) - aStartRef->VertexDefId = aCanonVertexId; + { + theGraph.Editor().Vertices().SetRefVertexDefId(aStartRef, aCanonVertexId); + } } if (anEdge->EndVertexRefId.IsValid()) { BRepGraph_MutGuard anEndRef = theGraph.Editor().Vertices().MutRef(anEdge->EndVertexRefId); if (anEndRef->VertexDefId == anOldVertexId) - anEndRef->VertexDefId = aCanonVertexId; + { + theGraph.Editor().Vertices().SetRefVertexDefId(anEndRef, aCanonVertexId); + } } for (const BRepGraph_VertexRefId& anInternalRefId : anEdge->InternalVertexRefIds) { BRepGraph_MutGuard anInternalRef = theGraph.Editor().Vertices().MutRef(anInternalRefId); if (anInternalRef->VertexDefId == anOldVertexId) - anInternalRef->VertexDefId = aCanonVertexId; + { + theGraph.Editor().Vertices().SetRefVertexDefId(anInternalRef, aCanonVertexId); + } } } @@ -325,7 +331,9 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG BRepGraph_MutGuard aVRef = theGraph.Editor().Vertices().MutRef(aVRefId); if (aVRef->VertexDefId == anOldVertexId) - aVRef->VertexDefId = aCanonVertexId; + { + theGraph.Editor().Vertices().SetRefVertexDefId(aVRef, aCanonVertexId); + } } } @@ -610,7 +618,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aMutWireRef = theGraph.Editor().Wires().MutRef(aWireRefId); - aMutWireRef->WireDefId = aCanonWireId; + theGraph.Editor().Wires().SetRefWireDefId(aMutWireRef, aCanonWireId); } } } @@ -630,7 +638,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aMutCR = theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); - aMutCR->ChildDefId = aCanonId; + theGraph.Editor().Gen().SetChildRefChildDefId(aMutCR, aCanonId); } } } @@ -647,7 +655,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aMutCR = theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); - aMutCR->ChildDefId = aCanonId; + theGraph.Editor().Gen().SetChildRefChildDefId(aMutCR, aCanonId); } } } @@ -775,7 +783,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aMutFaceRef = theGraph.Editor().Faces().MutRef(aFaceRefId); - aMutFaceRef->FaceDefId = aCanonFaceId; + theGraph.Editor().Faces().SetRefFaceDefId(aMutFaceRef, aCanonFaceId); } } @@ -791,7 +799,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aMutCE = theGraph.Editor().CoEdges().Mut(aCEId); - aMutCE->FaceDefId = aCanonFaceId; + theGraph.Editor().CoEdges().SetFaceDefId(aMutCE, aCanonFaceId); } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx index 40b709a0f7..3fbe58b2e5 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx @@ -352,6 +352,34 @@ static void rebindCoEdgesForEdgeReplacement(BRepGraphInc_Storage& theStorage, //================================================================================================= +//! True if no active CoEdgeRef in theWire (other than theExcludingRef) resolves to +//! a non-removed CoEdge that references theEdge. Used to gate Edge->Wire unbind +//! when a single CoEdgeRef is removed: deduped reverse entry must drop only when +//! the last sibling pointing at the same edge is gone. +static bool isLastEdgeUsageInWire(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWire, + const BRepGraph_EdgeId theEdge, + const BRepGraph_CoEdgeRefId theExcludingRef) +{ + if (!theWire.IsValid(theStorage.NbWires()) || !theEdge.IsValid()) + return true; + const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); + for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) + { + if (!aRefId.IsValid(theStorage.NbCoEdgeRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); + if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) + continue; + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); + if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) + return false; + } + return true; +} + +//================================================================================================= + static void unbindCoEdgesOfRemovedEdge(BRepGraphInc_Storage& theStorage, const BRepGraph_EdgeId theEdgeId) { @@ -546,6 +574,7 @@ BRepGraph_EdgeId BRepGraph::EditorView::EdgeOps::Add(const BRepGraph_VertexId aStartVtxRef.Orientation = TopAbs_FORWARD; myGraph->allocateRefUID(aStartVtxRefId); anEdgeDef.StartVertexRefId = aStartVtxRefId; + aStorage.ChangeReverseIndex().BindVertexToEdge(theStartVtx, aEdgeId); } if (theEndVtx.IsValid()) { @@ -557,6 +586,8 @@ BRepGraph_EdgeId BRepGraph::EditorView::EdgeOps::Add(const BRepGraph_VertexId anEndVtxRef.Orientation = TopAbs_REVERSED; myGraph->allocateRefUID(anEndVtxRefId); anEdgeDef.EndVertexRefId = anEndVtxRefId; + if (theEndVtx != theStartVtx) // BindVertexToEdge dedups, but skip needless work + aStorage.ChangeReverseIndex().BindVertexToEdge(theEndVtx, aEdgeId); } anEdgeDef.ParamFirst = theFirst; anEdgeDef.ParamLast = theLast; @@ -670,7 +701,7 @@ BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::AddInternalVertex( myGraph->allocateRefUID(aVertexRefId); aStorage.ChangeEdge(theEdgeEntity).InternalVertexRefIds.Append(aVertexRefId); - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().BindVertexToEdge(theVertexEntity, theEdgeEntity); myGraph->markModified(theEdgeEntity); return aVertexRefId; } @@ -718,6 +749,7 @@ BRepGraph_FaceId BRepGraph::EditorView::FaceOps::Add( anOuterWireRef.IsOuter = true; myGraph->allocateRefUID(anOuterWireRefId); aStorage.ChangeFace(aFaceId).WireRefIds.Append(anOuterWireRefId); + aStorage.ChangeReverseIndex().BindWireToFace(theOuterWire, aFaceId); } for (const BRepGraph_WireId& aWireDefId : theInnerWires) @@ -734,6 +766,7 @@ BRepGraph_FaceId BRepGraph::EditorView::FaceOps::Add( aWireRef.IsOuter = false; myGraph->allocateRefUID(aWireRefId); aStorage.ChangeFace(aFaceId).WireRefIds.Append(aWireRefId); + aStorage.ChangeReverseIndex().BindWireToFace(aWireDefId, aFaceId); } return aFaceId; @@ -761,7 +794,6 @@ BRepGraph_VertexRefId BRepGraph::EditorView::FaceOps::AddVertex( myGraph->allocateRefUID(aVertexRefId); aStorage.ChangeFace(theFaceEntity).VertexRefIds.Append(aVertexRefId); - aStorage.BuildReverseIndex(); myGraph->markModified(theFaceEntity); return aVertexRefId; } @@ -812,6 +844,7 @@ BRepGraph_FaceRefId BRepGraph::EditorView::ShellOps::AddFace(const BRepGraph_She myGraph->allocateRefUID(aFaceRefId); aStorage.ChangeShell(theShellEntity).FaceRefIds.Append(aFaceRefId); + aStorage.ChangeReverseIndex().BindFaceToShell(theFaceEntity, theShellEntity); myGraph->markModified(theShellEntity); return aFaceRefId; } @@ -843,7 +876,6 @@ BRepGraph_ChildRefId BRepGraph::EditorView::ShellOps::AddChild( myGraph->allocateRefUID(aChildRefId); aStorage.ChangeShell(theShellEntity).AuxChildRefIds.Append(aChildRefId); - aStorage.BuildReverseIndex(); myGraph->markModified(theShellEntity); return aChildRefId; } @@ -871,6 +903,7 @@ BRepGraph_ShellRefId BRepGraph::EditorView::SolidOps::AddShell( myGraph->allocateRefUID(aShellRefId); aStorage.ChangeSolid(theSolidEntity).ShellRefIds.Append(aShellRefId); + aStorage.ChangeReverseIndex().BindShellToSolid(theShellEntity, theSolidEntity); myGraph->markModified(theSolidEntity); return aShellRefId; } @@ -902,7 +935,6 @@ BRepGraph_ChildRefId BRepGraph::EditorView::SolidOps::AddChild( myGraph->allocateRefUID(aChildRefId); aStorage.ChangeSolid(theSolidEntity).AuxChildRefIds.Append(aChildRefId); - aStorage.BuildReverseIndex(); myGraph->markModified(theSolidEntity); return aChildRefId; } @@ -935,6 +967,7 @@ BRepGraph_CompoundId BRepGraph::EditorView::CompoundOps::Add( aCREntry.ChildDefId = aChild; myGraph->allocateRefUID(aChildRefId); aCompDef.ChildRefIds.Append(aChildRefId); + aStorage.ChangeReverseIndex().BindCompoundChild(aChild, aCompoundId); } return aCompoundId; @@ -953,7 +986,6 @@ BRepGraph_ChildRefId BRepGraph::EditorView::CompoundOps::AddChild( return BRepGraph_ChildRefId(); } - const uint32_t anOldNbChildRefs = static_cast(aStorage.NbChildRefs()); const BRepGraph_ChildRefId aChildRefId(aStorage.NbChildRefs()); aStorage.AppendChildRef(); BRepGraphInc::ChildRef& aChildRef = aStorage.ChangeChildRef(aChildRefId); @@ -963,15 +995,7 @@ BRepGraph_ChildRefId BRepGraph::EditorView::CompoundOps::AddChild( myGraph->allocateRefUID(aChildRefId); aStorage.ChangeCompound(theCompoundEntity).ChildRefIds.Append(aChildRefId); - aStorage.BuildDeltaReverseIndex(static_cast(aStorage.NbEdges()), - static_cast(aStorage.NbWires()), - static_cast(aStorage.NbFaces()), - static_cast(aStorage.NbShells()), - static_cast(aStorage.NbSolids()), - static_cast(aStorage.NbCompounds()), - static_cast(aStorage.NbCompSolids()), - anOldNbChildRefs, - static_cast(aStorage.NbSolidRefs())); + aStorage.ChangeReverseIndex().BindCompoundChild(theChildEntity, theCompoundEntity); myGraph->markModified(theCompoundEntity); return aChildRefId; } @@ -1004,6 +1028,7 @@ BRepGraph_CompSolidId BRepGraph::EditorView::CompSolidOps::Add( aSREntry.SolidDefId = aSolidId; myGraph->allocateRefUID(aSolidRefId); aCSolDef.SolidRefIds.Append(aSolidRefId); + aStorage.ChangeReverseIndex().BindSolidToCompSolid(aSolidId, aCompSolidId); } return aCompSolidId; @@ -1022,7 +1047,6 @@ BRepGraph_SolidRefId BRepGraph::EditorView::CompSolidOps::AddSolid( return BRepGraph_SolidRefId(); } - const uint32_t anOldNbSolidRefs = static_cast(aStorage.NbSolidRefs()); const BRepGraph_SolidRefId aSolidRefId(aStorage.NbSolidRefs()); aStorage.AppendSolidRef(); BRepGraphInc::SolidRef& aSREntry = aStorage.ChangeSolidRef(aSolidRefId); @@ -1032,15 +1056,7 @@ BRepGraph_SolidRefId BRepGraph::EditorView::CompSolidOps::AddSolid( myGraph->allocateRefUID(aSolidRefId); aStorage.ChangeCompSolid(theCompSolidEntity).SolidRefIds.Append(aSolidRefId); - aStorage.BuildDeltaReverseIndex(static_cast(aStorage.NbEdges()), - static_cast(aStorage.NbWires()), - static_cast(aStorage.NbFaces()), - static_cast(aStorage.NbShells()), - static_cast(aStorage.NbSolids()), - static_cast(aStorage.NbCompounds()), - static_cast(aStorage.NbCompSolids()), - static_cast(aStorage.NbChildRefs()), - anOldNbSolidRefs); + aStorage.ChangeReverseIndex().BindSolidToCompSolid(theSolidEntity, theCompSolidEntity); myGraph->markModified(theCompSolidEntity); return aSolidRefId; } @@ -1081,7 +1097,8 @@ BRepGraph_ProductId BRepGraph::EditorView::ProductOps::LinkProductToTopology( myGraph->allocateRefUID(anOccRefId); aProductDef.OccurrenceRefIds.Append(anOccRefId); - aStorage.BuildReverseIndex(); + // No reverse-index bind: the occurrence's child is a topology root, not a product; + // myProductToOccurrences only indexes occurrences whose ChildDefId is a Product. myGraph->myData->myRootProductIds.Append(aProductId); return aProductId; @@ -1103,36 +1120,23 @@ BRepGraph_ProductId BRepGraph::EditorView::ProductOps::CreateEmptyProduct() //================================================================================================= -BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( - const BRepGraph_ProductId theParentProduct, - const BRepGraph_ProductId theReferencedProduct, - const TopLoc_Location& thePlacement) -{ - return LinkProducts(theParentProduct, - theReferencedProduct, - thePlacement, - BRepGraph_OccurrenceId()); -} - -//================================================================================================= - BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( const BRepGraph_ProductId theParentProduct, const BRepGraph_ProductId theReferencedProduct, const TopLoc_Location& thePlacement, - const BRepGraph_OccurrenceId theParentOccurrence) + const BRepGraph_OccurrenceId theParentOccurrence, + BRepGraph_OccurrenceRefId* theOutOccurrenceRefId) { + if (theOutOccurrenceRefId != nullptr) + *theOutOccurrenceRefId = BRepGraph_OccurrenceRefId(); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - // Validate that both arguments are active Product nodes with valid indices. if (!isActiveNode(aStorage, theParentProduct)) return BRepGraph_OccurrenceId(); if (!isActiveNode(aStorage, theReferencedProduct)) return BRepGraph_OccurrenceId(); - // Prevent self-referencing cycles in the assembly DAG. if (theParentProduct == theReferencedProduct) return BRepGraph_OccurrenceId(); - // Validate parent occurrence if provided. if (theParentOccurrence.IsValid() && !isActiveNode(aStorage, theParentOccurrence)) return BRepGraph_OccurrenceId(); if (theParentOccurrence.IsValid() @@ -1150,7 +1154,6 @@ BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( BRepGraph_OccurrenceId()); myGraph->allocateUID(anOccId); - // Add OccurrenceRef to the parent product. const BRepGraph_OccurrenceRefId anOccRefId(aStorage.NbOccurrenceRefs()); aStorage.AppendOccurrenceRef(); BRepGraphInc::OccurrenceRef& anOccRef = aStorage.ChangeOccurrenceRef(anOccRefId); @@ -1160,14 +1163,11 @@ BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( myGraph->allocateRefUID(anOccRefId); aStorage.ChangeProduct(theParentProduct).OccurrenceRefIds.Append(anOccRefId); - // If the referenced product was a root, it is no longer (it now has a parent). removeFromRootProducts(myGraph->myData->myRootProductIds, theReferencedProduct); + aStorage.ChangeReverseIndex().BindProductOccurrence(anOccId, theReferencedProduct); - // Rebuild product->occurrence reverse index to keep it in sync - // after appending a new occurrence entity. - aStorage.ChangeReverseIndex().BuildProductOccurrences(aStorage.myOccurrences.Entities, - aStorage.NbProducts()); - + if (theOutOccurrenceRefId != nullptr) + *theOutOccurrenceRefId = anOccRefId; return anOccId; } @@ -1236,8 +1236,31 @@ void BRepGraph::EditorView::GenOps::RemoveNode(const BRepGraph_NodeId theNode, if (theNode.NodeKind == BRepGraph_NodeId::Kind::Edge) { + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(theNode); + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); + BRepGraphInc_ReverseIndex& aRI = aStorage.ChangeReverseIndex(); + if (anEdge.StartVertexRefId.IsValid(aStorage.NbVertexRefs())) + { + const BRepGraph_VertexId aStartV = aStorage.VertexRef(anEdge.StartVertexRefId).VertexDefId; + if (aStartV.IsValid()) + aRI.UnbindVertexFromEdge(aStartV, anEdgeId); + } + if (anEdge.EndVertexRefId.IsValid(aStorage.NbVertexRefs())) + { + const BRepGraph_VertexId anEndV = aStorage.VertexRef(anEdge.EndVertexRefId).VertexDefId; + if (anEndV.IsValid()) + aRI.UnbindVertexFromEdge(anEndV, anEdgeId); + } + for (const BRepGraph_VertexRefId& anIntRefId : anEdge.InternalVertexRefIds) + { + if (!anIntRefId.IsValid(aStorage.NbVertexRefs())) + continue; + const BRepGraph_VertexId anIntV = aStorage.VertexRef(anIntRefId).VertexDefId; + if (anIntV.IsValid()) + aRI.UnbindVertexFromEdge(anIntV, anEdgeId); + } // Keep reverse edge->coedge table coherent for pure removals too. - unbindCoEdgesOfRemovedEdge(aStorage, BRepGraph_EdgeId::FromNodeId(theNode)); + unbindCoEdgesOfRemovedEdge(aStorage, anEdgeId); } // Mark removed on the entity (which is the sole definition store). @@ -1272,8 +1295,35 @@ void BRepGraph::EditorView::GenOps::RemoveNode(const BRepGraph_NodeId theNode, //================================================================================================= +namespace +{ +struct RemoveSubgraphScope +{ + BRepGraph_Data& Data; + bool IsOutermost; + + explicit RemoveSubgraphScope(BRepGraph_Data& theData) + : Data(theData), + IsOutermost(false) + { + ++Data.myRemoveSubgraphDepth; + IsOutermost = (Data.myRemoveSubgraphDepth == 1); + } + + ~RemoveSubgraphScope() { --Data.myRemoveSubgraphDepth; } + + RemoveSubgraphScope(const RemoveSubgraphScope&) = delete; + RemoveSubgraphScope& operator=(const RemoveSubgraphScope&) = delete; +}; +} // namespace + void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNode) { + // Cascade-aware reverse-index maintenance: per-kind unbinds inside the recursion + // are skipped; we rebuild once at the outermost call exit. Inner mutations stay O(1). + RemoveSubgraphScope aScope(*myGraph->myData); + const bool isOutermost = aScope.IsOutermost; + // Collect and recursively remove children BEFORE marking this node as removed, // because iterators (DefsIterator, RefsIterator) skip removed parents/children. // Children shared with other active parents are NOT removed (ownership-aware). @@ -1393,6 +1443,13 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod // Mark the node as removed AFTER children have been collected and processed, // so that iterators can still see this node as a valid parent during traversal. RemoveNode(theNode); + + if (isOutermost) + { + myGraph->myData->myIncStorage.BuildReverseIndex(); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), + "RemoveSubgraph: reverse-index invariant violated"); + } } //================================================================================================= @@ -1404,13 +1461,114 @@ bool BRepGraph::EditorView::GenOps::RemoveRef(const BRepGraph_RefId theRef) return false; } - if (!myGraph->myData->myIncStorage.MarkRemovedRef(theRef)) + // Snapshot ref state before MarkRemoved flips IsRemoved. + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraphInc::BaseRef& aRef = aStorage.BaseRef(theRef); + if (aRef.IsRemoved) + return false; + const BRepGraph_NodeId aParent = aRef.ParentId; + BRepGraph_NodeId aChildNode; + BRepGraph_NodeId aOccChildDef; + BRepGraph_EdgeId aCoEdgeUnderlyingEdge; + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Shell: + aChildNode = aStorage.ShellRef(BRepGraph_ShellRefId(theRef)).ShellDefId; + break; + case BRepGraph_RefId::Kind::Face: + aChildNode = aStorage.FaceRef(BRepGraph_FaceRefId(theRef)).FaceDefId; + break; + case BRepGraph_RefId::Kind::Wire: + aChildNode = aStorage.WireRef(BRepGraph_WireRefId(theRef)).WireDefId; + break; + case BRepGraph_RefId::Kind::CoEdge: { + aChildNode = aStorage.CoEdgeRef(BRepGraph_CoEdgeRefId(theRef)).CoEdgeDefId; + if (aChildNode.IsValid(aStorage.NbCoEdges())) + aCoEdgeUnderlyingEdge = aStorage.CoEdge(BRepGraph_CoEdgeId(aChildNode)).EdgeDefId; + break; + } + case BRepGraph_RefId::Kind::Vertex: + aChildNode = aStorage.VertexRef(BRepGraph_VertexRefId(theRef)).VertexDefId; + break; + case BRepGraph_RefId::Kind::Solid: + aChildNode = aStorage.SolidRef(BRepGraph_SolidRefId(theRef)).SolidDefId; + break; + case BRepGraph_RefId::Kind::Child: + aChildNode = aStorage.ChildRef(BRepGraph_ChildRefId(theRef)).ChildDefId; + break; + case BRepGraph_RefId::Kind::Occurrence: { + aChildNode = aStorage.OccurrenceRef(BRepGraph_OccurrenceRefId(theRef)).OccurrenceDefId; + if (aChildNode.IsValid()) + aOccChildDef = aStorage.Occurrence(BRepGraph_OccurrenceId(aChildNode)).ChildDefId; + break; + } + default: + break; + } + + if (!aStorage.MarkRemovedRef(theRef)) { return false; } + BRepGraphInc_ReverseIndex& aRI = aStorage.ChangeReverseIndex(); + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Face: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Shell && aChildNode.IsValid()) + aRI.UnbindFaceFromShell(BRepGraph_FaceId(aChildNode), BRepGraph_ShellId(aParent)); + break; + case BRepGraph_RefId::Kind::Wire: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Face && aChildNode.IsValid()) + aRI.UnbindWireFromFace(BRepGraph_WireId(aChildNode), BRepGraph_FaceId(aParent)); + break; + case BRepGraph_RefId::Kind::CoEdge: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Wire && aChildNode.IsValid()) + { + const BRepGraph_WireId aWireId(aParent); + aRI.UnbindCoEdgeFromWire(BRepGraph_CoEdgeId(aChildNode), aWireId); + if (aCoEdgeUnderlyingEdge.IsValid() + && isLastEdgeUsageInWire(aStorage, + aWireId, + aCoEdgeUnderlyingEdge, + BRepGraph_CoEdgeRefId(theRef))) + { + aRI.UnbindEdgeFromWire(aCoEdgeUnderlyingEdge, aWireId); + } + } + break; + case BRepGraph_RefId::Kind::Vertex: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Edge && aChildNode.IsValid()) + aRI.UnbindVertexFromEdge(BRepGraph_VertexId(aChildNode), BRepGraph_EdgeId(aParent)); + break; + case BRepGraph_RefId::Kind::Shell: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Solid && aChildNode.IsValid()) + aRI.UnbindShellFromSolid(BRepGraph_ShellId(aChildNode), BRepGraph_SolidId(aParent)); + break; + case BRepGraph_RefId::Kind::Solid: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::CompSolid && aChildNode.IsValid()) + aRI.UnbindSolidFromCompSolid(BRepGraph_SolidId(aChildNode), BRepGraph_CompSolidId(aParent)); + break; + case BRepGraph_RefId::Kind::Child: + if (aParent.NodeKind == BRepGraph_NodeId::Kind::Compound && aChildNode.IsValid()) + aRI.UnbindCompoundChild(aChildNode, BRepGraph_CompoundId(aParent)); + break; + case BRepGraph_RefId::Kind::Occurrence: + if (aChildNode.IsValid() && aOccChildDef.IsValid() + && aOccChildDef.NodeKind == BRepGraph_NodeId::Kind::Product) + { + aRI.UnbindProductOccurrence(BRepGraph_OccurrenceId(aChildNode), + BRepGraph_ProductId::FromNodeId(aOccChildDef)); + } + break; + default: + break; + } + myGraph->myLayerRegistry.DispatchOnRefRemoved(theRef); myGraph->markRefModified(theRef); + Standard_ASSERT_VOID(aStorage.ValidateReverseIndex(), + "BRepGraph::RemoveRef: reverse-index invariant violated"); return true; } @@ -1473,7 +1631,7 @@ void BRepGraph::EditorView::CoEdgeOps::SetPCurve(const BRepGraph_CoEdgeId const occ::handle& theCurve2d) { BRepGraph_MutGuard aCoEdge = myGraph->Editor().CoEdges().Mut(theCoEdge); - aCoEdge->Curve2DRepId = + aCoEdge.Internal().Curve2DRepId = theCurve2d.IsNull() ? BRepGraph_Curve2DRepId() : CreateCurve2DRep(theCurve2d); } @@ -2096,12 +2254,13 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit } myGraph->allocateRefUID(aNewRefId); - const size_t aPos = i + anInsertOffset; - BRepGraphInc::WireDef& aWireEnt = aStorage.ChangeWire(aWireId); - if (aPos < aWireEnt.CoEdgeRefIds.Size()) - aWireEnt.CoEdgeRefIds.InsertAfter(aPos, aNewRefId); + const size_t aPos = i + anInsertOffset; + BRepGraphInc::WireDef& aWireEnt = aStorage.ChangeWire(aWireId); + auto& aCoEdgeRefIds = aWireEnt.CoEdgeRefIds; + if (aPos < aCoEdgeRefIds.Size()) + aCoEdgeRefIds.InsertAfter(aPos, aNewRefId); else - aWireEnt.CoEdgeRefIds.Append(aNewRefId); + aCoEdgeRefIds.Append(aNewRefId); ++anInsertOffset; aRevIdx.UnbindCoEdgeFromWire(aOldCEId, aWireId); @@ -2340,12 +2499,13 @@ bool BRepGraph::EditorView::CompoundOps::RemoveChild(const BRepGraph_CompoundId NCollection_DynamicArray& aCompoundRefIds = aStorage.ChangeCompound(theCompoundDefId).ChildRefIds; - if (!detachOrderedParentRef(*myGraph, theChildRefId, aCompoundRefIds, aRef.ChildDefId, true)) + const BRepGraph_NodeId aChildDefId = aRef.ChildDefId; + if (!detachOrderedParentRef(*myGraph, theChildRefId, aCompoundRefIds, aChildDefId, true)) { return false; } - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().UnbindCompoundChild(aChildDefId, theCompoundDefId); myGraph->markModified(theCompoundDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2386,20 +2546,28 @@ bool BRepGraph::EditorView::ProductOps::RemoveOccurrence( NCollection_DynamicArray& aProductRefIds = aStorage.ChangeProduct(theProductDefId).OccurrenceRefIds; + const BRepGraph_OccurrenceId anOccDefId = aRef.OccurrenceDefId; + // Capture child def id before detach (myProductToOccurrences is keyed by ChildDefId). + const BRepGraph_NodeId aChildDefIdSnap = aStorage.Occurrence(anOccDefId).ChildDefId; if (!detachOrderedParentRef(*myGraph, theOccurrenceRefId, aProductRefIds, - BRepGraph_NodeId(aRef.OccurrenceDefId), + BRepGraph_NodeId(anOccDefId), true)) { return false; } - aStorage.BuildReverseIndex(); + if (aChildDefIdSnap.IsValid() && aChildDefIdSnap.NodeKind == BRepGraph_NodeId::Kind::Product) + { + aStorage.ChangeReverseIndex().UnbindProductOccurrence( + anOccDefId, + BRepGraph_ProductId::FromNodeId(aChildDefIdSnap)); + } // Check if the removed occurrence's child product was its last parent; // if so, re-add the child product to root products. - const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.OccurrenceDefId); + const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(anOccDefId); if (anOccDef.ChildDefId.IsValid() && anOccDef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) { @@ -2460,18 +2628,21 @@ bool BRepGraph::EditorView::ProductOps::RemoveShapeRoot(const BRepGraph_ProductI } // Detach the shape-root occurrence from the product. + const BRepGraph_OccurrenceId aShapeRootOccId = + aStorage.OccurrenceRef(aShapeRootRefId).OccurrenceDefId; detachOrderedParentRef(*myGraph, aShapeRootRefId, aProduct.OccurrenceRefIds, - BRepGraph_NodeId(aStorage.OccurrenceRef(aShapeRootRefId).OccurrenceDefId), + BRepGraph_NodeId(aShapeRootOccId), true); + // No reverse-index unbind: shape-root occurrence's ChildDefId is topology, not a product. + if (!hasAnyActiveUsage(*myGraph, aShapeRoot)) { myGraph->Editor().Gen().RemoveSubgraph(aShapeRoot); } - aStorage.BuildReverseIndex(); myGraph->markModified(theProductDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2515,7 +2686,6 @@ bool BRepGraph::EditorView::ShellOps::RemoveChild(const BRepGraph_ShellId the return false; } - aStorage.BuildReverseIndex(); myGraph->markModified(theShellDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2559,7 +2729,6 @@ bool BRepGraph::EditorView::SolidOps::RemoveChild(const BRepGraph_SolidId the return false; } - aStorage.BuildReverseIndex(); myGraph->markModified(theSolidDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2598,16 +2767,17 @@ bool BRepGraph::EditorView::CompSolidOps::RemoveSolid(const BRepGraph_CompSolidI NCollection_DynamicArray& aCompSolidRefIds = aStorage.ChangeCompSolid(theCompSolidDefId).SolidRefIds; + const BRepGraph_SolidId aSolidDefId = aRef.SolidDefId; if (!detachOrderedParentRef(*myGraph, theSolidRefId, aCompSolidRefIds, - BRepGraph_NodeId(aRef.SolidDefId), + BRepGraph_NodeId(aSolidDefId), true)) { return false; } - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().UnbindSolidFromCompSolid(aSolidDefId, theCompSolidDefId); myGraph->markModified(theCompSolidDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2646,16 +2816,17 @@ bool BRepGraph::EditorView::SolidOps::RemoveShell(const BRepGraph_SolidId the NCollection_DynamicArray& aSolidRefIds = aStorage.ChangeSolid(theSolidDefId).ShellRefIds; + const BRepGraph_ShellId aShellDefId = aRef.ShellDefId; if (!detachOrderedParentRef(*myGraph, theShellRefId, aSolidRefIds, - BRepGraph_NodeId(aRef.ShellDefId), + BRepGraph_NodeId(aShellDefId), true)) { return false; } - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().UnbindShellFromSolid(aShellDefId, theSolidDefId); myGraph->markModified(theSolidDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2694,16 +2865,17 @@ bool BRepGraph::EditorView::ShellOps::RemoveFace(const BRepGraph_ShellId theSh NCollection_DynamicArray& aShellRefIds = aStorage.ChangeShell(theShellDefId).FaceRefIds; + const BRepGraph_FaceId aFaceDefId = aRef.FaceDefId; if (!detachOrderedParentRef(*myGraph, theFaceRefId, aShellRefIds, - BRepGraph_NodeId(aRef.FaceDefId), + BRepGraph_NodeId(aFaceDefId), true)) { return false; } - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().UnbindFaceFromShell(aFaceDefId, theShellDefId); myGraph->markModified(theShellDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2751,7 +2923,6 @@ bool BRepGraph::EditorView::FaceOps::RemoveVertex(const BRepGraph_FaceId th return false; } - aStorage.BuildReverseIndex(); myGraph->markModified(theFaceDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2790,16 +2961,17 @@ bool BRepGraph::EditorView::FaceOps::RemoveWire(const BRepGraph_FaceId theFac NCollection_DynamicArray& aFaceRefIds = aStorage.ChangeFace(theFaceDefId).WireRefIds; + const BRepGraph_WireId aWireDefId = aRef.WireDefId; if (!detachOrderedParentRef(*myGraph, theWireRefId, aFaceRefIds, - BRepGraph_NodeId(aRef.WireDefId), + BRepGraph_NodeId(aWireDefId), true)) { return false; } - aStorage.BuildReverseIndex(); + aStorage.ChangeReverseIndex().UnbindWireFromFace(aWireDefId, theFaceDefId); myGraph->markModified(theFaceDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) @@ -2996,12 +3168,42 @@ bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId th return false; } - if (isFixed && !hasAnyActiveUsage(*myGraph, BRepGraph_NodeId(aRef.VertexDefId))) + const BRepGraph_VertexId aVertexDefId = aRef.VertexDefId; + if (isFixed && !hasAnyActiveUsage(*myGraph, BRepGraph_NodeId(aVertexDefId))) { - myGraph->Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aRef.VertexDefId)); + myGraph->Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aVertexDefId)); } - aStorage.BuildReverseIndex(); + // Unbind vertex->edge only when no other active ref from this edge points to the same vertex. + bool aStillReferenced = false; + { + const BRepGraphInc::EdgeDef& anEdgeRO = aStorage.Edge(theEdgeDefId); + auto refTargets = [&](const BRepGraph_VertexRefId theRefId) { + if (!theRefId.IsValid()) + return false; + const BRepGraphInc::VertexRef& aVR = aStorage.VertexRef(theRefId); + return !aVR.IsRemoved && aVR.VertexDefId == aVertexDefId; + }; + if (refTargets(anEdgeRO.StartVertexRefId) || refTargets(anEdgeRO.EndVertexRefId)) + { + aStillReferenced = true; + } + else + { + for (const BRepGraph_VertexRefId& anIVRefId : anEdgeRO.InternalVertexRefIds) + { + if (refTargets(anIVRefId)) + { + aStillReferenced = true; + break; + } + } + } + } + if (!aStillReferenced) + { + aStorage.ChangeReverseIndex().UnbindVertexFromEdge(aVertexDefId, theEdgeDefId); + } myGraph->markModified(theEdgeDefId); if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx index 671a3693ab..8925cd1f19 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -29,6 +31,10 @@ class Geom_Surface; class Geom_Curve; class Geom2d_Curve; +class Poly_Triangulation; +class Poly_Polygon3D; +class Poly_Polygon2D; +class Poly_PolygonOnTriangulation; //! @brief Non-const view for programmatic graph construction and structural editing. //! @@ -94,6 +100,59 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutPolygonOnTri( const BRepGraph_PolygonOnTriRepId thePolygon); + //! Set the surface handle on a SurfaceRep. + Standard_EXPORT void SetSurface(const BRepGraph_SurfaceRepId theRep, + const occ::handle& theSurface); + Standard_EXPORT void SetSurface(BRepGraph_MutGuard& theMut, + const occ::handle& theSurface); + + //! Set the 3D curve handle on a Curve3DRep. + Standard_EXPORT void SetCurve3D(const BRepGraph_Curve3DRepId theRep, + const occ::handle& theCurve); + Standard_EXPORT void SetCurve3D(BRepGraph_MutGuard& theMut, + const occ::handle& theCurve); + + //! Set the 2D curve handle on a Curve2DRep. + Standard_EXPORT void SetCurve2D(const BRepGraph_Curve2DRepId theRep, + const occ::handle& theCurve); + Standard_EXPORT void SetCurve2D(BRepGraph_MutGuard& theMut, + const occ::handle& theCurve); + + //! Set the triangulation handle on a TriangulationRep. + Standard_EXPORT void SetTriangulation(const BRepGraph_TriangulationRepId theRep, + const occ::handle& theTri); + Standard_EXPORT void SetTriangulation( + BRepGraph_MutGuard& theMut, + const occ::handle& theTri); + + //! Set the polygon handle on a Polygon3DRep. + Standard_EXPORT void SetPolygon3D(const BRepGraph_Polygon3DRepId theRep, + const occ::handle& thePolygon); + Standard_EXPORT void SetPolygon3D(BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon); + + //! Set the polygon handle on a Polygon2DRep. + Standard_EXPORT void SetPolygon2D(const BRepGraph_Polygon2DRepId theRep, + const occ::handle& thePolygon); + Standard_EXPORT void SetPolygon2D(BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon); + + //! Set the polygon-on-triangulation handle on a PolygonOnTriRep. + Standard_EXPORT void SetPolygonOnTri( + const BRepGraph_PolygonOnTriRepId theRep, + const occ::handle& thePolygon); + Standard_EXPORT void SetPolygonOnTri( + BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon); + + //! Set the triangulation rep id linked to a PolygonOnTriRep. + Standard_EXPORT void SetPolygonOnTriTriangulationId( + const BRepGraph_PolygonOnTriRepId theRep, + const BRepGraph_TriangulationRepId theTriRep); + Standard_EXPORT void SetPolygonOnTriTriangulationId( + BRepGraph_MutGuard& theMut, + const BRepGraph_TriangulationRepId theTriRep); + private: friend class EditorView; @@ -124,6 +183,53 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_VertexRefId theVertexRef); + //! Set the 3D point of a vertex definition and fire immediate notification. + //! @param[in] theVertex typed vertex definition identifier + //! @param[in] thePoint new 3D coordinates + Standard_EXPORT void SetPoint(const BRepGraph_VertexId theVertex, const gp_Pnt& thePoint); + + //! Set the 3D point of a vertex definition inside a batched mutation scope. + //! Marks the guard dirty so the destructor fires a single notification. + //! @param[in] theMut active mutable vertex guard + //! @param[in] thePoint new 3D coordinates + Standard_EXPORT void SetPoint(BRepGraph_MutGuard& theMut, + const gp_Pnt& thePoint); + + //! Set the tolerance of a vertex definition. + Standard_EXPORT void SetTolerance(const BRepGraph_VertexId theVertex, + const double theTolerance); + + //! Set the tolerance inside a batched mutation scope. + Standard_EXPORT void SetTolerance(BRepGraph_MutGuard& theMut, + const double theTolerance); + + //! Set the orientation of a vertex reference. + Standard_EXPORT void SetRefOrientation(const BRepGraph_VertexRefId theVertexRef, + const TopAbs_Orientation theOrientation); + + //! Set the orientation inside a batched mutation scope. + Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Set the local location of a vertex reference and fire immediate notification. + //! @param[in] theVertexRef typed vertex reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_VertexRefId theVertexRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a vertex reference inside a batched mutation scope. + //! @param[in] theMut active mutable vertex reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Rewire a vertex reference to a different vertex def (rebinds VertexToEdges if parent is + //! Edge). + Standard_EXPORT void SetRefVertexDefId(const BRepGraph_VertexRefId theVertexRef, + const BRepGraph_VertexId theVertex); + Standard_EXPORT void SetRefVertexDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_VertexId theVertex); + private: friend class EditorView; @@ -208,6 +314,70 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( const BRepGraph_EdgeId theEdge); + //! Set the tolerance of an edge definition and fire immediate notification. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theTolerance new tolerance value + Standard_EXPORT void SetTolerance(const BRepGraph_EdgeId theEdge, double theTolerance); + + //! Set the tolerance of an edge definition inside a batched mutation scope. + //! @param[in] theMut active mutable edge guard + //! @param[in] theTolerance new tolerance value + Standard_EXPORT void SetTolerance(BRepGraph_MutGuard& theMut, + double theTolerance); + + //! Set the parametric range of an edge definition. + Standard_EXPORT void SetParamRange(const BRepGraph_EdgeId theEdge, + const double theFirst, + const double theLast); + Standard_EXPORT void SetParamRange(BRepGraph_MutGuard& theMut, + const double theFirst, + const double theLast); + + //! Set the SameParameter flag of an edge definition. + Standard_EXPORT void SetSameParameter(const BRepGraph_EdgeId theEdge, + const bool theSameParameter); + Standard_EXPORT void SetSameParameter(BRepGraph_MutGuard& theMut, + const bool theSameParameter); + + //! Set the SameRange flag of an edge definition. + Standard_EXPORT void SetSameRange(const BRepGraph_EdgeId theEdge, const bool theSameRange); + Standard_EXPORT void SetSameRange(BRepGraph_MutGuard& theMut, + const bool theSameRange); + + //! Set the IsDegenerate flag of an edge definition. + Standard_EXPORT void SetDegenerate(const BRepGraph_EdgeId theEdge, const bool theIsDegenerate); + Standard_EXPORT void SetDegenerate(BRepGraph_MutGuard& theMut, + const bool theIsDegenerate); + + //! Set the Curve3DRep id bound to an edge (invalid id clears the binding). + Standard_EXPORT void SetCurve3DRepId(const BRepGraph_EdgeId theEdge, + const BRepGraph_Curve3DRepId theRep); + Standard_EXPORT void SetCurve3DRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_Curve3DRepId theRep); + + //! Set the Polygon3DRep id bound to an edge (invalid id clears the binding). + Standard_EXPORT void SetPolygon3DRepId(const BRepGraph_EdgeId theEdge, + const BRepGraph_Polygon3DRepId theRep); + Standard_EXPORT void SetPolygon3DRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_Polygon3DRepId theRep); + + //! Set the IsClosed flag (StartVertex == EndVertex topology) of an edge. + Standard_EXPORT void SetIsClosed(const BRepGraph_EdgeId theEdge, const bool theIsClosed); + Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, + const bool theIsClosed); + + //! Set the start vertex-ref id (rebinds VertexToEdges). Caller maintains reverse indices. + Standard_EXPORT void SetStartVertexRefId(const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theVertexRef); + Standard_EXPORT void SetStartVertexRefId(BRepGraph_MutGuard& theMut, + const BRepGraph_VertexRefId theVertexRef); + + //! Set the end vertex-ref id (rebinds VertexToEdges). Caller maintains reverse indices. + Standard_EXPORT void SetEndVertexRefId(const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theVertexRef); + Standard_EXPORT void SetEndVertexRefId(BRepGraph_MutGuard& theMut, + const BRepGraph_VertexRefId theVertexRef); + private: friend class EditorView; @@ -270,6 +440,109 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_CoEdgeRefId theCoEdgeRef); + //! Set the parametric range of a coedge definition and fire immediate notification. + //! @param[in] theCoEdge typed coedge definition identifier + //! @param[in] theFirst new first parameter value + //! @param[in] theLast new last parameter value + Standard_EXPORT void SetParamRange(const BRepGraph_CoEdgeId theCoEdge, + double theFirst, + double theLast); + + //! Set the parametric range of a coedge definition inside a batched mutation scope. + //! @param[in] theMut active mutable coedge guard + //! @param[in] theFirst new first parameter value + //! @param[in] theLast new last parameter value + Standard_EXPORT void SetParamRange(BRepGraph_MutGuard& theMut, + double theFirst, + double theLast); + + //! Set the local location of a coedge reference and fire immediate notification. + //! @param[in] theCoEdgeRef typed coedge reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_CoEdgeRefId theCoEdgeRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a coedge reference inside a batched mutation scope. + //! @param[in] theMut active mutable coedge reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Rewire a coedge reference to a different coedge def (rebinds CoEdgeToWires + EdgeToWires). + Standard_EXPORT void SetRefCoEdgeDefId(const BRepGraph_CoEdgeRefId theCoEdgeRef, + const BRepGraph_CoEdgeId theCoEdge); + Standard_EXPORT void SetRefCoEdgeDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_CoEdgeId theCoEdge); + + //! Set the orientation of a coedge definition. + Standard_EXPORT void SetOrientation(const BRepGraph_CoEdgeId theCoEdge, + const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Set the UV box (UV1 = at ParamFirst, UV2 = at ParamLast) of a coedge definition. + Standard_EXPORT void SetUVBox(const BRepGraph_CoEdgeId theCoEdge, + const gp_Pnt2d& theUV1, + const gp_Pnt2d& theUV2); + Standard_EXPORT void SetUVBox(BRepGraph_MutGuard& theMut, + const gp_Pnt2d& theUV1, + const gp_Pnt2d& theUV2); + + //! Set the geometric continuity of a coedge definition. + Standard_EXPORT void SetContinuity(const BRepGraph_CoEdgeId theCoEdge, + const GeomAbs_Shape theContinuity); + Standard_EXPORT void SetContinuity(BRepGraph_MutGuard& theMut, + const GeomAbs_Shape theContinuity); + + //! Set the seam-pair continuity of a coedge definition. + Standard_EXPORT void SetSeamContinuity(const BRepGraph_CoEdgeId theCoEdge, + const GeomAbs_Shape theContinuity); + Standard_EXPORT void SetSeamContinuity(BRepGraph_MutGuard& theMut, + const GeomAbs_Shape theContinuity); + + //! Set the Curve2DRep id bound to a coedge (invalid id clears the binding). + Standard_EXPORT void SetCurve2DRepId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_Curve2DRepId theRep); + Standard_EXPORT void SetCurve2DRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_Curve2DRepId theRep); + + //! Set the Polygon2DRep id bound to a coedge. + Standard_EXPORT void SetPolygon2DRepId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_Polygon2DRepId theRep); + Standard_EXPORT void SetPolygon2DRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_Polygon2DRepId theRep); + + //! Set the PolygonOnTriRep id bound to a coedge. + Standard_EXPORT void SetPolygonOnTriRepId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_PolygonOnTriRepId theRep); + Standard_EXPORT void SetPolygonOnTriRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_PolygonOnTriRepId theRep); + + //! Drop face-bound parametric payload (PCurve, param range, continuity, UVs) + //! while keeping structural links - used when the owning face is removed. + Standard_EXPORT void ClearPCurveBinding(const BRepGraph_CoEdgeId theCoEdge); + Standard_EXPORT void ClearPCurveBinding(BRepGraph_MutGuard& theMut); + + //! Set the seam-pair id linking two coedges of a seam edge (invalid breaks the link). + Standard_EXPORT void SetSeamPairId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_CoEdgeId theSeamPairId); + Standard_EXPORT void SetSeamPairId(BRepGraph_MutGuard& theMut, + const BRepGraph_CoEdgeId theSeamPairId); + + //! Rewire a coedge to a different parent edge (rebinds EdgeToCoEdges, EdgeToWires, + //! EdgeToFaces). Caller maintains reverse indices. + Standard_EXPORT void SetEdgeDefId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theEdge); + Standard_EXPORT void SetEdgeDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_EdgeId theEdge); + + //! Rewire a coedge to a different owning face (rebinds EdgeToFaces). Caller maintains reverse + //! indices. + Standard_EXPORT void SetFaceDefId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_FaceId theFace); + Standard_EXPORT void SetFaceDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace); + private: friend class EditorView; @@ -324,6 +597,46 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_WireRefId theWireRef); + //! Set the IsClosed flag of a wire definition and fire immediate notification. + //! @param[in] theWire typed wire definition identifier + //! @param[in] theIsClosed new closed state + Standard_EXPORT void SetIsClosed(const BRepGraph_WireId theWire, bool theIsClosed); + + //! Set the IsClosed flag of a wire definition inside a batched mutation scope. + //! @param[in] theMut active mutable wire guard + //! @param[in] theIsClosed new closed state + Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, + bool theIsClosed); + + //! Set the local location of a wire reference and fire immediate notification. + //! @param[in] theWireRef typed wire reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_WireRefId theWireRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a wire reference inside a batched mutation scope. + //! @param[in] theMut active mutable wire reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Set the IsOuter flag on a wire reference. + Standard_EXPORT void SetRefIsOuter(const BRepGraph_WireRefId theWireRef, const bool theIsOuter); + Standard_EXPORT void SetRefIsOuter(BRepGraph_MutGuard& theMut, + const bool theIsOuter); + + //! Set the orientation of a wire reference. + Standard_EXPORT void SetRefOrientation(const BRepGraph_WireRefId theWireRef, + const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Rewire a wire reference to a different wire def (rebinds WireToFaces if parent is Face). + Standard_EXPORT void SetRefWireDefId(const BRepGraph_WireRefId theWireRef, + const BRepGraph_WireId theWire); + Standard_EXPORT void SetRefWireDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_WireId theWire); + private: friend class EditorView; @@ -390,6 +703,75 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_FaceRefId theFaceRef); + //! Set the tolerance of a face definition and fire immediate notification. + //! @param[in] theFace typed face definition identifier + //! @param[in] theTolerance new tolerance value + Standard_EXPORT void SetTolerance(const BRepGraph_FaceId theFace, double theTolerance); + + //! Set the tolerance of a face definition inside a batched mutation scope. + //! @param[in] theMut active mutable face guard + //! @param[in] theTolerance new tolerance value + Standard_EXPORT void SetTolerance(BRepGraph_MutGuard& theMut, + double theTolerance); + + //! Set the NaturalRestriction flag of a face definition and fire immediate notification. + //! @param[in] theFace typed face definition identifier + //! @param[in] theNaturalRestriction new flag value + Standard_EXPORT void SetNaturalRestriction(const BRepGraph_FaceId theFace, + bool theNaturalRestriction); + + //! Set the NaturalRestriction flag inside a batched mutation scope. + //! @param[in] theMut active mutable face guard + //! @param[in] theNaturalRestriction new flag value + Standard_EXPORT void SetNaturalRestriction(BRepGraph_MutGuard& theMut, + bool theNaturalRestriction); + + //! Set the triangulation representation id and fire immediate notification. + //! Pass an invalid id to clear the triangulation binding. + //! @param[in] theFace typed face definition identifier + //! @param[in] theRep new triangulation rep identifier (may be invalid to clear) + Standard_EXPORT void SetTriangulationRep(const BRepGraph_FaceId theFace, + const BRepGraph_TriangulationRepId theRep); + + Standard_EXPORT void SetTriangulationRep(BRepGraph_MutGuard& theMut, + const BRepGraph_TriangulationRepId theRep); + + //! Set the SurfaceRep id bound to a face (invalid id clears the binding). + Standard_EXPORT void SetSurfaceRepId(const BRepGraph_FaceId theFace, + const BRepGraph_SurfaceRepId theRep); + Standard_EXPORT void SetSurfaceRepId(BRepGraph_MutGuard& theMut, + const BRepGraph_SurfaceRepId theRep); + + //! Set the orientation of a face reference and fire immediate notification. + //! @param[in] theFaceRef typed face reference identifier + //! @param[in] theOrientation new orientation value + Standard_EXPORT void SetRefOrientation(const BRepGraph_FaceRefId theFaceRef, + TopAbs_Orientation theOrientation); + + //! Set the orientation of a face reference inside a batched mutation scope. + //! @param[in] theMut active mutable face reference guard + //! @param[in] theOrientation new orientation value + Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, + TopAbs_Orientation theOrientation); + + //! Set the local location of a face reference and fire immediate notification. + //! @param[in] theFaceRef typed face reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_FaceRefId theFaceRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a face reference inside a batched mutation scope. + //! @param[in] theMut active mutable face reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Rewire a face reference to a different face def (rebinds FaceToShells if parent is Shell). + Standard_EXPORT void SetRefFaceDefId(const BRepGraph_FaceRefId theFaceRef, + const BRepGraph_FaceId theFace); + Standard_EXPORT void SetRefFaceDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace); + private: friend class EditorView; @@ -458,6 +840,40 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_ShellRefId theShellRef); + //! Set the local location of a shell reference and fire immediate notification. + //! @param[in] theShellRef typed shell reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_ShellRefId theShellRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a shell reference inside a batched mutation scope. + //! @param[in] theMut active mutable shell reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Set the orientation of a shell reference. + Standard_EXPORT void SetRefOrientation(const BRepGraph_ShellRefId theShellRef, + const TopAbs_Orientation theOrientation); + + //! Set the orientation inside a batched mutation scope. + Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Rewire a shell reference to a different shell def (rebinds ShellToSolids if parent is + //! Solid). + Standard_EXPORT void SetRefShellDefId(const BRepGraph_ShellRefId theShellRef, + const BRepGraph_ShellId theShell); + Standard_EXPORT void SetRefShellDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_ShellId theShell); + + //! Set the IsClosed flag of a shell definition. + Standard_EXPORT void SetIsClosed(const BRepGraph_ShellId theShell, const bool theIsClosed); + + //! Set the IsClosed flag inside a batched mutation scope. + Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, + const bool theIsClosed); + private: friend class EditorView; @@ -526,6 +942,33 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_SolidRefId theSolidRef); + //! Set the local location of a solid reference and fire immediate notification. + //! @param[in] theSolidRef typed solid reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_SolidRefId theSolidRef, + const TopLoc_Location& theLoc); + + //! Set the local location of a solid reference inside a batched mutation scope. + //! @param[in] theMut active mutable solid reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Set the orientation of a solid reference. + Standard_EXPORT void SetRefOrientation(const BRepGraph_SolidRefId theSolidRef, + const TopAbs_Orientation theOrientation); + + //! Set the orientation inside a batched mutation scope. + Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Rewire a solid reference to a different solid def (rebinds SolidToCompSolid if parent is + //! CompSolid). + Standard_EXPORT void SetRefSolidDefId(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theSolid); + Standard_EXPORT void SetRefSolidDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_SolidId theSolid); + private: friend class EditorView; @@ -647,29 +1090,19 @@ public: //! @return typed product definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_ProductId CreateEmptyProduct(); - //! Link two existing Products via a fresh top-level Occurrence. - //! @param[in] theParentProduct typed parent product identifier - //! @param[in] theReferencedProduct typed child product identifier being instantiated - //! @param[in] thePlacement local placement relative to parent - //! @return typed occurrence definition identifier, or invalid unless the - //! parent and referenced products are both active and not equal - [[nodiscard]] Standard_EXPORT BRepGraph_OccurrenceId - LinkProducts(const BRepGraph_ProductId theParentProduct, - const BRepGraph_ProductId theReferencedProduct, - const TopLoc_Location& thePlacement); - - //! Link two existing Products via a fresh Occurrence inside an explicit parent occurrence - //! (for nested assembly chains with unambiguous GlobalLocation in DAGs). - //! @param[in] theParentProduct typed parent product identifier - //! @param[in] theReferencedProduct typed child product identifier being instantiated - //! @param[in] thePlacement local placement relative to parent - //! @param[in] theParentOccurrence typed occurrence that placed the parent product - //! @return typed occurrence definition identifier, or invalid unless the chain is active + //! Link two existing Products via a fresh Occurrence. + //! @param[in] theParentProduct typed parent product identifier + //! @param[in] theReferencedProduct typed child product identifier being instantiated + //! @param[in] thePlacement local placement relative to parent + //! @param[in] theParentOccurrence optional placing occurrence (nested assembly chains) + //! @param[out] theOutOccurrenceRefId optional out: typed ref id of the inserted OccurrenceRef + //! @return typed occurrence definition identifier, or invalid if the chain is not active [[nodiscard]] Standard_EXPORT BRepGraph_OccurrenceId LinkProducts(const BRepGraph_ProductId theParentProduct, const BRepGraph_ProductId theReferencedProduct, const TopLoc_Location& thePlacement, - const BRepGraph_OccurrenceId theParentOccurrence); + const BRepGraph_OccurrenceId theParentOccurrence = BRepGraph_OccurrenceId(), + BRepGraph_OccurrenceRefId* theOutOccurrenceRefId = nullptr); //! Detach one exact occurrence ref from a product definition. //! Use BRepGraph_RefsOccurrenceOfProduct::CurrentId() when removing from a @@ -719,6 +1152,36 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_OccurrenceRefId theOccurrenceRef); + //! Set the local location of an occurrence reference and fire immediate notification. + //! @param[in] theOccurrenceRef typed occurrence reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation(const BRepGraph_OccurrenceRefId theOccurrenceRef, + const TopLoc_Location& theLoc); + + //! Set the local location of an occurrence reference inside a batched mutation scope. + //! @param[in] theMut active mutable occurrence reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + + //! Set the child node referenced by an occurrence definition. + //! The child kind must be a topology root or a Product - invalid kinds are + //! accepted but the resulting graph will fail Validate. + Standard_EXPORT void SetChildDefId(const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theChildDefId); + + //! Set the child node id inside a batched mutation scope. + Standard_EXPORT void SetChildDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChildDefId); + + //! Rewire an occurrence reference to a different occurrence def (rebinds ProductToOccurrences). + Standard_EXPORT void SetRefOccurrenceDefId(const BRepGraph_OccurrenceRefId theOccurrenceRef, + const BRepGraph_OccurrenceId theOccurrence); + Standard_EXPORT void SetRefOccurrenceDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_OccurrenceId theOccurrence); + private: friend class EditorView; @@ -788,6 +1251,33 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutChildRef( const BRepGraph_ChildRefId theChildRef); + //! Set the local location of a child reference and fire immediate notification. + //! @param[in] theChildRef typed child reference identifier + //! @param[in] theLoc new local location + Standard_EXPORT void SetChildRefLocalLocation(const BRepGraph_ChildRefId theChildRef, + const TopLoc_Location& theLoc); + + //! Set the orientation of a child reference. + Standard_EXPORT void SetChildRefOrientation(const BRepGraph_ChildRefId theChildRef, + const TopAbs_Orientation theOrientation); + + //! Set the orientation inside a batched mutation scope. + Standard_EXPORT void SetChildRefOrientation(BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation); + + //! Rewire a child reference to a different child def (rebinds CompoundsOf). + Standard_EXPORT void SetChildRefChildDefId(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theChild); + Standard_EXPORT void SetChildRefChildDefId(BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChild); + + //! Set the local location of a child reference inside a batched mutation scope. + //! @param[in] theMut active mutable child reference guard + //! @param[in] theLoc new local location + Standard_EXPORT void SetChildRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc); + //! Apply a modification operation and record history. //! @param[in] theTarget node to modify //! @param[in] theModifier callback that performs the modification and returns replacements diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx new file mode 100644 index 0000000000..21f96ba0f8 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx @@ -0,0 +1,1785 @@ +// Copyright (c) 2026 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +//! True if no other active coedge of theEdge references theFace. +bool isLastCoEdgeOfEdgeOnFace(const BRepGraphInc_Storage& theStorage, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const BRepGraph_CoEdgeId theExcluding) +{ + const NCollection_DynamicArray* aCoEdges = + theStorage.ReverseIndex().CoEdgesOfEdge(theEdge); + if (aCoEdges == nullptr) + return true; + for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) + { + if (aCoEdgeId == theExcluding) + continue; + if (!aCoEdgeId.IsValid(theStorage.NbCoEdges())) + continue; + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); + if (!aCoEdge.IsRemoved && aCoEdge.FaceDefId == theFace) + return false; + } + return true; +} + +} // namespace + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetPoint(const BRepGraph_VertexId theVertex, + const gp_Pnt& thePoint) +{ + myGraph->myData->myIncStorage.ChangeVertex(theVertex).Point = thePoint; + myGraph->markModified(theVertex); +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetPoint(BRepGraph_MutGuard& theMut, + const gp_Pnt& thePoint) +{ + theMut.Internal().Point = thePoint; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetTolerance(const BRepGraph_EdgeId theEdge, + double theTolerance) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).Tolerance = theTolerance; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetTolerance(BRepGraph_MutGuard& theMut, + double theTolerance) +{ + theMut.Internal().Tolerance = theTolerance; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetParamRange(const BRepGraph_CoEdgeId theCoEdge, + double theFirst, + double theLast) +{ + BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); + aDef.ParamFirst = theFirst; + aDef.ParamLast = theLast; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetParamRange( + BRepGraph_MutGuard& theMut, + double theFirst, + double theLast) +{ + theMut.Internal().ParamFirst = theFirst; + theMut.Internal().ParamLast = theLast; +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetIsClosed(const BRepGraph_WireId theWire, bool theIsClosed) +{ + myGraph->myData->myIncStorage.ChangeWire(theWire).IsClosed = theIsClosed; + myGraph->markModified(theWire); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetIsClosed(BRepGraph_MutGuard& theMut, + bool theIsClosed) +{ + theMut.Internal().IsClosed = theIsClosed; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetTolerance(const BRepGraph_FaceId theFace, + double theTolerance) +{ + myGraph->myData->myIncStorage.ChangeFace(theFace).Tolerance = theTolerance; + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetTolerance(BRepGraph_MutGuard& theMut, + double theTolerance) +{ + theMut.Internal().Tolerance = theTolerance; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetNaturalRestriction(const BRepGraph_FaceId theFace, + bool theNaturalRestriction) +{ + myGraph->myData->myIncStorage.ChangeFace(theFace).NaturalRestriction = theNaturalRestriction; + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetNaturalRestriction( + BRepGraph_MutGuard& theMut, + bool theNaturalRestriction) +{ + theMut.Internal().NaturalRestriction = theNaturalRestriction; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetTriangulationRep(const BRepGraph_FaceId theFace, + const BRepGraph_TriangulationRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeFace(theFace).TriangulationRepId = theRep; + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetRefOrientation(const BRepGraph_FaceRefId theFaceRef, + TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeFaceRef(theFaceRef).Orientation = theOrientation; + myGraph->markRefModified(theFaceRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetRefOrientation( + BRepGraph_MutGuard& theMut, + TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetRefLocalLocation(const BRepGraph_FaceRefId theFaceRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeFaceRef(theFaceRef).LocalLocation = theLoc; + myGraph->markRefModified(theFaceRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::OccurrenceOps::SetRefLocalLocation( + const BRepGraph_OccurrenceRefId theOccurrenceRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeOccurrenceRef(theOccurrenceRef).LocalLocation = theLoc; + myGraph->markRefModified(theOccurrenceRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::OccurrenceOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetRefLocalLocation(const BRepGraph_VertexRefId theVertexRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeVertexRef(theVertexRef).LocalLocation = theLoc; + myGraph->markRefModified(theVertexRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetRefLocalLocation(const BRepGraph_CoEdgeRefId theCoEdgeRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeCoEdgeRef(theCoEdgeRef).LocalLocation = theLoc; + myGraph->markRefModified(theCoEdgeRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefLocalLocation(const BRepGraph_WireRefId theWireRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).LocalLocation = theLoc; + myGraph->markRefModified(theWireRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetRefLocalLocation(const BRepGraph_ShellRefId theShellRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeShellRef(theShellRef).LocalLocation = theLoc; + myGraph->markRefModified(theShellRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::SolidOps::SetRefLocalLocation(const BRepGraph_SolidRefId theSolidRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeSolidRef(theSolidRef).LocalLocation = theLoc; + myGraph->markRefModified(theSolidRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::SolidOps::SetRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::GenOps::SetChildRefLocalLocation(const BRepGraph_ChildRefId theChildRef, + const TopLoc_Location& theLoc) +{ + myGraph->myData->myIncStorage.ChangeChildRef(theChildRef).LocalLocation = theLoc; + myGraph->markRefModified(theChildRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::GenOps::SetChildRefLocalLocation( + BRepGraph_MutGuard& theMut, + const TopLoc_Location& theLoc) +{ + theMut.Internal().LocalLocation = theLoc; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetParamRange(const BRepGraph_EdgeId theEdge, + const double theFirst, + const double theLast) +{ + BRepGraphInc::EdgeDef& aDef = myGraph->myData->myIncStorage.ChangeEdge(theEdge); + aDef.ParamFirst = theFirst; + aDef.ParamLast = theLast; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetParamRange( + BRepGraph_MutGuard& theMut, + const double theFirst, + const double theLast) +{ + theMut.Internal().ParamFirst = theFirst; + theMut.Internal().ParamLast = theLast; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetSameParameter(const BRepGraph_EdgeId theEdge, + const bool theSameParameter) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).SameParameter = theSameParameter; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetSameParameter( + BRepGraph_MutGuard& theMut, + const bool theSameParameter) +{ + theMut.Internal().SameParameter = theSameParameter; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetSameRange(const BRepGraph_EdgeId theEdge, + const bool theSameRange) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).SameRange = theSameRange; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetSameRange(BRepGraph_MutGuard& theMut, + const bool theSameRange) +{ + theMut.Internal().SameRange = theSameRange; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetDegenerate(const BRepGraph_EdgeId theEdge, + const bool theIsDegenerate) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).IsDegenerate = theIsDegenerate; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetDegenerate( + BRepGraph_MutGuard& theMut, + const bool theIsDegenerate) +{ + theMut.Internal().IsDegenerate = theIsDegenerate; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetCurve3DRepId(const BRepGraph_EdgeId theEdge, + const BRepGraph_Curve3DRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).Curve3DRepId = theRep; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetCurve3DRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_Curve3DRepId theRep) +{ + theMut.Internal().Curve3DRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetPolygon3DRepId(const BRepGraph_EdgeId theEdge, + const BRepGraph_Polygon3DRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).Polygon3DRepId = theRep; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetPolygon3DRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_Polygon3DRepId theRep) +{ + theMut.Internal().Polygon3DRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetTriangulationRep( + BRepGraph_MutGuard& theMut, + const BRepGraph_TriangulationRepId theRep) +{ + theMut.Internal().TriangulationRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetSurfaceRepId(const BRepGraph_FaceId theFace, + const BRepGraph_SurfaceRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeFace(theFace).SurfaceRepId = theRep; + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetSurfaceRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_SurfaceRepId theRep) +{ + theMut.Internal().SurfaceRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetOrientation(const BRepGraph_CoEdgeId theCoEdge, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Orientation = theOrientation; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetUVBox(const BRepGraph_CoEdgeId theCoEdge, + const gp_Pnt2d& theUV1, + const gp_Pnt2d& theUV2) +{ + BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); + aDef.UV1 = theUV1; + aDef.UV2 = theUV2; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetUVBox(BRepGraph_MutGuard& theMut, + const gp_Pnt2d& theUV1, + const gp_Pnt2d& theUV2) +{ + theMut.Internal().UV1 = theUV1; + theMut.Internal().UV2 = theUV2; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetContinuity(const BRepGraph_CoEdgeId theCoEdge, + const GeomAbs_Shape theContinuity) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Continuity = theContinuity; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetContinuity( + BRepGraph_MutGuard& theMut, + const GeomAbs_Shape theContinuity) +{ + theMut.Internal().Continuity = theContinuity; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetSeamContinuity(const BRepGraph_CoEdgeId theCoEdge, + const GeomAbs_Shape theContinuity) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).SeamContinuity = theContinuity; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetSeamContinuity( + BRepGraph_MutGuard& theMut, + const GeomAbs_Shape theContinuity) +{ + theMut.Internal().SeamContinuity = theContinuity; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetCurve2DRepId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_Curve2DRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Curve2DRepId = theRep; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetCurve2DRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_Curve2DRepId theRep) +{ + theMut.Internal().Curve2DRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPolygon2DRepId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_Polygon2DRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Polygon2DRepId = theRep; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPolygon2DRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_Polygon2DRepId theRep) +{ + theMut.Internal().Polygon2DRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPolygonOnTriRepId( + const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_PolygonOnTriRepId theRep) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).PolygonOnTriRepId = theRep; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPolygonOnTriRepId( + BRepGraph_MutGuard& theMut, + const BRepGraph_PolygonOnTriRepId theRep) +{ + theMut.Internal().PolygonOnTriRepId = theRep; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::ClearPCurveBinding(const BRepGraph_CoEdgeId theCoEdge) +{ + BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); + aDef.Curve2DRepId = BRepGraph_Curve2DRepId(); + aDef.ParamFirst = 0.0; + aDef.ParamLast = 0.0; + aDef.Continuity = GeomAbs_C0; + aDef.UV1 = gp_Pnt2d(); + aDef.UV2 = gp_Pnt2d(); + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::ClearPCurveBinding( + BRepGraph_MutGuard& theMut) +{ + theMut.Internal().Curve2DRepId = BRepGraph_Curve2DRepId(); + theMut.Internal().ParamFirst = 0.0; + theMut.Internal().ParamLast = 0.0; + theMut.Internal().Continuity = GeomAbs_C0; + theMut.Internal().UV1 = gp_Pnt2d(); + theMut.Internal().UV2 = gp_Pnt2d(); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefIsOuter(const BRepGraph_WireRefId theWireRef, + const bool theIsOuter) +{ + myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).IsOuter = theIsOuter; + myGraph->markRefModified(theWireRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefIsOuter( + BRepGraph_MutGuard& theMut, + const bool theIsOuter) +{ + theMut.Internal().IsOuter = theIsOuter; +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefOrientation(const BRepGraph_WireRefId theWireRef, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).Orientation = theOrientation; + myGraph->markRefModified(theWireRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetRefOrientation(const BRepGraph_ShellRefId theShellRef, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeShellRef(theShellRef).Orientation = theOrientation; + myGraph->markRefModified(theShellRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetRefOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetIsClosed(const BRepGraph_ShellId theShell, + const bool theIsClosed) +{ + myGraph->myData->myIncStorage.ChangeShell(theShell).IsClosed = theIsClosed; + myGraph->markModified(theShell); +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetIsClosed( + BRepGraph_MutGuard& theMut, + const bool theIsClosed) +{ + theMut.Internal().IsClosed = theIsClosed; +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetTolerance(const BRepGraph_VertexId theVertex, + const double theTolerance) +{ + myGraph->myData->myIncStorage.ChangeVertex(theVertex).Tolerance = theTolerance; + myGraph->markModified(theVertex); +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetTolerance( + BRepGraph_MutGuard& theMut, + const double theTolerance) +{ + theMut.Internal().Tolerance = theTolerance; +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetRefOrientation(const BRepGraph_VertexRefId theVertexRef, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeVertexRef(theVertexRef).Orientation = theOrientation; + myGraph->markRefModified(theVertexRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetRefOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetIsClosed(const BRepGraph_EdgeId theEdge, + const bool theIsClosed) +{ + myGraph->myData->myIncStorage.ChangeEdge(theEdge).IsClosed = theIsClosed; + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetIsClosed(BRepGraph_MutGuard& theMut, + const bool theIsClosed) +{ + theMut.Internal().IsClosed = theIsClosed; +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetSeamPairId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_CoEdgeId theSeamPairId) +{ + myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).SeamPairId = theSeamPairId; + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetSeamPairId( + BRepGraph_MutGuard& theMut, + const BRepGraph_CoEdgeId theSeamPairId) +{ + theMut.Internal().SeamPairId = theSeamPairId; +} + +//================================================================================================= + +void BRepGraph::EditorView::SolidOps::SetRefOrientation(const BRepGraph_SolidRefId theSolidRef, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeSolidRef(theSolidRef).Orientation = theOrientation; + myGraph->markRefModified(theSolidRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::SolidOps::SetRefOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::OccurrenceOps::SetChildDefId(const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theChildDefId) +{ + myGraph->myData->myIncStorage.ChangeOccurrence(theOccurrence).ChildDefId = theChildDefId; + myGraph->markModified(theOccurrence); +} + +//================================================================================================= + +void BRepGraph::EditorView::OccurrenceOps::SetChildDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChildDefId) +{ + theMut.Internal().ChildDefId = theChildDefId; +} + +//================================================================================================= + +void BRepGraph::EditorView::GenOps::SetChildRefOrientation(const BRepGraph_ChildRefId theChildRef, + const TopAbs_Orientation theOrientation) +{ + myGraph->myData->myIncStorage.ChangeChildRef(theChildRef).Orientation = theOrientation; + myGraph->markRefModified(theChildRef); +} + +//================================================================================================= + +void BRepGraph::EditorView::GenOps::SetChildRefOrientation( + BRepGraph_MutGuard& theMut, + const TopAbs_Orientation theOrientation) +{ + theMut.Internal().Orientation = theOrientation; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetSurface(const BRepGraph_SurfaceRepId theRep, + const occ::handle& theSurface) +{ + myGraph->myData->myIncStorage.ChangeSurfaceRep(theRep).Surface = theSurface; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetSurface(BRepGraph_MutGuard& theMut, + const occ::handle& theSurface) +{ + theMut.Internal().Surface = theSurface; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetCurve3D(const BRepGraph_Curve3DRepId theRep, + const occ::handle& theCurve) +{ + myGraph->myData->myIncStorage.ChangeCurve3DRep(theRep).Curve = theCurve; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetCurve3D(BRepGraph_MutGuard& theMut, + const occ::handle& theCurve) +{ + theMut.Internal().Curve = theCurve; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetCurve2D(const BRepGraph_Curve2DRepId theRep, + const occ::handle& theCurve) +{ + myGraph->myData->myIncStorage.ChangeCurve2DRep(theRep).Curve = theCurve; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetCurve2D(BRepGraph_MutGuard& theMut, + const occ::handle& theCurve) +{ + theMut.Internal().Curve = theCurve; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetTriangulation(const BRepGraph_TriangulationRepId theRep, + const occ::handle& theTri) +{ + myGraph->myData->myIncStorage.ChangeTriangulationRep(theRep).Triangulation = theTri; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetTriangulation( + BRepGraph_MutGuard& theMut, + const occ::handle& theTri) +{ + theMut.Internal().Triangulation = theTri; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygon3D(const BRepGraph_Polygon3DRepId theRep, + const occ::handle& thePolygon) +{ + myGraph->myData->myIncStorage.ChangePolygon3DRep(theRep).Polygon = thePolygon; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygon3D( + BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon) +{ + theMut.Internal().Polygon = thePolygon; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygon2D(const BRepGraph_Polygon2DRepId theRep, + const occ::handle& thePolygon) +{ + myGraph->myData->myIncStorage.ChangePolygon2DRep(theRep).Polygon = thePolygon; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygon2D( + BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon) +{ + theMut.Internal().Polygon = thePolygon; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygonOnTri( + const BRepGraph_PolygonOnTriRepId theRep, + const occ::handle& thePolygon) +{ + myGraph->myData->myIncStorage.ChangePolygonOnTriRep(theRep).Polygon = thePolygon; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygonOnTri( + BRepGraph_MutGuard& theMut, + const occ::handle& thePolygon) +{ + theMut.Internal().Polygon = thePolygon; +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygonOnTriTriangulationId( + const BRepGraph_PolygonOnTriRepId theRep, + const BRepGraph_TriangulationRepId theTriRep) +{ + myGraph->myData->myIncStorage.ChangePolygonOnTriRep(theRep).TriangulationRepId = theTriRep; + myGraph->markRepModified(theRep); +} + +//================================================================================================= + +void BRepGraph::EditorView::RepOps::SetPolygonOnTriTriangulationId( + BRepGraph_MutGuard& theMut, + const BRepGraph_TriangulationRepId theTriRep) +{ + theMut.Internal().TriangulationRepId = theTriRep; +} + +//================================================================================================= + +namespace +{ +BRepGraph_VertexId vertexFromRef(const BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexRefId theRef) +{ + if (!theRef.IsValid(theStorage.NbVertexRefs())) + return BRepGraph_VertexId(); + return theStorage.VertexRef(theRef).VertexDefId; +} + +bool isLastVertexUsageOnEdge(const BRepGraphInc_Storage& theStorage, + const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexId theVtx, + const BRepGraph_VertexRefId theExcludingRef) +{ + if (!theEdge.IsValid(theStorage.NbEdges()) || !theVtx.IsValid()) + return true; + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(theEdge); + auto refResolvesTo = [&](const BRepGraph_VertexRefId aRefId) -> bool { + if (!aRefId.IsValid(theStorage.NbVertexRefs()) || aRefId == theExcludingRef) + return false; + const BRepGraphInc::VertexRef& aRef = theStorage.VertexRef(aRefId); + return !aRef.IsRemoved && aRef.VertexDefId == theVtx; + }; + if (refResolvesTo(anEdge.StartVertexRefId)) + return false; + if (refResolvesTo(anEdge.EndVertexRefId)) + return false; + for (const BRepGraph_VertexRefId& anIntRefId : anEdge.InternalVertexRefIds) + { + if (refResolvesTo(anIntRefId)) + return false; + } + return true; +} + +void rebindVertexEdge(BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexId theOldVtx, + const BRepGraph_VertexId theNewVtx, + const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theExcludingRef) +{ + if (theOldVtx == theNewVtx) + return; + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldVtx.IsValid() + && isLastVertexUsageOnEdge(theStorage, theEdge, theOldVtx, theExcludingRef)) + { + aRI.UnbindVertexFromEdge(theOldVtx, theEdge); + } + if (theNewVtx.IsValid()) + aRI.BindVertexToEdge(theNewVtx, theEdge); +} +} // namespace + +void BRepGraph::EditorView::EdgeOps::SetStartVertexRefId(const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theVertexRef) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + if (anEdge.StartVertexRefId == theVertexRef) + return; + const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, anEdge.StartVertexRefId); + const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); + anEdge.StartVertexRefId = theVertexRef; + rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theEdge, BRepGraph_VertexRefId()); + myGraph->markModified(theEdge); +} + +void BRepGraph::EditorView::EdgeOps::SetStartVertexRefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_VertexRefId theVertexRef) +{ + if (theMut->StartVertexRefId == theVertexRef) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, theMut->StartVertexRefId); + const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); + theMut.Internal().StartVertexRefId = theVertexRef; + rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theMut.Id(), BRepGraph_VertexRefId()); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetEndVertexRefId(const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theVertexRef) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + if (anEdge.EndVertexRefId == theVertexRef) + return; + const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, anEdge.EndVertexRefId); + const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); + anEdge.EndVertexRefId = theVertexRef; + rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theEdge, BRepGraph_VertexRefId()); + myGraph->markModified(theEdge); +} + +void BRepGraph::EditorView::EdgeOps::SetEndVertexRefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_VertexRefId theVertexRef) +{ + if (theMut->EndVertexRefId == theVertexRef) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, theMut->EndVertexRefId); + const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); + theMut.Internal().EndVertexRefId = theVertexRef; + rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theMut.Id(), BRepGraph_VertexRefId()); +} + +//================================================================================================= + +namespace +{ +//! True if no active CoEdge in theWire (other than theExcluding) references theEdge. +bool isLastCoEdgeOfEdgeInWire(const BRepGraphInc_Storage& theStorage, + const BRepGraph_EdgeId theEdge, + const BRepGraph_WireId theWire, + const BRepGraph_CoEdgeId theExcluding) +{ + if (!theWire.IsValid(theStorage.NbWires())) + return true; + const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); + for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) + { + if (!aRefId.IsValid(theStorage.NbCoEdgeRefs())) + continue; + const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); + if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) + continue; + if (aRef.CoEdgeDefId == theExcluding) + continue; + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); + if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) + return false; + } + return true; +} + +//! Apply rev-index updates for CoEdge.EdgeDefId rewrite. +void rebindCoEdgeEdge(BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdge, + const BRepGraphInc::CoEdgeDef& theDef, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge) +{ + if (theOldEdge == theNewEdge) + return; + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldEdge.IsValid()) + aRI.UnbindEdgeFromCoEdge(theOldEdge, theCoEdge); + if (theNewEdge.IsValid()) + aRI.BindEdgeToCoEdge(theNewEdge, theCoEdge); + // Wires that own this coedge: rebind edge-to-wire only when no other coedge of the + // same wire still references the old edge. + const NCollection_DynamicArray* aWires = aRI.WiresOfCoEdge(theCoEdge); + if (aWires != nullptr) + { + for (const BRepGraph_WireId& aWireId : *aWires) + { + if (theOldEdge.IsValid() + && isLastCoEdgeOfEdgeInWire(theStorage, theOldEdge, aWireId, theCoEdge)) + { + aRI.UnbindEdgeFromWire(theOldEdge, aWireId); + } + if (theNewEdge.IsValid()) + aRI.BindEdgeToWire(theNewEdge, aWireId); + } + } + // Face binding follows the (edge, face) pair; if the coedge has a face, rebind. + if (theDef.FaceDefId.IsValid()) + { + if (theOldEdge.IsValid() + && isLastCoEdgeOfEdgeOnFace(theStorage, theOldEdge, theDef.FaceDefId, theCoEdge)) + { + aRI.UnbindEdgeFromFace(theOldEdge, theDef.FaceDefId); + } + if (theNewEdge.IsValid()) + aRI.BindEdgeToFace(theNewEdge, theDef.FaceDefId); + } +} + +//! Apply rev-index updates for CoEdge.FaceDefId rewrite. +void rebindCoEdgeFace(BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theOldFace, + const BRepGraph_FaceId theNewFace) +{ + if (theOldFace == theNewFace || !theEdge.IsValid()) + return; + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldFace.IsValid() && isLastCoEdgeOfEdgeOnFace(theStorage, theEdge, theOldFace, theCoEdge)) + { + aRI.UnbindEdgeFromFace(theEdge, theOldFace); + } + if (theNewFace.IsValid()) + aRI.BindEdgeToFace(theEdge, theNewFace); +} +} // namespace + +void BRepGraph::EditorView::CoEdgeOps::SetEdgeDefId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theEdge) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + if (aDef.EdgeDefId == theEdge) + return; + const BRepGraph_EdgeId anOldEdge = aDef.EdgeDefId; + aDef.EdgeDefId = theEdge; + rebindCoEdgeEdge(aStorage, theCoEdge, aDef, anOldEdge, theEdge); + myGraph->markModified(theCoEdge); +} + +void BRepGraph::EditorView::CoEdgeOps::SetEdgeDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_EdgeId theEdge) +{ + if (theMut->EdgeDefId == theEdge) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_EdgeId anOldEdge = theMut->EdgeDefId; + theMut.Internal().EdgeDefId = theEdge; + rebindCoEdgeEdge(aStorage, theMut.Id(), *theMut, anOldEdge, theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetFaceDefId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_FaceId theFace) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + if (aDef.FaceDefId == theFace) + return; + const BRepGraph_FaceId anOldFace = aDef.FaceDefId; + const BRepGraph_EdgeId anEdge = aDef.EdgeDefId; + aDef.FaceDefId = theFace; + rebindCoEdgeFace(aStorage, theCoEdge, anEdge, anOldFace, theFace); + myGraph->markModified(theCoEdge); +} + +void BRepGraph::EditorView::CoEdgeOps::SetFaceDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace) +{ + if (theMut->FaceDefId == theFace) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_FaceId anOldFace = theMut->FaceDefId; + const BRepGraph_EdgeId anEdge = theMut->EdgeDefId; + theMut.Internal().FaceDefId = theFace; + rebindCoEdgeFace(aStorage, theMut.Id(), anEdge, anOldFace, theFace); +} + +//================================================================================================= + +namespace +{ +void rebindVertexRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_VertexId theOldVtx, + const BRepGraph_VertexId theNewVtx, + const BRepGraph_VertexRefId theMutatedRef) +{ + if (theOldVtx == theNewVtx) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Edge) + return; + const BRepGraph_EdgeId anEdge(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldVtx.IsValid() && isLastVertexUsageOnEdge(theStorage, anEdge, theOldVtx, theMutatedRef)) + { + aRI.UnbindVertexFromEdge(theOldVtx, anEdge); + } + if (theNewVtx.IsValid()) + aRI.BindVertexToEdge(theNewVtx, anEdge); +} +} // namespace + +void BRepGraph::EditorView::VertexOps::SetRefVertexDefId(const BRepGraph_VertexRefId theVertexRef, + const BRepGraph_VertexId theVertex) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::VertexRef& aRef = aStorage.ChangeVertexRef(theVertexRef); + if (aRef.VertexDefId == theVertex) + return; + const BRepGraph_VertexId anOldVtx = aRef.VertexDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.VertexDefId = theVertex; + rebindVertexRef(aStorage, aParent, anOldVtx, theVertex, theVertexRef); + myGraph->markRefModified(theVertexRef); +} + +void BRepGraph::EditorView::VertexOps::SetRefVertexDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_VertexId theVertex) +{ + if (theMut->VertexDefId == theVertex) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_VertexId anOldVtx = theMut->VertexDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().VertexDefId = theVertex; + rebindVertexRef(aStorage, aParent, anOldVtx, theVertex, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +bool isLastWireUsageOnFace(const BRepGraphInc_Storage& theStorage, + const BRepGraph_FaceId theFace, + const BRepGraph_WireId theWire, + const BRepGraph_WireRefId theExcludingRef) +{ + if (!theFace.IsValid(theStorage.NbFaces()) || !theWire.IsValid()) + return true; + const BRepGraphInc::FaceDef& aFaceDef = theStorage.Face(theFace); + for (const BRepGraph_WireRefId& aRefId : aFaceDef.WireRefIds) + { + if (!aRefId.IsValid(theStorage.NbWireRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::WireRef& aRef = theStorage.WireRef(aRefId); + if (!aRef.IsRemoved && aRef.WireDefId == theWire) + return false; + } + return true; +} + +void rebindWireRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_WireId theOldWire, + const BRepGraph_WireId theNewWire, + const BRepGraph_WireRefId theMutatedRef) +{ + if (theOldWire == theNewWire) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Face) + return; + const BRepGraph_FaceId aFace(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldWire.IsValid() && isLastWireUsageOnFace(theStorage, aFace, theOldWire, theMutatedRef)) + { + aRI.UnbindWireFromFace(theOldWire, aFace); + } + if (theNewWire.IsValid()) + aRI.BindWireToFace(theNewWire, aFace); +} +} // namespace + +void BRepGraph::EditorView::WireOps::SetRefWireDefId(const BRepGraph_WireRefId theWireRef, + const BRepGraph_WireId theWire) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::WireRef& aRef = aStorage.ChangeWireRef(theWireRef); + if (aRef.WireDefId == theWire) + return; + const BRepGraph_WireId anOldWire = aRef.WireDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.WireDefId = theWire; + rebindWireRef(aStorage, aParent, anOldWire, theWire, theWireRef); + myGraph->markRefModified(theWireRef); +} + +void BRepGraph::EditorView::WireOps::SetRefWireDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_WireId theWire) +{ + if (theMut->WireDefId == theWire) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_WireId anOldWire = theMut->WireDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().WireDefId = theWire; + rebindWireRef(aStorage, aParent, anOldWire, theWire, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +bool isLastFaceUsageOnShell(const BRepGraphInc_Storage& theStorage, + const BRepGraph_ShellId theShell, + const BRepGraph_FaceId theFace, + const BRepGraph_FaceRefId theExcludingRef) +{ + if (!theShell.IsValid(theStorage.NbShells()) || !theFace.IsValid()) + return true; + const BRepGraphInc::ShellDef& aShellDef = theStorage.Shell(theShell); + for (const BRepGraph_FaceRefId& aRefId : aShellDef.FaceRefIds) + { + if (!aRefId.IsValid(theStorage.NbFaceRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::FaceRef& aRef = theStorage.FaceRef(aRefId); + if (!aRef.IsRemoved && aRef.FaceDefId == theFace) + return false; + } + return true; +} + +void rebindFaceRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_FaceId theOldFace, + const BRepGraph_FaceId theNewFace, + const BRepGraph_FaceRefId theMutatedRef) +{ + if (theOldFace == theNewFace) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Shell) + return; + const BRepGraph_ShellId aShell(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldFace.IsValid() && isLastFaceUsageOnShell(theStorage, aShell, theOldFace, theMutatedRef)) + { + aRI.UnbindFaceFromShell(theOldFace, aShell); + } + if (theNewFace.IsValid()) + aRI.BindFaceToShell(theNewFace, aShell); +} +} // namespace + +void BRepGraph::EditorView::FaceOps::SetRefFaceDefId(const BRepGraph_FaceRefId theFaceRef, + const BRepGraph_FaceId theFace) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::FaceRef& aRef = aStorage.ChangeFaceRef(theFaceRef); + if (aRef.FaceDefId == theFace) + return; + const BRepGraph_FaceId anOldFace = aRef.FaceDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.FaceDefId = theFace; + rebindFaceRef(aStorage, aParent, anOldFace, theFace, theFaceRef); + myGraph->markRefModified(theFaceRef); +} + +void BRepGraph::EditorView::FaceOps::SetRefFaceDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace) +{ + if (theMut->FaceDefId == theFace) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_FaceId anOldFace = theMut->FaceDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().FaceDefId = theFace; + rebindFaceRef(aStorage, aParent, anOldFace, theFace, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +bool isLastShellUsageOnSolid(const BRepGraphInc_Storage& theStorage, + const BRepGraph_SolidId theSolid, + const BRepGraph_ShellId theShell, + const BRepGraph_ShellRefId theExcludingRef) +{ + if (!theSolid.IsValid(theStorage.NbSolids()) || !theShell.IsValid()) + return true; + const BRepGraphInc::SolidDef& aSolidDef = theStorage.Solid(theSolid); + for (const BRepGraph_ShellRefId& aRefId : aSolidDef.ShellRefIds) + { + if (!aRefId.IsValid(theStorage.NbShellRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::ShellRef& aRef = theStorage.ShellRef(aRefId); + if (!aRef.IsRemoved && aRef.ShellDefId == theShell) + return false; + } + return true; +} + +void rebindShellRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_ShellId theOldShell, + const BRepGraph_ShellId theNewShell, + const BRepGraph_ShellRefId theMutatedRef) +{ + if (theOldShell == theNewShell) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Solid) + return; + const BRepGraph_SolidId aSolid(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldShell.IsValid() + && isLastShellUsageOnSolid(theStorage, aSolid, theOldShell, theMutatedRef)) + { + aRI.UnbindShellFromSolid(theOldShell, aSolid); + } + if (theNewShell.IsValid()) + aRI.BindShellToSolid(theNewShell, aSolid); +} +} // namespace + +void BRepGraph::EditorView::ShellOps::SetRefShellDefId(const BRepGraph_ShellRefId theShellRef, + const BRepGraph_ShellId theShell) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::ShellRef& aRef = aStorage.ChangeShellRef(theShellRef); + if (aRef.ShellDefId == theShell) + return; + const BRepGraph_ShellId anOldShell = aRef.ShellDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.ShellDefId = theShell; + rebindShellRef(aStorage, aParent, anOldShell, theShell, theShellRef); + myGraph->markRefModified(theShellRef); +} + +void BRepGraph::EditorView::ShellOps::SetRefShellDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_ShellId theShell) +{ + if (theMut->ShellDefId == theShell) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_ShellId anOldShell = theMut->ShellDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().ShellDefId = theShell; + rebindShellRef(aStorage, aParent, anOldShell, theShell, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +bool isLastSolidUsageOnCompSolid(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CompSolidId theCompSolid, + const BRepGraph_SolidId theSolid, + const BRepGraph_SolidRefId theExcludingRef) +{ + if (!theCompSolid.IsValid(theStorage.NbCompSolids()) || !theSolid.IsValid()) + return true; + const BRepGraphInc::CompSolidDef& aCSDef = theStorage.CompSolid(theCompSolid); + for (const BRepGraph_SolidRefId& aRefId : aCSDef.SolidRefIds) + { + if (!aRefId.IsValid(theStorage.NbSolidRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::SolidRef& aRef = theStorage.SolidRef(aRefId); + if (!aRef.IsRemoved && aRef.SolidDefId == theSolid) + return false; + } + return true; +} + +void rebindSolidRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_SolidId theOldSolid, + const BRepGraph_SolidId theNewSolid, + const BRepGraph_SolidRefId theMutatedRef) +{ + if (theOldSolid == theNewSolid) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::CompSolid) + return; + const BRepGraph_CompSolidId aCompSolid(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldSolid.IsValid() + && isLastSolidUsageOnCompSolid(theStorage, aCompSolid, theOldSolid, theMutatedRef)) + { + aRI.UnbindSolidFromCompSolid(theOldSolid, aCompSolid); + } + if (theNewSolid.IsValid()) + aRI.BindSolidToCompSolid(theNewSolid, aCompSolid); +} +} // namespace + +void BRepGraph::EditorView::SolidOps::SetRefSolidDefId(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theSolid) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::SolidRef& aRef = aStorage.ChangeSolidRef(theSolidRef); + if (aRef.SolidDefId == theSolid) + return; + const BRepGraph_SolidId anOldSolid = aRef.SolidDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.SolidDefId = theSolid; + rebindSolidRef(aStorage, aParent, anOldSolid, theSolid, theSolidRef); + myGraph->markRefModified(theSolidRef); +} + +void BRepGraph::EditorView::SolidOps::SetRefSolidDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_SolidId theSolid) +{ + if (theMut->SolidDefId == theSolid) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_SolidId anOldSolid = theMut->SolidDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().SolidDefId = theSolid; + rebindSolidRef(aStorage, aParent, anOldSolid, theSolid, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +//! Resolve EdgeDefId reachable through a CoEdgeId (via CoEdgeDef.EdgeDefId). +BRepGraph_EdgeId edgeFromCoEdge(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdge) +{ + if (!theCoEdge.IsValid(theStorage.NbCoEdges())) + return BRepGraph_EdgeId(); + return theStorage.CoEdge(theCoEdge).EdgeDefId; +} + +//! True if no active CoEdgeRef in theWire references theEdge except theExcluding. +bool isLastCoEdgeRefOfEdgeInWire(const BRepGraphInc_Storage& theStorage, + const BRepGraph_EdgeId theEdge, + const BRepGraph_WireId theWire, + const BRepGraph_CoEdgeRefId theExcluding) +{ + if (!theWire.IsValid(theStorage.NbWires())) + return true; + const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); + for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) + { + if (aRefId == theExcluding) + continue; + if (!aRefId.IsValid(theStorage.NbCoEdgeRefs())) + continue; + const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); + if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) + continue; + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); + if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) + return false; + } + return true; +} + +void rebindCoEdgeRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeRefId theRefId, + const BRepGraph_NodeId theParent, + const BRepGraph_CoEdgeId theOldCoEdge, + const BRepGraph_CoEdgeId theNewCoEdge) +{ + if (theOldCoEdge == theNewCoEdge) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Wire) + return; + const BRepGraph_WireId aWire(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldCoEdge.IsValid()) + aRI.UnbindCoEdgeFromWire(theOldCoEdge, aWire); + if (theNewCoEdge.IsValid()) + aRI.BindCoEdgeToWire(theNewCoEdge, aWire); + // EdgeToWires is derived from coedge.EdgeDefId x wire (deduplicated). Unbind only + // when no other CoEdgeRef in the wire still references the old edge. + const BRepGraph_EdgeId anOldEdge = edgeFromCoEdge(theStorage, theOldCoEdge); + const BRepGraph_EdgeId aNewEdge = edgeFromCoEdge(theStorage, theNewCoEdge); + if (anOldEdge != aNewEdge) + { + if (anOldEdge.IsValid() && isLastCoEdgeRefOfEdgeInWire(theStorage, anOldEdge, aWire, theRefId)) + { + aRI.UnbindEdgeFromWire(anOldEdge, aWire); + } + if (aNewEdge.IsValid()) + aRI.BindEdgeToWire(aNewEdge, aWire); + } +} +} // namespace + +void BRepGraph::EditorView::CoEdgeOps::SetRefCoEdgeDefId(const BRepGraph_CoEdgeRefId theCoEdgeRef, + const BRepGraph_CoEdgeId theCoEdge) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeRef& aRef = aStorage.ChangeCoEdgeRef(theCoEdgeRef); + if (aRef.CoEdgeDefId == theCoEdge) + return; + const BRepGraph_CoEdgeId anOldCoEdge = aRef.CoEdgeDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.CoEdgeDefId = theCoEdge; + rebindCoEdgeRef(aStorage, theCoEdgeRef, aParent, anOldCoEdge, theCoEdge); + myGraph->markRefModified(theCoEdgeRef); +} + +void BRepGraph::EditorView::CoEdgeOps::SetRefCoEdgeDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_CoEdgeId theCoEdge) +{ + if (theMut->CoEdgeDefId == theCoEdge) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_CoEdgeId anOldCoEdge = theMut->CoEdgeDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().CoEdgeDefId = theCoEdge; + rebindCoEdgeRef(aStorage, theMut.Id(), aParent, anOldCoEdge, theCoEdge); +} + +//================================================================================================= + +namespace +{ +bool isLastChildUsageOnCompound(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CompoundId theCompound, + const BRepGraph_NodeId theChild, + const BRepGraph_ChildRefId theExcludingRef) +{ + if (!theCompound.IsValid(theStorage.NbCompounds()) || !theChild.IsValid()) + return true; + const BRepGraphInc::CompoundDef& aCompDef = theStorage.Compound(theCompound); + for (const BRepGraph_ChildRefId& aRefId : aCompDef.ChildRefIds) + { + if (!aRefId.IsValid(theStorage.NbChildRefs()) || aRefId == theExcludingRef) + continue; + const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aRefId); + if (!aRef.IsRemoved && aRef.ChildDefId == theChild) + return false; + } + return true; +} + +void rebindChildRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild, + const BRepGraph_ChildRefId theMutatedRef) +{ + if (theOldChild == theNewChild) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Compound) + return; + const BRepGraph_CompoundId aCompound(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldChild.IsValid() + && isLastChildUsageOnCompound(theStorage, aCompound, theOldChild, theMutatedRef)) + { + aRI.UnbindCompoundChild(theOldChild, aCompound); + } + if (theNewChild.IsValid()) + aRI.BindCompoundChild(theNewChild, aCompound); +} +} // namespace + +void BRepGraph::EditorView::GenOps::SetChildRefChildDefId(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theChild) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::ChildRef& aRef = aStorage.ChangeChildRef(theChildRef); + if (aRef.ChildDefId == theChild) + return; + const BRepGraph_NodeId anOldChild = aRef.ChildDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.ChildDefId = theChild; + rebindChildRef(aStorage, aParent, anOldChild, theChild, theChildRef); + myGraph->markRefModified(theChildRef); +} + +void BRepGraph::EditorView::GenOps::SetChildRefChildDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChild) +{ + if (theMut->ChildDefId == theChild) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_NodeId anOldChild = theMut->ChildDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().ChildDefId = theChild; + rebindChildRef(aStorage, aParent, anOldChild, theChild, theMut.Id()); +} + +//================================================================================================= + +namespace +{ +void rebindOccurrenceRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParent, + const BRepGraph_OccurrenceId theOldOcc, + const BRepGraph_OccurrenceId theNewOcc) +{ + if (theOldOcc == theNewOcc) + return; + if (theParent.NodeKind != BRepGraph_NodeId::Kind::Product) + return; + const BRepGraph_ProductId aProduct(theParent); + BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); + if (theOldOcc.IsValid()) + aRI.UnbindProductOccurrence(theOldOcc, aProduct); + if (theNewOcc.IsValid()) + aRI.BindProductOccurrence(theNewOcc, aProduct); +} +} // namespace + +void BRepGraph::EditorView::OccurrenceOps::SetRefOccurrenceDefId( + const BRepGraph_OccurrenceRefId theOccurrenceRef, + const BRepGraph_OccurrenceId theOccurrence) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::OccurrenceRef& aRef = aStorage.ChangeOccurrenceRef(theOccurrenceRef); + if (aRef.OccurrenceDefId == theOccurrence) + return; + const BRepGraph_OccurrenceId anOldOcc = aRef.OccurrenceDefId; + const BRepGraph_NodeId aParent = aRef.ParentId; + aRef.OccurrenceDefId = theOccurrence; + rebindOccurrenceRef(aStorage, aParent, anOldOcc, theOccurrence); + myGraph->markRefModified(theOccurrenceRef); +} + +void BRepGraph::EditorView::OccurrenceOps::SetRefOccurrenceDefId( + BRepGraph_MutGuard& theMut, + const BRepGraph_OccurrenceId theOccurrence) +{ + if (theMut->OccurrenceDefId == theOccurrence) + return; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_OccurrenceId anOldOcc = theMut->OccurrenceDefId; + const BRepGraph_NodeId aParent = theMut->ParentId; + theMut.Internal().OccurrenceDefId = theOccurrence; + rebindOccurrenceRef(aStorage, aParent, anOldOcc, theOccurrence); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx index e522d46a40..8857bddf89 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx @@ -23,32 +23,27 @@ class BRepGraph; -//! @brief RAII guard wrapping a mutable topology definition or reference entry. +//! @brief RAII scope token batching mutation notifications for a single entity. //! -//! Obtained via BRepGraph::Editor().MutEdge(), MutVertex(), MutFaceRef(), etc. -//! Provides operator-> / operator* for direct field access. -//! Calls the appropriate markModified() or markRefModified() exactly once -//! on scope exit (destruction), regardless of how many fields were modified. +//! Obtained via BRepGraph::Editor().().Mut() / MutRef() / MutSurface() etc. +//! Reads via `operator->()` / `operator*()`; writes via Editor's typed setters +//! (or `Internal()` for in-tree structural remaps). Any call to `Internal()` +//! flags the guard dirty and the destructor fires `markModified` / +//! `markRefModified` / `markRepModified` once on scope exit. //! -//! Move-only; non-copyable. After a move, the source guard -//! becomes inert and will not trigger notification. +//! Move-only; non-copyable. After a move, the source guard becomes inert. //! //! Compile-time dispatch selects the ID type and notification method: //! - For types derived from BRepGraphInc::BaseDef: BRepGraph_NodeId + markModified() //! - For types derived from BRepGraphInc::BaseRef: BRepGraph_RefId + markRefModified() //! - For types derived from BRepGraphInc::BaseRep: BRepGraph_RepId + markRepModified() //! -//! @warning Guarded access is scoped mutation access, not a general transaction. -//! Callers should not mix `Mut*()` and structural `Add*()` / `Remove*()` edits in -//! the same logical mutation step, and parallel mutation batches should use -//! `BRepGraph_DeferredScope`. -//! //! @code //! { //! BRepGraph_MutGuard anEdge = -//! theGraph.Editor().MutEdge(BRepGraph_EdgeId(42)); -//! anEdge->Tolerance = 0.5; -//! anEdge->SameParameter = true; +//! theGraph.Editor().Edges().Mut(BRepGraph_EdgeId(42)); +//! theGraph.Editor().Edges().SetTolerance (anEdge, 0.5); +//! theGraph.Editor().Edges().SetSameParameter (anEdge, true); //! } // markModified called once here //! @endcode template @@ -87,41 +82,45 @@ public: BRepGraph_MutGuard(BRepGraph* theGraph, T* theEntity, const TypeId theId) : myGraph(theGraph), myEntity(theEntity), - myId(theId) + myId(theId), + myDirty(false) { } - //! Destructor: notifies the graph if the guard still owns the reference. + //! Destructor: notifies the graph if the guard owns an entity AND + //! at least one setter (or `MarkDirty`) flagged it modified. ~BRepGraph_MutGuard() { - if (myGraph != nullptr) + if (myGraph != nullptr && myDirty) { notify(); } } - //! Move constructor: transfers ownership; source becomes inert. BRepGraph_MutGuard(BRepGraph_MutGuard&& theOther) noexcept : myGraph(theOther.myGraph), myEntity(theOther.myEntity), - myId(theOther.myId) + myId(theOther.myId), + myDirty(theOther.myDirty) { theOther.myGraph = nullptr; theOther.myEntity = nullptr; + theOther.myDirty = false; } - //! Move assignment: flushes current guard, then transfers ownership. BRepGraph_MutGuard& operator=(BRepGraph_MutGuard&& theOther) noexcept { if (this != &theOther) { - if (myGraph != nullptr) + if (myGraph != nullptr && myDirty) notify(); myGraph = theOther.myGraph; myEntity = theOther.myEntity; myId = theOther.myId; + myDirty = theOther.myDirty; theOther.myGraph = nullptr; theOther.myEntity = nullptr; + theOther.myDirty = false; } return *this; } @@ -130,12 +129,12 @@ public: BRepGraph_MutGuard& operator=(const BRepGraph_MutGuard&) = delete; //! True when the guard still owns an entity; false after a move or when - //! constructed in an inert state. Callers that want to branch on the guard - //! state without triggering the operator-> null-check should use this. + //! constructed in an inert state. [[nodiscard]] explicit operator bool() const noexcept { return myEntity != nullptr; } - //! Access the entity via pointer syntax. - [[nodiscard]] T* operator->() + //! Read-only access via pointer syntax. Field writes go through the + //! Editor's typed setters. + [[nodiscard]] const T* operator->() const { Standard_ProgramError_Raise_if( myEntity == nullptr, @@ -143,18 +142,41 @@ public: return myEntity; } - //! Dereference to the entity. - [[nodiscard]] T& operator*() + //! Read-only dereference. + [[nodiscard]] const T& operator*() const { Standard_ProgramError_Raise_if(myEntity == nullptr, "BRepGraph_MutGuard::operator*(): guard is empty or moved-from"); return *myEntity; } + //! Identity for notification. + [[nodiscard]] TypeId Id() const noexcept { return myId; } + + //! Owning graph pointer (nullptr after move). + [[nodiscard]] BRepGraph* Graph() const noexcept { return myGraph; } + + //! Flag the guarded entity as modified without writing through `Internal()`. + //! Use when an external mutation (e.g. in-place geometry transform on a shared + //! Geom handle) is not visible to the guard. + void MarkDirty() noexcept { myDirty = true; } + + //! True if `Internal()` or `MarkDirty()` flagged the entity modified. + [[nodiscard]] bool IsDirty() const noexcept { return myDirty; } + + //! INTERNAL USE ONLY. Mutable accessor; auto-flags dirty. External code MUST go + //! through Editor's typed setters. Use `operator->()` / `operator*()` for reads. + [[nodiscard]] T& Internal() noexcept + { + myDirty = true; + return *myEntity; + } + private: BRepGraph* myGraph; //!< Owning graph (nullptr after move). - T* myEntity; //!< Mutable entity pointer. + T* myEntity; //!< Mutable entity pointer; access via Editor setters. TypeId myId; //!< Identity for notification. + bool myDirty; //!< True once a setter has modified the entity in this scope. }; #endif // _BRepGraph_MutGuard_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx index 5caad7908b..6c5c05dd8e 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx @@ -22,8 +22,16 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include namespace { @@ -42,16 +50,263 @@ void forEachRootProduct(BRepGraph& theGraph, ApplyProductFn&& theApplyProduct) } } +//! Copy and transform a Poly_Triangulation: nodes, UV nodes, normals, deflection. +//! For negative-scale transforms the triangle winding is flipped. +//! @param[in,out] theTri triangulation to update in-place (should already be a copy) +//! @param[in] theTrsf transformation to apply +//! @param[in] theSurf optional surface for UV reparametrization (may be null) +void transformTriangulation(const occ::handle& theTri, + const gp_Trsf& theTrsf, + const occ::handle& theSurf) +{ + const double aScale = std::abs(theTrsf.ScaleFactor()); + theTri->Deflection(theTri->Deflection() * aScale); + + for (int i = 1; i <= theTri->NbNodes(); ++i) + { + gp_Pnt aP = theTri->Node(i); + aP.Transform(theTrsf); + theTri->SetNode(i, aP); + } + + if (theTri->HasUVNodes() && !theSurf.IsNull()) + { + const gp_GTrsf2d aUVTrsf = theSurf->ParametricTransformation(theTrsf); + if (aUVTrsf.Form() != gp_Identity) + { + for (int i = 1; i <= theTri->NbNodes(); ++i) + { + gp_Pnt2d aUV = theTri->UVNode(i); + aUVTrsf.Transforms(aUV.ChangeCoord()); + theTri->SetUVNode(i, aUV); + } + } + } + + if (theTri->HasNormals()) + { + for (int i = 1; i <= theTri->NbNodes(); ++i) + { + gp_Dir aN = theTri->Normal(i); + aN.Transform(theTrsf); + theTri->SetNormal(i, aN); + } + } + + // For negative-scale transforms, flip triangle winding to preserve outward normals. + if (theTrsf.IsNegative()) + { + for (int i = 1; i <= theTri->NbTriangles(); ++i) + { + int n1, n2, n3; + theTri->Triangle(i).Get(n1, n2, n3); + theTri->SetTriangle(i, Poly_Triangle(n1, n3, n2)); + } + } +} + +//! Copy and transform a Poly_Polygon3D: nodes and deflection. +//! @param[in,out] thePoly polygon to update in-place (should already be a copy) +//! @param[in] theTrsf transformation to apply +void transformPolygon3D(const occ::handle& thePoly, const gp_Trsf& theTrsf) +{ + thePoly->Deflection(thePoly->Deflection() * std::abs(theTrsf.ScaleFactor())); + NCollection_Array1& aNodes = thePoly->ChangeNodes(); + for (int i = aNodes.Lower(); i <= aNodes.Upper(); ++i) + aNodes.ChangeValue(i).Transform(theTrsf); +} + +//! Copy mesh representations from theSource into theDest, optionally transforming them. +//! +//! Handles all three mesh layers: +//! - Triangulations (persistent FaceDef entry + all cached LOD entries). +//! - Polygon3D reps (edge mesh cache). +//! - PolygonOnTriangulation reps (coedge mesh cache); TriangulationRepId references +//! are remapped to point to the newly created triangulation reps. +//! +//! @param[in] theSource source graph whose face IDs and mesh cache are iterated +//! @param[in] theDest destination graph (mesh storage is populated by this call) +//! @param[in] theTrsf transformation to apply (ignored when !theDoTransform) +//! @param[in] theDoTransform true: copy + transform; false: copy only (location-only mode) +//! @param[in] thePolySource graph whose poly storage is used for TriangulationRep lookups; +//! when nullptr, theSource is used. Pass the original source graph +//! when theSource is a CopyNode result whose poly storage is empty +//! yet its FaceMeshEntry TriangulationRepIds still reference the +//! original graph's poly storage. +//! @param[in] theSourceCache raw mesh cache storage to read LOD entries from, bypassing the +//! OwnGen freshness check. Use when theSource's face OwnGens may +//! have been bumped since the LOD entries were stored (e.g. by +//! Mut guards in the geometry-transform pass that runs before this +//! call). When nullptr, theSource.Mesh().Faces().CachedMesh() is +//! used (includes the freshness check). +void applyMeshCopy(const BRepGraph& theSource, + BRepGraph& theDest, + const gp_Trsf& theTrsf, + const bool theDoTransform, + const BRepGraph* thePolySource = nullptr, + const BRepGraph_MeshCacheStorage* theSourceCache = nullptr) +{ + const BRepGraph::MeshView::PolyOps& aSrcPoly = + thePolySource != nullptr ? thePolySource->Mesh().Poly() : theSource.Mesh().Poly(); + + // Old TriangulationRepId.Index -> new TriangulationRepId in theDest storage. + NCollection_DataMap aTriRepMap; + + // -- Triangulations (faces) -- + for (BRepGraph_FaceIterator aFaceIt(theSource); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + + // Snapshot LOD cache entries. When theSourceCache is provided it is used directly + // (bypassing the OwnGen freshness check) because the geometry-transform Mut guards + // may have bumped the face OwnGen since the entries were stored, making them appear + // stale to CachedMesh() even though the triangulation data is still intact. + // The snapshot is also required for the self-aliasing case (theSource == theDest): + // ClearFaceCache below would invalidate a raw pointer into the same storage. + NCollection_DynamicArray aSrcLODs; + int aSrcActiveIdx = -1; + { + const BRepGraph_MeshCache::FaceMeshEntry* aSrcEntry = + theSourceCache != nullptr ? theSourceCache->FindFaceMesh(aFaceId) + : theSource.Mesh().Faces().CachedMesh(aFaceId); + if (aSrcEntry != nullptr && aSrcEntry->IsPresent()) + { + for (int i = 0; i < aSrcEntry->TriangulationRepIds.Length(); ++i) + aSrcLODs.Append(aSrcEntry->TriangulationRepIds.Value(i)); + aSrcActiveIdx = aSrcEntry->ActiveTriangulationIndex; + } + } + + BRepGraph_Tool::Mesh::ClearFaceCache(theDest, aFaceId); + + // Helper lambda: copy one triangulation from source, optionally transform, register in dest. + auto copyOneTri = + [&](const BRepGraph_TriangulationRepId aSrcRepId) -> BRepGraph_TriangulationRepId { + if (!aSrcRepId.IsValid(aSrcPoly.NbTriangulations())) + return BRepGraph_TriangulationRepId(); + if (const BRepGraph_TriangulationRepId* aExisting = aTriRepMap.Seek(aSrcRepId.Index)) + return *aExisting; + + const occ::handle& aSrcTri = + aSrcPoly.TriangulationRep(aSrcRepId).Triangulation; + if (aSrcTri.IsNull()) + return BRepGraph_TriangulationRepId(); + + occ::handle aNewTri = aSrcTri->Copy(); + if (theDoTransform) + { + const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theDest, aFaceId); + transformTriangulation(aNewTri, theTrsf, aSurf); + } + const BRepGraph_TriangulationRepId aNewRepId = + BRepGraph_Tool::Mesh::CreateTriangulationRep(theDest, aNewTri); + aTriRepMap.Bind(aSrcRepId.Index, aNewRepId); + return aNewRepId; + }; + + // Persistent triangulation stored on FaceDef (not in the cache; safe to read after clear). + const BRepGraph_TriangulationRepId aSrcPersistId = + theSource.Topo().Faces().Definition(aFaceId).TriangulationRepId; + const BRepGraph_TriangulationRepId aNewPersistId = copyOneTri(aSrcPersistId); + theDest.Editor().Faces().SetTriangulationRep(aFaceId, aNewPersistId); + + // Cached LOD entries (MeshLayer) - use the pre-clear snapshot. + for (int i = 0; i < aSrcLODs.Length(); ++i) + { + const BRepGraph_TriangulationRepId aNewRepId = copyOneTri(aSrcLODs.Value(i)); + if (aNewRepId.IsValid()) + BRepGraph_Tool::Mesh::AppendCachedTriangulation(theDest, aFaceId, aNewRepId); + } + if (aSrcActiveIdx >= 0) + BRepGraph_Tool::Mesh::SetCachedActiveIndex(theDest, aFaceId, aSrcActiveIdx); + } + + // -- Polygon3D (edges) -- + for (BRepGraph_EdgeIterator anEdgeIt(theSource); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + BRepGraph_Tool::Mesh::ClearEdgeCache(theDest, anEdgeId); + + const BRepGraph_MeshCache::EdgeMeshEntry* aSrcEdge = + theSource.Mesh().Edges().CachedMesh(anEdgeId); + if (aSrcEdge == nullptr || !aSrcEdge->IsPresent()) + continue; + + const BRepGraph_Polygon3DRepId aSrcPolyRepId = aSrcEdge->Polygon3DRepId; + if (!aSrcPolyRepId.IsValid(aSrcPoly.NbPolygons3D())) + continue; + + const occ::handle& aSrcPoly3D = aSrcPoly.Polygon3DRep(aSrcPolyRepId).Polygon; + if (aSrcPoly3D.IsNull()) + continue; + + occ::handle aNewPoly3D = aSrcPoly3D->Copy(); + if (theDoTransform) + transformPolygon3D(aNewPoly3D, theTrsf); + + const BRepGraph_Polygon3DRepId aNewRepId = + BRepGraph_Tool::Mesh::CreatePolygon3DRep(theDest, aNewPoly3D); + BRepGraph_Tool::Mesh::SetCachedPolygon3D(theDest, anEdgeId, aNewRepId); + } + + // -- PolygonOnTriangulation (coedges) -- + for (BRepGraph_CoEdgeIterator aCoEdgeIt(theSource); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + + const BRepGraph_MeshCache::CoEdgeMeshEntry* aSrcCoEdge = + theSource.Mesh().CoEdges().CachedMesh(aCoEdgeId); + if (aSrcCoEdge == nullptr || !aSrcCoEdge->IsPresent()) + continue; + + for (int i = 0; i < aSrcCoEdge->PolygonOnTriRepIds.Length(); ++i) + { + const BRepGraph_PolygonOnTriRepId aSrcRepId = aSrcCoEdge->PolygonOnTriRepIds.Value(i); + if (!aSrcRepId.IsValid(aSrcPoly.NbPolygonsOnTri())) + continue; + + const BRepGraphInc::PolygonOnTriRep& aSrcRep = aSrcPoly.PolygonOnTriRep(aSrcRepId); + if (aSrcRep.Polygon.IsNull()) + continue; + + occ::handle aNewPoly = aSrcRep.Polygon->Copy(); + if (theDoTransform) + aNewPoly->Deflection(aNewPoly->Deflection() * std::abs(theTrsf.ScaleFactor())); + + // Remap triangulation reference to the newly created dest rep. + BRepGraph_TriangulationRepId aNewTriRepId; + if (const BRepGraph_TriangulationRepId* aFound = + aTriRepMap.Seek(aSrcRep.TriangulationRepId.Index)) + aNewTriRepId = *aFound; + + const BRepGraph_PolygonOnTriRepId aNewRepId = + BRepGraph_Tool::Mesh::CreatePolygonOnTriRep(theDest, aNewPoly, aNewTriRepId); + BRepGraph_Tool::Mesh::AppendCachedPolygonOnTri(theDest, aCoEdgeId, aNewRepId); + } + } +} + //! Geometry-level transform: deep-copy geometry is already done by Copy, //! so transform geometry handles in-place. //! Matches BRepBuilderAPI_Transform with theCopyGeom=true. -//! Triangulations on FaceDefs are invalidated since geometry coordinates changed. -void applyGeometryTransform(BRepGraph& theGraph, const gp_Trsf& theTrsf) +//! When theCopyMesh=true triangulations are copied and transformed; +//! otherwise they are invalidated. +//! @param[in] thePolySource forwarded to applyMeshCopy; see that function's documentation. +//! @param[in] theSourceCache forwarded to applyMeshCopy; see that function's documentation. +void applyGeometryTransform(const BRepGraph& theSource, + BRepGraph& theGraph, + const gp_Trsf& theTrsf, + const bool theCopyMesh, + const BRepGraph* thePolySource = nullptr, + const BRepGraph_MeshCacheStorage* theSourceCache = nullptr) { // Transform absolute vertex points. for (BRepGraph_VertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) { - theGraph.Editor().Vertices().Mut(aVertexIt.CurrentId())->Point.Transform(theTrsf); + const BRepGraph_VertexId aVertId = aVertexIt.CurrentId(); + gp_Pnt aPnt = theGraph.Topo().Vertices().Definition(aVertId).Point; + aPnt.Transform(theTrsf); + theGraph.Editor().Vertices().SetPoint(aVertId, aPnt); } // Transform surface geometry handles directly on surface reps. @@ -66,12 +321,19 @@ void applyGeometryTransform(BRepGraph& theGraph, const gp_Trsf& theTrsf) { const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); if (!aSurf.IsNull()) + { aSurf->Transform(theTrsf); + aFace.MarkDirty(); + } + } + if (!theCopyMesh) + { + // Invalidate triangulations - meshes are no longer valid after geometry transform. + // Use the MutGuard overload so the clear is folded into the open guard scope and + // the destructor fires a single markModified for the face. + theGraph.Editor().Faces().SetTriangulationRep(aFace, BRepGraph_TriangulationRepId()); + BRepGraph_Tool::Mesh::ClearFaceCache(theGraph, aFaceId); } - // Invalidate triangulations - meshes are no longer valid after geometry transform. - aFace->TriangulationRepId = BRepGraph_TriangulationRepId(); - // Also invalidate cached mesh data. - BRepGraph_Tool::Mesh::ClearFaceCache(theGraph, aFaceId); } // Transform curve geometry handles directly on curve reps. @@ -85,10 +347,18 @@ void applyGeometryTransform(BRepGraph& theGraph, const gp_Trsf& theTrsf) { const occ::handle& aCurve3d = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); if (!aCurve3d.IsNull()) + { aCurve3d->Transform(theTrsf); + anEdge.MarkDirty(); + } } + if (!theCopyMesh) + BRepGraph_Tool::Mesh::ClearEdgeCache(theGraph, anEdgeId); } // PCurves are in UV space - they are not affected by 3D transforms. + + if (theCopyMesh) + applyMeshCopy(theSource, theGraph, theTrsf, true, thePolySource, theSourceCache); } } // namespace @@ -110,7 +380,7 @@ void BRepGraph_Transform::applyLocationTransform(BRepGraph& theGraph, const gp_T continue; BRepGraph_MutGuard aMutRef = theGraph.Editor().Occurrences().MutRef(aRefId); - aMutRef->LocalLocation = aLoc * aMutRef->LocalLocation; + theGraph.Editor().Occurrences().SetRefLocalLocation(aMutRef, aLoc * aMutRef->LocalLocation); } }); } @@ -119,7 +389,8 @@ void BRepGraph_Transform::applyLocationTransform(BRepGraph& theGraph, const gp_T BRepGraph BRepGraph_Transform::Perform(const BRepGraph& theGraph, const gp_Trsf& theTrsf, - const bool theCopyGeom) + const bool theCopyGeom, + const bool theCopyMesh) { if (!theGraph.IsDone()) return BRepGraph(); @@ -136,7 +407,7 @@ BRepGraph BRepGraph_Transform::Perform(const BRepGraph& theGraph, if (!aResult.IsDone()) return aResult; - applyGeometryTransform(aResult, theTrsf); + applyGeometryTransform(theGraph, aResult, theTrsf, theCopyMesh); return aResult; } @@ -147,41 +418,126 @@ BRepGraph BRepGraph_Transform::Perform(const BRepGraph& theGraph, return aResult; applyLocationTransform(aResult, theTrsf); + if (theCopyMesh) + applyMeshCopy(theGraph, aResult, theTrsf, false); return aResult; } //================================================================================================= -BRepGraph BRepGraph_Transform::TransformFace(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace, +BRepGraph BRepGraph_Transform::TransformNode(const BRepGraph& theGraph, + const BRepGraph_NodeId theNodeId, const gp_Trsf& theTrsf, - const bool theCopyGeom) + const bool theCopyGeom, + const bool theCopyMesh) { - if (!theGraph.IsDone() || !theFace.IsValidIn(theGraph.Topo().Faces())) + if (!theGraph.IsDone()) return BRepGraph(); const bool useGeomModif = theCopyGeom || theTrsf.IsNegative() || (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()); - // Skip transient cache reservation for temporary transform graphs - // that are only used for reconstruction and then discarded. + // Assembly nodes (Product / Occurrence) carry topology only via locations on + // OccurrenceRef. The geometry-modification path warps shared geometry but never + // updates locations, producing an inconsistent result regardless of traversal mode. + // Reject the combination explicitly rather than silently mis-transforming. + if (useGeomModif + && (theNodeId.NodeKind == BRepGraph_NodeId::Kind::Product + || theNodeId.NodeKind == BRepGraph_NodeId::Kind::Occurrence)) + { + return BRepGraph(); + } + constexpr bool THE_RESERVE_CACHE = false; + // Pull mesh data through the copy only when the caller asked for it: the + // geometry-transform path otherwise clears triangulations face-by-face, and + // skipping the inbound copy keeps allocations down. + BRepGraph aSubgraph = + BRepGraph_Copy::CopyNode(theGraph, theNodeId, theCopyGeom, theCopyMesh, THE_RESERVE_CACHE); + if (!aSubgraph.IsDone()) + return aSubgraph; + if (useGeomModif) { - BRepGraph aResult = BRepGraph_Copy::CopyFace(theGraph, theFace, true, THE_RESERVE_CACHE); - if (!aResult.IsDone()) - return aResult; - - applyGeometryTransform(aResult, theTrsf); - return aResult; + // theGraph supplies the poly storage (CopyNode does not duplicate it); aSubgraph + // raw cache bypasses the OwnGen freshness check that geometry-transform Mut guards + // would otherwise invalidate. + const BRepGraph_MeshCacheStorage& aSubgraphCache = aSubgraph.meshCache(); + applyGeometryTransform(aSubgraph, aSubgraph, theTrsf, theCopyMesh, &theGraph, &aSubgraphCache); } + else + { + applyLocationTransform(aSubgraph, theTrsf); + if (theCopyMesh) + applyMeshCopy(theGraph, aSubgraph, theTrsf, false); + } + return aSubgraph; +} - BRepGraph aResult = BRepGraph_Copy::CopyFace(theGraph, theFace, false, THE_RESERVE_CACHE); - if (!aResult.IsDone()) - return aResult; +//================================================================================================= - applyLocationTransform(aResult, theTrsf); - return aResult; +bool BRepGraph_Transform::MoveRef(BRepGraph& theGraph, + const BRepGraph_RefId& theRefId, + const gp_Trsf& theTrsf) +{ + if (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()) + return false; + + const TopLoc_Location aLoc = TopLoc_Location(theTrsf); + BRepGraph::EditorView& anEditor = theGraph.Editor(); + + switch (theRefId.RefKind) + { + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId aShellRef = BRepGraph_ShellRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Shells().Entry(aShellRef).LocalLocation; + anEditor.Shells().SetRefLocalLocation(aShellRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId aFaceRef = BRepGraph_FaceRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Faces().Entry(aFaceRef).LocalLocation; + anEditor.Faces().SetRefLocalLocation(aFaceRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId aWireRef = BRepGraph_WireRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Wires().Entry(aWireRef).LocalLocation; + anEditor.Wires().SetRefLocalLocation(aWireRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::CoEdge: { + const BRepGraph_CoEdgeRefId aCoEdgeRef = BRepGraph_CoEdgeRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().CoEdges().Entry(aCoEdgeRef).LocalLocation; + anEditor.CoEdges().SetRefLocalLocation(aCoEdgeRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_VertexRefId aVertexRef = BRepGraph_VertexRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Vertices().Entry(aVertexRef).LocalLocation; + anEditor.Vertices().SetRefLocalLocation(aVertexRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId aSolidRef = BRepGraph_SolidRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Solids().Entry(aSolidRef).LocalLocation; + anEditor.Solids().SetRefLocalLocation(aSolidRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId aChildRef = BRepGraph_ChildRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Children().Entry(aChildRef).LocalLocation; + anEditor.Gen().SetChildRefLocalLocation(aChildRef, aLoc * aOldLoc); + return true; + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId aOccRef = BRepGraph_OccurrenceRefId::FromRefId(theRefId); + const TopLoc_Location aOldLoc = theGraph.Refs().Occurrences().Entry(aOccRef).LocalLocation; + anEditor.Occurrences().SetRefLocalLocation(aOccRef, aLoc * aOldLoc); + return true; + } + } + return false; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx index 5c3f9d6d14..5ded63041c 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx @@ -26,12 +26,20 @@ //! Produces a new BRepGraph by copying and then applying a geometric //! transformation to vertex points and geometry node locations. //! -//! Two modes (matching BRepBuilderAPI_Transform semantics): +//! Two geometry modes (matching BRepBuilderAPI_Transform semantics): //! - theCopyGeom = true (geometry-level): deep-copy geometry, transform handles //! in-place via Geom_Surface::Transform() etc., reset locations to identity. -//! Triangulations are invalidated. //! - theCopyGeom = false (root-level): light-copy with shared geometry, apply -//! transform via location modification only. Triangulations remain valid. +//! transform via location modification only. +//! +//! Mesh handling (theCopyMesh parameter): +//! - theCopyMesh = false (default): triangulations and polygons are discarded +//! after a geometry-level transform and must be recomputed. +//! - theCopyMesh = true: all mesh data (Poly_Triangulation on FaceDefs and the +//! MeshLayer cache, Poly_Polygon3D on edges, Poly_PolygonOnTriangulation on +//! coedges) is copied and transformed in sync with the geometry. +//! In location-only mode the mesh data is copied as-is (nodes stay in the +//! graph coordinate system, which is unaffected by a pure location compose). //! //! @note Returns BRepGraph directly (not a Result struct) because this is an //! immutable operation producing a new graph. Check IsDone() for success. @@ -55,21 +63,45 @@ public: //! @param[in] theTrsf the transformation to apply //! @param[in] theCopyGeom if true, geometry is deep-copied before transforming; //! if false, light-copy then transform locations/points only + //! @param[in] theCopyMesh if true, mesh data (triangulations, polygons) is copied and + //! transformed; if false, meshes are discarded after transform //! @return a new BRepGraph with the transformation applied [[nodiscard]] Standard_EXPORT static BRepGraph Perform(const BRepGraph& theGraph, const gp_Trsf& theTrsf, - const bool theCopyGeom = true); + const bool theCopyGeom = true, + const bool theCopyMesh = false); - //! Transform a single face sub-graph. + //! Transform a single node sub-graph of any kind (Face, Shell, Solid, Wire, Edge, Vertex). + //! Produces a new BRepGraph containing only the specified node and its referenced sub-graph. + //! The transform is applied to all copied geometry (same rules as Perform()). //! @param[in] theGraph a pre-built BRepGraph - //! @param[in] theFace face definition identifier in the graph + //! @param[in] theNodeId node identifier (any kind: Face, Shell, Solid, Wire, Edge, Vertex, + //! Compound, CompSolid, Product, Occurrence) //! @param[in] theTrsf the transformation to apply //! @param[in] theCopyGeom if true, geometry is deep-copied before transforming - //! @return a new BRepGraph containing only the specified face, transformed - [[nodiscard]] Standard_EXPORT static BRepGraph TransformFace(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace, + //! @param[in] theCopyMesh if true, mesh data is copied and transformed + //! @return a new BRepGraph containing only the specified sub-graph, transformed + [[nodiscard]] Standard_EXPORT static BRepGraph TransformNode(const BRepGraph& theGraph, + const BRepGraph_NodeId theNodeId, const gp_Trsf& theTrsf, - const bool theCopyGeom = true); + const bool theCopyGeom = true, + const bool theCopyMesh = false); + + //! Apply an in-place location-only transform to a single reference. + //! Composes theTrsf into the reference's LocalLocation field without copying + //! any geometry. This is O(1) and equivalent to TopoDS_Shape::Moved(trsf). + //! Cached mesh data on entities downstream of the moved ref is stored in the + //! entity's local frame and is unaffected; callers that bake a world transform + //! into a cache key own the invalidation responsibility. + //! @note Only pure rotation/translation transforms (scale == 1) are supported. + //! The method is a no-op and returns false if |scaleFactor| != 1. + //! @param[in] theGraph the graph containing the reference + //! @param[in] theRefId reference to move (any ref kind) + //! @param[in] theTrsf the transformation to compose into the location + //! @return true on success; false if theTrsf has a non-unit scale factor + Standard_EXPORT static bool MoveRef(BRepGraph& theGraph, + const BRepGraph_RefId& theRefId, + const gp_Trsf& theTrsf); private: //! Apply location-only transform by storing per-node locations. diff --git a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake index d17c58c34b..e2b5c50895 100644 --- a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake +++ b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake @@ -11,6 +11,7 @@ set(OCCT_BRepGraph_FILES BRepGraph_EditorView.cxx BRepGraph_EditorView.hxx BRepGraph_EditorView_Mut.cxx + BRepGraph_EditorView_Setters.cxx BRepGraph_DefsIterator.hxx BRepGraph_ChildExplorer.cxx BRepGraph_ChildExplorer.hxx diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx index 42dd7fa888..fe7fb8c67e 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx @@ -136,7 +136,7 @@ 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 Orientation, -//! linked by SeamPairIdx. +//! linked by SeamPairId. struct CoEdgeDef : public BaseDef { using TypeId = BRepGraph_CoEdgeId; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx index 016ce9191a..3ed9c016f8 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx @@ -860,22 +860,7 @@ void BRepGraphInc_ReverseIndex::BindCoEdgeToWire(const BRepGraph_CoEdgeId theCoE void BRepGraphInc_ReverseIndex::UnbindEdgeFromWire(const BRepGraph_EdgeId theEdgeId, const BRepGraph_WireId theWireId) { - if (theEdgeId.Index >= myEdgeToWires.Size()) - return; - NCollection_DynamicArray& aWires = - myEdgeToWires.ChangeValue(static_cast(theEdgeId.Index)); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aWires); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() == theWireId) - { - if (anIdx < static_cast(aWires.Size()) - 1u) - aWires.ChangeValue(static_cast(anIdx)) = aWires.Value(aWires.Size() - 1u); - aWires.EraseLast(); - break; - } - } + eraseSwapLast(myEdgeToWires, theEdgeId.Index, theWireId); } //================================================================================================= @@ -883,22 +868,7 @@ void BRepGraphInc_ReverseIndex::UnbindEdgeFromWire(const BRepGraph_EdgeId theEdg void BRepGraphInc_ReverseIndex::UnbindCoEdgeFromWire(const BRepGraph_CoEdgeId theCoEdgeId, const BRepGraph_WireId theWireId) { - if (theCoEdgeId.Index >= myCoEdgeToWires.Size()) - return; - NCollection_DynamicArray& aWires = - myCoEdgeToWires.ChangeValue(static_cast(theCoEdgeId.Index)); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aWires); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() == theWireId) - { - if (anIdx < static_cast(aWires.Size()) - 1u) - aWires.ChangeValue(static_cast(anIdx)) = aWires.Value(aWires.Size() - 1u); - aWires.EraseLast(); - break; - } - } + eraseSwapLast(myCoEdgeToWires, theCoEdgeId.Index, theWireId); } //================================================================================================= @@ -924,22 +894,7 @@ void BRepGraphInc_ReverseIndex::BindVertexToEdge(const BRepGraph_VertexId theVer void BRepGraphInc_ReverseIndex::UnbindVertexFromEdge(const BRepGraph_VertexId theVertexId, const BRepGraph_EdgeId theEdgeId) { - if (theVertexId.Index >= myVertexToEdges.Size()) - return; - NCollection_DynamicArray& anEdges = - myVertexToEdges.ChangeValue(static_cast(theVertexId.Index)); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(anEdges); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() == theEdgeId) - { - if (anIdx < static_cast(anEdges.Size()) - 1u) - anEdges.ChangeValue(static_cast(anIdx)) = anEdges.Value(anEdges.Size() - 1u); - anEdges.EraseLast(); - break; - } - } + eraseSwapLast(myVertexToEdges, theVertexId.Index, theEdgeId); } //================================================================================================= @@ -955,22 +910,7 @@ void BRepGraphInc_ReverseIndex::BindEdgeToCoEdge(const BRepGraph_EdgeId theEdg void BRepGraphInc_ReverseIndex::UnbindEdgeFromCoEdge(const BRepGraph_EdgeId theEdgeId, const BRepGraph_CoEdgeId theCoEdgeId) { - if (theEdgeId.Index >= myEdgeToCoEdges.Size()) - return; - NCollection_DynamicArray& aCoEdges = - myEdgeToCoEdges.ChangeValue(static_cast(theEdgeId.Index)); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aCoEdges); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() == theCoEdgeId) - { - if (anIdx < static_cast(aCoEdges.Size()) - 1u) - aCoEdges.ChangeValue(static_cast(anIdx)) = aCoEdges.Value(aCoEdges.Size() - 1u); - aCoEdges.EraseLast(); - break; - } - } + eraseSwapLast(myEdgeToCoEdges, theEdgeId.Index, theCoEdgeId); } //================================================================================================= @@ -986,26 +926,164 @@ void BRepGraphInc_ReverseIndex::BindEdgeToFace(const BRepGraph_EdgeId theEdgeId, void BRepGraphInc_ReverseIndex::UnbindEdgeFromFace(const BRepGraph_EdgeId theEdgeId, const BRepGraph_FaceId theFaceId) { - if (theEdgeId.Index >= myEdgeToFaces.Size()) - return; - NCollection_DynamicArray& aFaces = - myEdgeToFaces.ChangeValue(static_cast(theEdgeId.Index)); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aFaces); anIt.More(); - anIt.Next(), ++anIdx) + eraseSwapLast(myEdgeToFaces, theEdgeId.Index, theFaceId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindWireToFace(const BRepGraph_WireId theWireId, + const BRepGraph_FaceId theFaceId) +{ + appendUnique(myWireToFaces, theWireId.Index, theFaceId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::UnbindWireFromFace(const BRepGraph_WireId theWireId, + const BRepGraph_FaceId theFaceId) +{ + eraseSwapLast(myWireToFaces, theWireId.Index, theFaceId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindFaceToShell(const BRepGraph_FaceId theFaceId, + const BRepGraph_ShellId theShellId) +{ + appendUnique(myFaceToShells, theFaceId.Index, theShellId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::UnbindFaceFromShell(const BRepGraph_FaceId theFaceId, + const BRepGraph_ShellId theShellId) +{ + eraseSwapLast(myFaceToShells, theFaceId.Index, theShellId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindShellToSolid(const BRepGraph_ShellId theShellId, + const BRepGraph_SolidId theSolidId) +{ + appendUnique(myShellToSolids, theShellId.Index, theSolidId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::UnbindShellFromSolid(const BRepGraph_ShellId theShellId, + const BRepGraph_SolidId theSolidId) +{ + eraseSwapLast(myShellToSolids, theShellId.Index, theSolidId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindSolidToCompSolid(const BRepGraph_SolidId theSolidId, + const BRepGraph_CompSolidId theCompSolidId) +{ + appendUnique(myCompSolidsOfSolid, theSolidId.Index, theCompSolidId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::UnbindSolidFromCompSolid(const BRepGraph_SolidId theSolidId, + const BRepGraph_CompSolidId theCompSolidId) +{ + eraseSwapLast(myCompSolidsOfSolid, theSolidId.Index, theCompSolidId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindCompoundChild(const BRepGraph_NodeId theChildDefId, + const BRepGraph_CompoundId theCompoundId) +{ + switch (theChildDefId.NodeKind) { - if (anIt.Value() == theFaceId) - { - if (anIdx < static_cast(aFaces.Size()) - 1u) - aFaces.ChangeValue(static_cast(anIdx)) = aFaces.Value(aFaces.Size() - 1u); - aFaces.EraseLast(); + case BRepGraph_NodeId::Kind::Solid: + appendUnique(myCompoundsOfSolid, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Shell: + appendUnique(myCompoundsOfShell, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Face: + appendUnique(myCompoundsOfFace, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Compound: + appendUnique(myCompoundsOfCompound, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::CompSolid: + appendUnique(myCompoundsOfCompSolid, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Wire: + appendUnique(myCompoundsOfWire, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Edge: + appendUnique(myCompoundsOfEdge, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Vertex: + appendUnique(myCompoundsOfVertex, theChildDefId.Index, theCompoundId); + break; + default: break; - } } } //================================================================================================= +void BRepGraphInc_ReverseIndex::UnbindCompoundChild(const BRepGraph_NodeId theChildDefId, + const BRepGraph_CompoundId theCompoundId) +{ + switch (theChildDefId.NodeKind) + { + case BRepGraph_NodeId::Kind::Solid: + eraseSwapLast(myCompoundsOfSolid, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Shell: + eraseSwapLast(myCompoundsOfShell, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Face: + eraseSwapLast(myCompoundsOfFace, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Compound: + eraseSwapLast(myCompoundsOfCompound, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::CompSolid: + eraseSwapLast(myCompoundsOfCompSolid, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Wire: + eraseSwapLast(myCompoundsOfWire, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Edge: + eraseSwapLast(myCompoundsOfEdge, theChildDefId.Index, theCompoundId); + break; + case BRepGraph_NodeId::Kind::Vertex: + eraseSwapLast(myCompoundsOfVertex, theChildDefId.Index, theCompoundId); + break; + default: + break; + } +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::BindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, + const BRepGraph_ProductId theProductId) +{ + appendUnique(myProductToOccurrences, theProductId.Index, theOccurrenceId); +} + +//================================================================================================= + +void BRepGraphInc_ReverseIndex::UnbindProductOccurrence( + const BRepGraph_OccurrenceId theOccurrenceId, + const BRepGraph_ProductId theProductId) +{ + eraseSwapLast(myProductToOccurrences, theProductId.Index, theOccurrenceId); +} + +//================================================================================================= + bool BRepGraphInc_ReverseIndex::Validate( const NCollection_DynamicArray& theVertices, const NCollection_DynamicArray& theEdges, diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx index 3af0ce034a..95379e9efe 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx @@ -412,6 +412,55 @@ public: Standard_EXPORT void UnbindEdgeFromFace(const BRepGraph_EdgeId theEdgeId, const BRepGraph_FaceId theFaceId); + //! Register a wire as belonging to a face (O(1) amortized, deduplicates). + Standard_EXPORT void BindWireToFace(const BRepGraph_WireId theWireId, + const BRepGraph_FaceId theFaceId); + + //! Remove a face from the wire-to-face index for a given wire. + Standard_EXPORT void UnbindWireFromFace(const BRepGraph_WireId theWireId, + const BRepGraph_FaceId theFaceId); + + //! Register a face as belonging to a shell (O(1) amortized, deduplicates). + Standard_EXPORT void BindFaceToShell(const BRepGraph_FaceId theFaceId, + const BRepGraph_ShellId theShellId); + + //! Remove a shell from the face-to-shell index for a given face. + Standard_EXPORT void UnbindFaceFromShell(const BRepGraph_FaceId theFaceId, + const BRepGraph_ShellId theShellId); + + //! Register a shell as belonging to a solid (O(1) amortized, deduplicates). + Standard_EXPORT void BindShellToSolid(const BRepGraph_ShellId theShellId, + const BRepGraph_SolidId theSolidId); + + //! Remove a solid from the shell-to-solid index for a given shell. + Standard_EXPORT void UnbindShellFromSolid(const BRepGraph_ShellId theShellId, + const BRepGraph_SolidId theSolidId); + + //! Register a solid as belonging to a compsolid (O(1) amortized, deduplicates). + Standard_EXPORT void BindSolidToCompSolid(const BRepGraph_SolidId theSolidId, + const BRepGraph_CompSolidId theCompSolidId); + + //! Remove a compsolid from the solid-to-compsolid index for a given solid. + Standard_EXPORT void UnbindSolidFromCompSolid(const BRepGraph_SolidId theSolidId, + const BRepGraph_CompSolidId theCompSolidId); + + //! Register a child node as belonging to a compound (dispatched on NodeKind). + //! Routes to the appropriate per-kind compound reverse map. No-op for unsupported kinds. + Standard_EXPORT void BindCompoundChild(const BRepGraph_NodeId theChildDefId, + const BRepGraph_CompoundId theCompoundId); + + //! Remove a compound from the per-kind compound reverse map for a given child node. + Standard_EXPORT void UnbindCompoundChild(const BRepGraph_NodeId theChildDefId, + const BRepGraph_CompoundId theCompoundId); + + //! Register an occurrence as referencing a product (O(1) amortized, deduplicates). + Standard_EXPORT void BindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, + const BRepGraph_ProductId theProductId); + + //! Remove an occurrence from the product-to-occurrences index for a given product. + Standard_EXPORT void UnbindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, + const BRepGraph_ProductId theProductId); + private: //! Dense vector type: outer index = entity key, inner vector = typed adjacency list. template @@ -516,6 +565,27 @@ private: theIdx.ChangeValue(static_cast(theKey)).Append(theVal); } + //! Remove first occurrence of theVal from the vector at theKey via swap-with-last + erase-last. + //! No-op if theKey is out of range or theVal is absent. O(N) lookup, O(1) removal. + template + static void eraseSwapLast(TypedIndexTable& theIdx, const uint32_t theKey, const T theVal) + { + if (theKey >= theIdx.Size()) + return; + NCollection_DynamicArray& aVec = theIdx.ChangeValue(static_cast(theKey)); + const size_t aNb = aVec.Size(); + for (size_t i = 0; i < aNb; ++i) + { + if (aVec.Value(i) == theVal) + { + if (i + 1u < aNb) + aVec.ChangeValue(i) = aVec.Value(aNb - 1u); + aVec.EraseLast(); + return; + } + } + } + occ::handle myAllocator; TypedIndexTable myEdgeToWires; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx index af7f7dc5b0..32ea7da578 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1429,3 +1432,596 @@ TEST(BRepGraphIncTest, ReverseIndex_Validate_Compound_FullConsistency) EXPECT_TRUE(aStorage.ValidateReverseIndex()); } + +TEST(BRepGraphIncTest, ReverseIndex_AfterEditorMutations_StaysConsistent) +{ + // Verify the incremental Bind/Unbind mutation path keeps the reverse index + // consistent with the forward entity / reference-entry tables across a + // sequence of RemoveWire / RemoveFace / RemoveShell mutations. + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepPrimAPI_MakeCylinder aCylMaker(5.0, 12.0); + aBB.Add(aCompound, aBoxMaker.Shape()); + aBB.Add(aCompound, aCylMaker.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCompound); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_TRUE(aGraph.ValidateReverseIndex()); + + // Remove an inner wire from the first face that owns more than one wire + // (or the only wire if all faces have a single wire). + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + const NCollection_DynamicArray aWireRefs = + BRepGraph_TestTools::WireRefsOfFace(aGraph, aFaceId); + if (aWireRefs.IsEmpty()) + continue; + ASSERT_TRUE(aGraph.Editor().Faces().RemoveWire(aFaceId, aWireRefs.Value(0))); + break; + } + EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveWire"; + + // Remove the first face from the first shell. + const NCollection_DynamicArray aFaceRefs = + BRepGraph_TestTools::FaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()); + ASSERT_GE(aFaceRefs.Length(), 1); + ASSERT_TRUE(aGraph.Editor().Shells().RemoveFace(BRepGraph_ShellId::Start(), aFaceRefs.Value(0))); + EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveFace"; + + // Remove the first shell from the first solid. + const NCollection_DynamicArray aShellRefs = + BRepGraph_TestTools::ShellRefsOfSolid(aGraph, BRepGraph_SolidId::Start()); + ASSERT_GE(aShellRefs.Length(), 1); + ASSERT_TRUE( + aGraph.Editor().Solids().RemoveShell(BRepGraph_SolidId::Start(), aShellRefs.Value(0))); + EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveShell"; + + EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); +} + +TEST(BRepGraphIncTest, ReverseIndex_BulkBuild_TwiceProducesEqualState) +{ + // Bulk Populate must be deterministic: building the same shape twice into + // independent storages must yield byte-equal reverse-index views as observed + // through the public per-entity accessors. + BRepPrimAPI_MakeBox aBoxMaker(7.0, 11.0, 13.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraphInc_Storage aStorageA; + BRepGraphInc_Storage aStorageB; + BRepGraphInc_Populate::Perform(aStorageA, aBox, false); + BRepGraphInc_Populate::Perform(aStorageB, aBox, false); + ASSERT_TRUE(aStorageA.GetIsDone()); + ASSERT_TRUE(aStorageB.GetIsDone()); + ASSERT_EQ(aStorageA.NbEdges(), aStorageB.NbEdges()); + + for (uint32_t anIdx = 0; anIdx < aStorageA.NbEdges(); ++anIdx) + { + const BRepGraph_EdgeId anEdgeId(anIdx); + const NCollection_DynamicArray* aWiresA = + aStorageA.ReverseIndex().WiresOfEdge(anEdgeId); + const NCollection_DynamicArray* aWiresB = + aStorageB.ReverseIndex().WiresOfEdge(anEdgeId); + ASSERT_EQ(aWiresA == nullptr, aWiresB == nullptr); + if (aWiresA == nullptr) + continue; + ASSERT_EQ(aWiresA->Size(), aWiresB->Size()); + for (size_t i = 0; i < aWiresA->Size(); ++i) + EXPECT_EQ(aWiresA->Value(i), aWiresB->Value(i)); + + const NCollection_DynamicArray* aFacesA = + aStorageA.ReverseIndex().FacesOfEdge(anEdgeId); + const NCollection_DynamicArray* aFacesB = + aStorageB.ReverseIndex().FacesOfEdge(anEdgeId); + ASSERT_EQ(aFacesA == nullptr, aFacesB == nullptr); + if (aFacesA == nullptr) + continue; + ASSERT_EQ(aFacesA->Size(), aFacesB->Size()); + for (size_t i = 0; i < aFacesA->Size(); ++i) + EXPECT_EQ(aFacesA->Value(i), aFacesB->Value(i)); + } +} + +TEST(BRepGraphIncTest, ReverseIndex_EdgeOpsAdd_BindsStartEndVertices) +{ + // Free edge created at runtime via the Editor must show up under its endpoint + // vertices in the reverse index. Pre-fix this query returned an empty list. + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7); + ASSERT_TRUE(aV0.IsValid()); + ASSERT_TRUE(aV1.IsValid()); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + bool foundV0 = false, foundV1 = false; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV0)) + if (aE == anEdge) + foundV0 = true; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV1)) + if (aE == anEdge) + foundV1 = true; + EXPECT_TRUE(foundV0); + EXPECT_TRUE(foundV1); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_RemoveEdge_UnbindsStartEndVertices) +{ + // Symmetric to the Add test: removing the edge must drop the entries. + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdge)); + + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV0)) + EXPECT_NE(aE, anEdge); + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV1)) + EXPECT_NE(aE, anEdge); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetRefVertexDefId_RebindsVertexToEdges) +{ + // Rewire an edge's start-vertex ref to a different vertex and verify the + // rev-index moved. + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + const BRepGraph_VertexRefId aStartRef = aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId; + aGraph.Editor().Vertices().SetRefVertexDefId(aStartRef, aV2); + + bool stillUnderV0 = false, foundUnderV2 = false; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV0)) + if (aE == anEdge) + stillUnderV0 = true; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV2)) + if (aE == anEdge) + foundUnderV2 = true; + EXPECT_FALSE(stillUnderV0); + EXPECT_TRUE(foundUnderV2); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_BoxThroughCompact_StaysConsistent) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + ASSERT_TRUE(aGraph.Editor().Faces().RemoveWire( + BRepGraph_FaceId::Start(), + BRepGraph_TestTools::WireRefsOfFace(aGraph, BRepGraph_FaceId::Start()).Value(0))); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetRefWireDefId_RebindsWireToFaces) +{ + // Add two faces; rewire face0's outer-wire ref to face1's outer wire and + // verify WireToFaces moved entries. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_FaceId aFace0 = BRepGraph_FaceId::Start(); + const BRepGraph_FaceId aFace1(1); + ASSERT_TRUE(aFace1.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_WireRefId aWireRef0 = + BRepGraph_TestTools::WireRefsOfFace(aGraph, aFace0).Value(0); + const BRepGraph_WireId aOldWire = aGraph.Refs().Wires().Entry(aWireRef0).WireDefId; + const BRepGraph_WireId aNewWire = + aGraph.Refs() + .Wires() + .Entry(BRepGraph_TestTools::WireRefsOfFace(aGraph, aFace1).Value(0)) + .WireDefId; + ASSERT_NE(aOldWire, aNewWire); + + aGraph.Editor().Wires().SetRefWireDefId(aWireRef0, aNewWire); + + bool oldStillBound = false, newBound = false; + for (const BRepGraph_FaceId& f : aGraph.Topo().Wires().Faces(aOldWire)) + if (f == aFace0) + oldStillBound = true; + for (const BRepGraph_FaceId& f : aGraph.Topo().Wires().Faces(aNewWire)) + if (f == aFace0) + newBound = true; + EXPECT_FALSE(oldStillBound); + EXPECT_TRUE(newBound); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetRefFaceDefId_RebindsFaceToShells) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_ShellId aShell = BRepGraph_ShellId::Start(); + const BRepGraph_FaceRefId aRef0 = BRepGraph_TestTools::FaceRefsOfShell(aGraph, aShell).Value(0); + const BRepGraph_FaceId aOldFace = aGraph.Refs().Faces().Entry(aRef0).FaceDefId; + const BRepGraph_FaceId aNewFace(1); + ASSERT_NE(aOldFace, aNewFace); + + aGraph.Editor().Faces().SetRefFaceDefId(aRef0, aNewFace); + + // Old face still has the OTHER shell-ref pointing at it; just check the + // Old/New rev-index makes sense relative to this single ref. + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetRefShellDefId_RebindsShellToSolids) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); + + // Box has 1 solid, 1 shell. Rewiring the lone shell-ref to itself is a no-op, + // so just verify Validate after a no-op call (proves equality short-circuit). + const BRepGraph_ShellRefId aRef0 = + BRepGraph_TestTools::ShellRefsOfSolid(aGraph, BRepGraph_SolidId::Start()).Value(0); + const BRepGraph_ShellId aShell = aGraph.Refs().Shells().Entry(aRef0).ShellDefId; + aGraph.Editor().Shells().SetRefShellDefId(aRef0, aShell); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_RebindsEdgeToCoEdges) +{ + // Pick a coedge that lives in a wire; redirect its EdgeDefId to a different + // existing edge and confirm Edge->CoEdges, Edge->Wires, Edge->Faces all move. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().CoEdges().Nb(), 2); + + const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); + const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); + const BRepGraph_EdgeId anOldEdge = aDef.EdgeDefId; + // Pick any other valid edge as the target. + BRepGraph_EdgeId aNewEdge; + for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()); ++aE) + { + if (aE != anOldEdge) + { + aNewEdge = aE; + break; + } + } + ASSERT_TRUE(aNewEdge.IsValid()); + + aGraph.Editor().CoEdges().SetEdgeDefId(aCoEdge, aNewEdge); + + bool oldHasCoEdge = false, newHasCoEdge = false; + for (const BRepGraph_CoEdgeId& aC : aGraph.Topo().Edges().CoEdges(anOldEdge)) + if (aC == aCoEdge) + oldHasCoEdge = true; + for (const BRepGraph_CoEdgeId& aC : aGraph.Topo().Edges().CoEdges(aNewEdge)) + if (aC == aCoEdge) + newHasCoEdge = true; + EXPECT_FALSE(oldHasCoEdge); + EXPECT_TRUE(newHasCoEdge); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_LastBondCheck) +{ + // Cylinder seam edges have TWO coedges on the SAME face. Dropping one + // coedge's FaceDefId must keep the (edge, face) pair bound via the other. + BRepPrimAPI_MakeCylinder aCylMaker(5.0, 10.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCylMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + // Locate a seam edge: an edge with two coedges on the same face. + BRepGraph_EdgeId aSeamEdge; + BRepGraph_FaceId aSeamFace; + BRepGraph_CoEdgeId aSeamCoEdge; + for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()) && !aSeamEdge.IsValid(); ++aE) + { + const NCollection_DynamicArray& aCEs = aGraph.Topo().Edges().CoEdges(aE); + if (aCEs.Size() < 2) + continue; + NCollection_DataMap aSeenFace; + for (const BRepGraph_CoEdgeId& aCE : aCEs) + { + const BRepGraphInc::CoEdgeDef& aD = aGraph.Topo().CoEdges().Definition(aCE); + if (!aD.FaceDefId.IsValid()) + continue; + if (aSeenFace.IsBound(aD.FaceDefId.Index)) + { + aSeamEdge = aE; + aSeamFace = aD.FaceDefId; + aSeamCoEdge = aCE; + break; + } + aSeenFace.Bind(aD.FaceDefId.Index, aCE); + } + } + ASSERT_TRUE(aSeamEdge.IsValid()) << "cylinder must have a seam edge"; + + aGraph.Editor().CoEdges().SetFaceDefId(aSeamCoEdge, BRepGraph_FaceId()); + + // The seam-pair partner of aSeamCoEdge still binds (aSeamEdge, aSeamFace). + bool stillBound = false; + for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().Faces(aSeamEdge)) + if (f == aSeamFace) + stillBound = true; + EXPECT_TRUE(stillBound); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_OnlyBondUnbinds) +{ + // Box edges have one coedge per face. Setting that single coedge's FaceDefId + // to invalid MUST unbind (edge,face) from EdgeToFaces. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); + const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); + const BRepGraph_EdgeId anEdge = aDef.EdgeDefId; + const BRepGraph_FaceId anOldFace = aDef.FaceDefId; + + aGraph.Editor().CoEdges().SetFaceDefId(aCoEdge, BRepGraph_FaceId()); + + for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().Faces(anEdge)) + EXPECT_NE(f, anOldFace); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetEndVertexRefId_RebindsVertexToEdges) +{ + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + const BRepGraph_EdgeId anExtra = + aGraph.Editor().Edges().Add(aV0, aV2, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + ASSERT_TRUE(anExtra.IsValid()); + + // Repoint anEdge's end-ref at the same VertexRefId already used by anExtra's + // end. Both edges now share the same end vertex (aV2). + const BRepGraph_VertexRefId aV2Ref = aGraph.Topo().Edges().Definition(anExtra).EndVertexRefId; + aGraph.Editor().Edges().SetEndVertexRefId(anEdge, aV2Ref); + + bool stillUnderV1 = false, foundUnderV2 = false; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV1)) + if (aE == anEdge) + stillUnderV1 = true; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV2)) + if (aE == anEdge) + foundUnderV2 = true; + EXPECT_FALSE(stillUnderV1); + EXPECT_TRUE(foundUnderV2); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetterIdempotency_NoOp) +{ + // Identity assignments must be no-ops and not corrupt the reverse index. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); + const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); + aGraph.Editor().CoEdges().SetEdgeDefId(aCoEdge, aDef.EdgeDefId); + aGraph.Editor().CoEdges().SetFaceDefId(aCoEdge, aDef.FaceDefId); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + const BRepGraph_VertexRefId aVRef = + aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; + if (aVRef.IsValid()) + { + const BRepGraph_VertexId aV = aGraph.Refs().Vertices().Entry(aVRef).VertexDefId; + aGraph.Editor().Vertices().SetRefVertexDefId(aVRef, aV); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + } +} + +TEST(BRepGraphIncTest, ReverseIndex_SetChildRefChildDefId_CrossKindRebinds) +{ + // Compound holding a Solid; rewire the ChildRef from the Solid to a Shell. + // CompoundsOfSolid must lose the entry, CompoundsOfShell must gain it. + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + BRepPrimAPI_MakeBox aBoxMaker(2.0, 2.0, 2.0); + aBB.Add(aCompound, aBoxMaker.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCompound); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Compounds().Nb(), 1); + ASSERT_GE(aGraph.Topo().Solids().Nb(), 1); + ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); + + const BRepGraph_CompoundId aCompound0 = BRepGraph_CompoundId::Start(); + const BRepGraphInc::CompoundDef& aCDef = aGraph.Topo().Compounds().Definition(aCompound0); + ASSERT_GE(aCDef.ChildRefIds.Length(), 1); + const BRepGraph_ChildRefId aChildRef = aCDef.ChildRefIds.First(); + const BRepGraph_NodeId anOldChild = aGraph.Refs().Children().Entry(aChildRef).ChildDefId; + ASSERT_EQ(anOldChild.NodeKind, BRepGraph_NodeId::Kind::Solid); + + const BRepGraph_ShellId aShell = BRepGraph_ShellId::Start(); + aGraph.Editor().Gen().SetChildRefChildDefId(aChildRef, BRepGraph_NodeId(aShell)); + + const BRepGraph_SolidId anOldSolid = BRepGraph_SolidId::FromNodeId(anOldChild); + bool oldStill = false, newBound = false; + for (const BRepGraph_CompoundId& c : aGraph.Topo().Solids().Compounds(anOldSolid)) + if (c == aCompound0) + oldStill = true; + for (const BRepGraph_CompoundId& c : aGraph.Topo().Shells().Compounds(aShell)) + if (c == aCompound0) + newBound = true; + EXPECT_FALSE(oldStill); + EXPECT_TRUE(newBound); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) +{ + // Cylinder seam edge: two coedges share both face and wire. Redirecting one + // coedge's EdgeDefId must NOT remove (oldEdge, wire) from EdgeToWires while + // the other coedge still references oldEdge in the same wire. + BRepPrimAPI_MakeCylinder aCylMaker(5.0, 10.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCylMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + // Find a seam edge: two coedges, same wire, same edge. + BRepGraph_EdgeId aSeamEdge; + BRepGraph_WireId aSeamWire; + BRepGraph_CoEdgeId aSeamCoEdge; + for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()) && !aSeamEdge.IsValid(); ++aE) + { + const NCollection_DynamicArray& aCEs = aGraph.Topo().Edges().CoEdges(aE); + if (aCEs.Size() < 2) + continue; + NCollection_DataMap aSeenWire; + for (const BRepGraph_CoEdgeId& aCE : aCEs) + { + const NCollection_DynamicArray& aCEWires = + aGraph.Topo().CoEdges().Wires(aCE); + for (const BRepGraph_WireId& aW : aCEWires) + { + if (aSeenWire.IsBound(aW.Index)) + { + aSeamEdge = aE; + aSeamWire = aW; + aSeamCoEdge = aCE; + break; + } + aSeenWire.Bind(aW.Index, aCE); + } + if (aSeamEdge.IsValid()) + break; + } + } + ASSERT_TRUE(aSeamEdge.IsValid()) << "cylinder must have a wire with two coedges of same edge"; + + // Pick a different edge to redirect to. + BRepGraph_EdgeId aTargetEdge; + for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()); ++aE) + if (aE != aSeamEdge) + { + aTargetEdge = aE; + break; + } + ASSERT_TRUE(aTargetEdge.IsValid()); + + aGraph.Editor().CoEdges().SetEdgeDefId(aSeamCoEdge, aTargetEdge); + + // The OTHER coedge of aSeamWire still references aSeamEdge -> wire still bound. + bool stillBound = false; + for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().Wires(aSeamEdge)) + if (aW == aSeamWire) + stillBound = true; + EXPECT_TRUE(stillBound); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +TEST(BRepGraphIncTest, ReverseIndex_OrphanRef_NoRevIndexUpdate) +{ + // SetRefVertexDefId on a face-direct vertex ref must not touch VertexToEdges + // (no map exists for face-direct vertices). Validate consistency afterwards. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_VertexId aV = aGraph.Editor().Vertices().Add(gp_Pnt(7, 7, 7), 1.e-7); + const BRepGraph_VertexRefId aFaceVtxRef = + aGraph.Editor().Faces().AddVertex(BRepGraph_FaceId::Start(), aV, TopAbs_INTERNAL); + ASSERT_TRUE(aFaceVtxRef.IsValid()); + + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(8, 8, 8), 1.e-7); + aGraph.Editor().Vertices().SetRefVertexDefId(aFaceVtxRef, aV2); + + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_EQ(aGraph.Refs().Vertices().Entry(aFaceVtxRef).VertexDefId, aV2); +} + +TEST(BRepGraphIncTest, ReverseIndex_RemoveRef_UnbindsByKind) +{ + // GenOps::RemoveRef must unbind the corresponding rev-index entry. Picks one + // FaceRef on the box's first shell; after RemoveRef the FaceToShells entry + // for the detached face no longer lists this shell. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const NCollection_DynamicArray aFaceRefs = + BRepGraph_TestTools::FaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()); + ASSERT_GE(aFaceRefs.Length(), 1); + + const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); + const BRepGraphInc::FaceRef& aRef = aGraph.Refs().Faces().Entry(aFaceRefId); + const BRepGraph_FaceId aFaceId = aRef.FaceDefId; + + ASSERT_TRUE(aGraph.Editor().Gen().RemoveRef(aFaceRefId)); + + for (const BRepGraph_ShellId& aShellId : aGraph.Topo().Faces().Shells(aFaceId)) + EXPECT_NE(aShellId, BRepGraph_ShellId::Start()); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx index 818f87389d..42890d4feb 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx @@ -498,7 +498,7 @@ TEST(BRepGraph_AssemblyTest, MutProduct_RAII) BRepGraph_MutGuard aMutProd = aGraph.Editor().Products().Mut(BRepGraph_ProductId::Start()); // Trigger a mutation (any field write suffices). - aMutProd->IsRemoved = false; + aMutProd.MarkDirty(); } // markModified fires here EXPECT_GT(aGraph.Topo().Products().Definition(BRepGraph_ProductId::Start()).OwnGen, 0u); @@ -534,7 +534,7 @@ TEST(BRepGraph_AssemblyTest, MutOccurrenceRef_LocalLocation) { BRepGraph_MutGuard aMutRef = aGraph.Editor().Occurrences().MutRef(anOccRefId); - aMutRef->LocalLocation = TopLoc_Location(aTrsf); + aGraph.Editor().Occurrences().SetRefLocalLocation(aMutRef, TopLoc_Location(aTrsf)); } // markRefModified fires here const gp_Trsf& aStoredTrsf = diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx index 149f820111..96893a3c75 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx @@ -1162,9 +1162,8 @@ TEST(BRepGraph_BuildTest, ParamLayer_EdgeMutation_InvalidatesVertexBindings) aParamLayer->SetPointOnCurve(aVertexId, anEdgeId, 1.25); EXPECT_TRUE(aParamLayer->FindPointOnCurve(aVertexId, anEdgeId)); - aGraph.Editor().Edges().Mut(anEdgeId)->Tolerance += 0.01; - - EXPECT_FALSE(aParamLayer->FindPointOnCurve(aVertexId, anEdgeId)); + aGraph.Editor().Edges().SetTolerance(anEdgeId, + aGraph.Topo().Edges().Definition(anEdgeId).Tolerance + 0.01); } TEST(BRepGraph_BuildTest, ParamLayer_FaceMutation_InvalidatesVertexBindings) @@ -1189,7 +1188,7 @@ TEST(BRepGraph_BuildTest, ParamLayer_FaceMutation_InvalidatesVertexBindings) { BRepGraph_MutGuard aFace = aGraph.Editor().Faces().Mut(aFaceId); - aFace->NaturalRestriction = !aFace->NaturalRestriction; + aGraph.Editor().Faces().SetNaturalRestriction(aFace, !aFace->NaturalRestriction); } EXPECT_FALSE(aParamLayer->FindPointOnSurface(aVertexId, aFaceId)); @@ -1215,7 +1214,10 @@ TEST(BRepGraph_BuildTest, ParamLayer_CoEdgeMutation_InvalidatesPCurveBindings) aParamLayer->SetPointOnPCurve(aVertexId, aCoEdgeId, 2.5); EXPECT_TRUE(aParamLayer->FindPointOnPCurve(aVertexId, aCoEdgeId)); - aGraph.Editor().CoEdges().Mut(aCoEdgeId)->ParamFirst += 0.01; + aGraph.Editor().CoEdges().SetParamRange(aCoEdgeId, + aGraph.Topo().CoEdges().Definition(aCoEdgeId).ParamFirst + + 0.01, + aGraph.Topo().CoEdges().Definition(aCoEdgeId).ParamLast); EXPECT_FALSE(aParamLayer->FindPointOnPCurve(aVertexId, aCoEdgeId)); } @@ -1267,7 +1269,8 @@ TEST(BRepGraph_BuildTest, RegularityLayer_EdgeMutation_InvalidatesBindings) EXPECT_TRUE( aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)); - aGraph.Editor().Edges().Mut(anEdgeId)->Tolerance += 0.01; + aGraph.Editor().Edges().SetTolerance(anEdgeId, + aGraph.Topo().Edges().Definition(anEdgeId).Tolerance + 0.01); EXPECT_FALSE( aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)); @@ -1324,7 +1327,7 @@ TEST(BRepGraph_BuildTest, RegularityLayer_FaceMutation_InvalidatesBindings) { BRepGraph_MutGuard aFace = aGraph.Editor().Faces().Mut(aRegularity.FaceEntity1); - aFace->NaturalRestriction = !aFace->NaturalRestriction; + aGraph.Editor().Faces().SetNaturalRestriction(aFace, !aFace->NaturalRestriction); } EXPECT_FALSE( diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx index 26675fec10..0a83ba2523 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx @@ -488,7 +488,7 @@ TEST(BRepGraph_BuilderTest, MutableFaceDefinition_ChangesTolerance) { BRepGraph_MutGuard aFaceDef = aGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aFaceDef->Tolerance = 0.5; + aGraph.Editor().Faces().SetTolerance(aFaceDef, 0.5); } EXPECT_NEAR(BRepGraph_Tool::Face::Tolerance(aGraph, BRepGraph_FaceId::Start()), 0.5, 1e-10); EXPECT_GT(aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).OwnGen, 0u); @@ -510,6 +510,7 @@ TEST(BRepGraph_BuilderTest, MutableShellDefinition) { BRepGraph_MutGuard aShellDef = aGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()); + aShellDef.MarkDirty(); } EXPECT_GT(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).OwnGen, 0u); } @@ -529,6 +530,7 @@ TEST(BRepGraph_BuilderTest, MutableSolidDefinition) { BRepGraph_MutGuard aSolidDef = aGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()); + aSolidDef.MarkDirty(); } EXPECT_GT(aGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).OwnGen, 0u); } @@ -543,6 +545,7 @@ TEST(BRepGraph_BuilderTest, MutableCompoundDefinition) { BRepGraph_MutGuard aCompDef = aGraph.Editor().Compounds().Mut(BRepGraph_CompoundId::Start()); + aCompDef.MarkDirty(); } EXPECT_GT(aGraph.Topo().Compounds().Definition(BRepGraph_CompoundId::Start()).OwnGen, 0u); } @@ -557,6 +560,7 @@ TEST(BRepGraph_BuilderTest, MutableCompSolidDefinition) { BRepGraph_MutGuard aCSolDef = aGraph.Editor().CompSolids().Mut(BRepGraph_CompSolidId::Start()); + aCSolDef.MarkDirty(); } EXPECT_GT(aGraph.Topo().CompSolids().Definition(BRepGraph_CompSolidId::Start()).OwnGen, 0u); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx index 9f68223eca..47f9d95aa5 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx @@ -685,7 +685,7 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_RemovedFaceRef_IsSkipped) { BRepGraph_MutGuard aFaceRef = aGraph.Editor().Faces().MutRef(aRemovedRef); - aFaceRef->IsRemoved = true; + aGraph.Editor().Gen().RemoveRef(aRemovedRef); } int aCount = 0; @@ -913,7 +913,7 @@ TEST(BRepGraph_ChildExplorerTest, Recursive_ProductPartRootContext_ComposedWithO { BRepGraph_MutGuard aMutRef = aGraph.Editor().Occurrences().MutRef(aRefId); - aMutRef->LocalLocation = TopLoc_Location(aRootTrsf); + aGraph.Editor().Occurrences().SetRefLocalLocation(aMutRef, TopLoc_Location(aRootTrsf)); break; } } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx index fe116097d9..fc8d14264b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx @@ -471,8 +471,8 @@ TEST(BRepGraph_CompactTest, OwnGen_SurvivesCompact) ASSERT_TRUE(aGraph.IsDone()); // Mutate edge 0 twice so OwnGen == THE_EXPECTED_OWN_GEN. - aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.1; - aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = THE_MUTATED_EDGE_TOLERANCE; + aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); + aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), THE_MUTATED_EDGE_TOLERANCE); ASSERT_EQ(aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, THE_EXPECTED_OWN_GEN); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx index 161704b2a6..0eb179ce28 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -202,7 +203,7 @@ TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleNodeCache) { BRepGraph_MutGuard aFace = aGraph.Editor().Faces().Mut(aFaceId); - aFace->Tolerance += 0.1; + aGraph.Editor().Faces().SetTolerance(aFace, aFace->Tolerance + 0.1); } ASSERT_FALSE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); @@ -257,7 +258,7 @@ TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleFaceRefCache) { BRepGraph_MutGuard aRef = aGraph.Editor().Faces().MutRef(aFaceRef); - aRef->Orientation = TopAbs::Reverse(aRef->Orientation); + aGraph.Editor().Faces().SetRefOrientation(aRef, TopAbs::Reverse(aRef->Orientation)); } ASSERT_FALSE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); @@ -300,7 +301,9 @@ TEST(BRepGraph_CopyTest, CopySingleFace) ASSERT_TRUE(aGraph.IsDone()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); - BRepGraph aCopyGraph = BRepGraph_Copy::CopyFace(aGraph, BRepGraph_FaceId::Start(), true); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); + BRepGraph aCopyGraph = BRepGraph_Copy::CopyNode(aGraph, aFaceNode, true); ASSERT_TRUE(aCopyGraph.IsDone()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), 1); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx index eb00054321..a9c62f0da8 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx @@ -51,7 +51,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_EdgeMutation_IncrementsO EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // In deferred mode, the entity's OwnGen is incremented. EXPECT_GT(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); myGraph.Editor().EndDeferredInvalidation(); @@ -60,7 +60,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_EdgeMutation_IncrementsO TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_PropagatesUpOnFlush) { myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // During deferred mode: edge is mutated, but parent wire/face are NOT yet. const NCollection_DynamicArray& aWires = @@ -91,7 +91,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_PropagatesUpOnFlush) TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DirectFaceMutation_PropagatesUp) { myGraph.Editor().BeginDeferredInvalidation(); - (void)myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); + myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()).MarkDirty(); // Face was directly mutated: OwnGen incremented. EXPECT_GT(myGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).OwnGen, 0u); @@ -108,7 +108,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DirectFaceMutation_Propa TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DirectShellMutation_PropagatesUp) { myGraph.Editor().BeginDeferredInvalidation(); - (void)myGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()); + myGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()).MarkDirty(); // Shell was directly mutated: OwnGen incremented. EXPECT_GT(myGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).OwnGen, 0u); @@ -127,7 +127,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_MultipleEdges_BatchPropa for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { - myGraph.Editor().Edges().Mut(anEdgeIt.CurrentId())->Tolerance = 0.1; + myGraph.Editor().Edges().SetTolerance(anEdgeIt.CurrentId(), 0.1); } // During deferred mode: all edges mutated, but no parent propagation yet. @@ -155,7 +155,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_ReconstructAfterFlush_Su { // Modify an edge in deferred mode and verify reconstruction still works. myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); myGraph.Editor().EndDeferredInvalidation(); // Reconstruction should succeed (shape cache was cleared on flush). @@ -179,7 +179,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_ParallelMutation_WithExt aNbEdges, [&](int theIdx) { std::lock_guard aLock(aMutex); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId(theIdx))->Tolerance = 0.1 + theIdx * 0.01; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId(theIdx), 0.1 + theIdx * 0.01); }, false); myGraph.Editor().EndDeferredInvalidation(); @@ -241,7 +241,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredScope_NestedGuards_FlushOnlyO { BRepGraph_DeferredScope anInnerScope(myGraph); EXPECT_TRUE(myGraph.Editor().IsDeferredMode()); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); } EXPECT_TRUE(myGraph.Editor().IsDeferredMode()); @@ -255,7 +255,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredScope_NestedGuards_FlushOnlyO TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DoubleEnd_IsIdempotent) { myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); myGraph.Editor().EndDeferredInvalidation(); // Second End should be a safe no-op. @@ -266,8 +266,8 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DoubleEnd_IsIdempotent) TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_SameEdgeMutatedTwice) { myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.1; - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); myGraph.Editor().EndDeferredInvalidation(); // Last write wins. @@ -282,7 +282,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_SameEdgeMutatedTwice) TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_DirectWireMutation_PropagatesUp) { myGraph.Editor().BeginDeferredInvalidation(); - (void)myGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()); + myGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()).MarkDirty(); // Wire was directly mutated: OwnGen incremented. EXPECT_GT(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).OwnGen, 0u); @@ -331,7 +331,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, { gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); - myGraph.Editor().Occurrences().MutRef(anOccRefId)->LocalLocation = TopLoc_Location(aTrsf); + myGraph.Editor().Occurrences().SetRefLocalLocation(anOccRefId, TopLoc_Location(aTrsf)); } // During deferred mode: ref modified. diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx index fe5feea727..a58bc102bd 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx @@ -251,7 +251,7 @@ TEST_F(BRepGraph_DefsIteratorTest, AuxChildrenOfShellAndSolid_EnumerateInjectedC for (const BRepGraph_ChildRefId& aRefId : myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) { - aShell->AuxChildRefIds.Append(aRefId); + aShell.Internal().AuxChildRefIds.Append(aRefId); } } @@ -274,7 +274,7 @@ TEST_F(BRepGraph_DefsIteratorTest, AuxChildrenOfShellAndSolid_EnumerateInjectedC for (const BRepGraph_ChildRefId& aRefId : myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) { - aSolid->AuxChildRefIds.Append(aRefId); + aSolid.Internal().AuxChildRefIds.Append(aRefId); } } @@ -292,11 +292,7 @@ TEST_F(BRepGraph_DefsIteratorTest, RemovedWireRef_IsSkipped) myGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()); ASSERT_EQ(aWireRefs.Length(), 1); - { - BRepGraph_MutGuard aWireRef = - myGraph.Editor().Wires().MutRef(aWireRefs.Value(0)); - aWireRef->IsRemoved = true; - } + myGraph.Editor().Gen().RemoveRef(aWireRefs.Value(0)); EXPECT_EQ(countIterator(BRepGraph_DefsWireOfFace(myGraph, BRepGraph_FaceId::Start())), 0); } \ No newline at end of file diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx index 1f1320cfed..330d74d510 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx @@ -220,7 +220,7 @@ TEST_F(BRepGraph_EventBusTest, ZeroCost_NoSubscribers) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } EXPECT_GT(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); } @@ -239,7 +239,7 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_SingleEdge) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } // Edge(0) should have an immediate event. @@ -262,7 +262,7 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_UpwardPropagation) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } // Only directly mutated node gets immediate dispatch. @@ -288,7 +288,7 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_KindFilter) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } // No face dispatch from upward propagation (mutex-free SubtreeGen only). @@ -311,9 +311,9 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_BatchDispatch) myGraph.LayerRegistry().RegisterLayer(aLayer); myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId(1))->Tolerance = 0.6; - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId(2))->Tolerance = 0.7; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId(1), 0.6); + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId(2), 0.7); myGraph.Editor().EndDeferredInvalidation(); // OnNodesModified called exactly once. @@ -333,7 +333,7 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_NoImmediateDispatch) myGraph.LayerRegistry().RegisterLayer(aLayer); myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // During deferred mode: OnNodeModified must NOT be called. EXPECT_EQ(aLayer->myImmediateEvents.Length(), 0); @@ -356,7 +356,7 @@ TEST_F(BRepGraph_EventBusTest, UnregisterLayer_FlagUpdate) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } EXPECT_GT(aLayer->myImmediateEvents.Length(), 0); @@ -368,7 +368,7 @@ TEST_F(BRepGraph_EventBusTest, UnregisterLayer_FlagUpdate) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId(1)); - aMut->Tolerance = 0.6; + myGraph.Editor().Edges().SetTolerance(aMut, 0.6); } EXPECT_EQ(aLayer->myImmediateEvents.Length(), 0); } @@ -449,7 +449,7 @@ TEST_F(BRepGraph_EventBusTest, MultipleSubscribers) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } // Edge layer gets edge events (directly mutated), no face events. @@ -474,7 +474,7 @@ TEST_F(BRepGraph_EventBusTest, DefaultSubscribedKinds_Zero) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } EXPECT_GT(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); } @@ -488,7 +488,7 @@ TEST_F(BRepGraph_EventBusTest, DeferredScope_DispatchesOnDestruction) { BRepGraph_DeferredScope aScope(myGraph); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // During guard scope: no batch dispatch yet. EXPECT_EQ(aLayer->myBatchCallCount, 0); @@ -622,7 +622,7 @@ TEST_F(BRepGraph_EventBusTest, OverlappingSubscription_EdgeAndFace) { BRepGraph_MutGuard aMut = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } // Edge events from direct mutation; NO face events (no parent dispatch). diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx index d9b10f9fd3..fc50880fca 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx @@ -112,7 +112,7 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) if (!pickActiveEdge(anEdgeId)) return false; BRepGraph_MutGuard aMut = theGraph.Editor().Edges().Mut(anEdgeId); - aMut->Tolerance = aMut->Tolerance + 1.0e-4; + theGraph.Editor().Edges().SetTolerance(aMut, aMut->Tolerance + 1.0e-4); return true; } case MutationKind::MutateVertexPoint: { @@ -122,8 +122,9 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) BRepGraph_MutGuard aMut = theGraph.Editor().Vertices().Mut(aVtxId); const gp_Pnt aOld = aMut->Point; std::uniform_real_distribution aDist(-0.1, 0.1); - aMut->Point = - gp_Pnt(aOld.X() + aDist(theRng), aOld.Y() + aDist(theRng), aOld.Z() + aDist(theRng)); + theGraph.Editor().Vertices().SetPoint( + aMut, + gp_Pnt(aOld.X() + aDist(theRng), aOld.Y() + aDist(theRng), aOld.Z() + aDist(theRng))); return true; } case MutationKind::BumpFaceTolerance: { @@ -134,7 +135,7 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) if (theGraph.Topo().Faces().Definition(aFaceId).IsRemoved) return false; BRepGraph_MutGuard aMut = theGraph.Editor().Faces().Mut(aFaceId); - aMut->Tolerance = aMut->Tolerance + 1.0e-4; + theGraph.Editor().Faces().SetTolerance(aMut, aMut->Tolerance + 1.0e-4); return true; } default: diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx index 059501f2df..b7d432c72a 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx @@ -460,7 +460,7 @@ TEST_F(BRepGraph_HistoryTest, SplitEdge_IgnoresRemovedCoEdgeRefEntries) { BRepGraph_MutGuard aMut = myGraph.Editor().CoEdges().MutRef(aRefToRemove); - aMut->IsRemoved = true; + myGraph.Editor().Gen().RemoveRef(aRefToRemove); } ASSERT_TRUE(myGraph.Refs().CoEdges().Entry(aRefToRemove).IsRemoved); const BRepGraph_CoEdgeId aRemovedCoEdgeId = diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx index 64a24e9882..5f850bc7a4 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx @@ -73,7 +73,7 @@ TEST(BRepGraph_MeshCacheTest, CacheStaleAfterFaceMutation) { BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aFaceId); - aGuard->Tolerance += 1.0e-6; + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); } const BRepGraph_MeshCache::FaceMeshEntry* aAfter = aGraph.Mesh().Faces().CachedMesh(aFaceId); @@ -104,7 +104,7 @@ TEST(BRepGraph_MeshCacheTest, CacheStaleAfterSurfaceRepMutation) { BRepGraph_MutGuard aGuard = aGraph.Editor().Reps().MutSurface(aSurfaceRepId); - (void)aGuard; + aGuard.MarkDirty(); } EXPECT_EQ(aGraph.Mesh().Faces().CachedMesh(aFaceId), nullptr) @@ -133,7 +133,7 @@ TEST(BRepGraph_MeshCacheTest, CacheSurvivesUnrelatedMutation) { BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(anOtherFaceId); - aGuard->Tolerance += 1.0e-6; + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); } EXPECT_NE(aGraph.Mesh().Faces().CachedMesh(aFaceId), nullptr) diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx index 0422a4d870..e918246110 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx @@ -88,10 +88,11 @@ TEST(BRepGraph_MutGuardTest, MoveAssignmentFlushesThenTransfers) { BRepGraph_MutGuard aFirst = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); - aFirst->Point = gp_Pnt(1.0, 2.0, 3.0); + aGraph.Editor().Vertices().SetPoint(aFirst, gp_Pnt(1.0, 2.0, 3.0)); BRepGraph_MutGuard aSecond = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId(1)); + aSecond.MarkDirty(); aSecond = std::move(aFirst); // aFirst now inert; aSecond owns what was aFirst. EXPECT_FALSE(static_cast(aFirst)); @@ -118,7 +119,7 @@ TEST(BRepGraph_MutGuardTest, ExceptionInsideScope_StillNotifies) { BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); - aGuard->Point = gp_Pnt(9.0, 9.0, 9.0); + aGraph.Editor().Vertices().SetPoint(aGuard, gp_Pnt(9.0, 9.0, 9.0)); throw std::runtime_error("test: throw mid-scope"); } catch (const std::runtime_error&) @@ -144,6 +145,7 @@ TEST(BRepGraph_MutGuardTest, MovedFrom_DoesNotDoubleNotify) { BRepGraph_MutGuard aSrc = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + aSrc.MarkDirty(); BRepGraph_MutGuard aDst(std::move(aSrc)); // Only aDst's destructor should notify; aSrc's destructor must be a no-op. } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx index 47926ebc80..40a7eb54fb 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx @@ -41,7 +41,7 @@ TEST_F(BRepGraph_MutationGenTest, OwnGen_IncrementedOnMutation) EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).SubtreeGen, 0u); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 1u); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).SubtreeGen, 1u); @@ -49,8 +49,8 @@ TEST_F(BRepGraph_MutationGenTest, OwnGen_IncrementedOnMutation) TEST_F(BRepGraph_MutationGenTest, OwnGen_MultipleIncrements) { - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.1; - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.2; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.2); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 2u); } @@ -58,7 +58,7 @@ TEST_F(BRepGraph_MutationGenTest, OwnGen_MultipleIncrements) TEST_F(BRepGraph_MutationGenTest, OwnGen_DeferredMode) { myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // OwnGen is incremented even in deferred mode. EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 1u); @@ -74,7 +74,7 @@ TEST_F(BRepGraph_MutationGenTest, SubtreeGen_PropagatedParent_Incremented) // Mutate an edge - parent wire/face/shell/solid get SubtreeGen incremented // via propagation, enabling generation-based cache freshness on parents. // Parent OwnGen must NOT change (only the edge itself was directly mutated). - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 1u); EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).SubtreeGen, 1u); @@ -141,7 +141,7 @@ TEST_F(BRepGraph_MutationGenTest, SubtreeGen_DeferredPropagatedParent_Incremente // Deferred mutation + flush. myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); myGraph.Editor().EndDeferredInvalidation(); // Directly mutated edge: OwnGen incremented by exactly 1. @@ -176,7 +176,7 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_SurfacePropagatesSubtreeGenToFace) { BRepGraph_MutGuard aGuard = myGraph.Editor().Reps().MutSurface(aSurfId); - (void)aGuard; + aGuard.MarkDirty(); } // Surface is the face's own geometry - rep mutation IS an own-data change. @@ -197,7 +197,7 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_Curve3DPropagatesSubtreeGenToEdge) { BRepGraph_MutGuard aGuard = myGraph.Editor().Reps().MutCurve3D(aCurveId); - (void)aGuard; + aGuard.MarkDirty(); } // Curve3D is the edge's own geometry - rep mutation IS an own-data change. @@ -221,7 +221,7 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_Curve2DPropagatesSubtreeGenToCoEdg { BRepGraph_MutGuard aGuard = myGraph.Editor().Reps().MutCurve2D(aCurveId); - (void)aGuard; + aGuard.MarkDirty(); } // Curve2D is the coedge's own geometry - rep mutation IS an own-data change. @@ -248,7 +248,7 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_TriangulationPropagatesSubtreeGenT { BRepGraph_MutGuard aGuard = myGraph.Editor().Reps().MutTriangulation(aTriId); - (void)aGuard; + aGuard.MarkDirty(); } // Triangulation is the face's own mesh - rep mutation IS an own-data change. @@ -274,7 +274,7 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_Polygon3DPropagatesSubtreeGenToEdg { BRepGraph_MutGuard aGuard = myGraph.Editor().Reps().MutPolygon3D(aPolyId); - (void)aGuard; + aGuard.MarkDirty(); } // Polygon3D is the edge's own mesh - rep mutation IS an own-data change. diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx new file mode 100644 index 0000000000..546fb7d370 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx @@ -0,0 +1,340 @@ +// Copyright (c) 2026 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +BRepGraph makeBoxGraph() +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aRes = + BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + return aGraph; +} + +BRepGraph_EdgeId makeSelfLoopEdge(BRepGraph& theGraph) +{ + const BRepGraph_VertexId aV = theGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const occ::handle aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0)); + return theGraph.Editor().Edges().Add(aV, aV, aLine, 0.0, 1.0, 1.0e-7); +} + +} // namespace + +// B1: RemoveRef on a Product->Occurrence ref must unbind the reverse-index entry +// keyed by the referenced product (OccurrenceDef::ChildDefId), not by the parent. +TEST(BRepGraph_PermissionUpdateTest, RemoveRef_OccurrenceRef_PreservesProductToOccurrencesIndex) +{ + // Targeted regression for the wrong-key Unbind in GenOps::RemoveRef. Walks the + // reverse index directly because BRepGraphInc_ReverseIndex::Validate currently + // covers only topology references. + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + + const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + ASSERT_TRUE(aParent.IsValid()); + ASSERT_TRUE(aChild.IsValid()); + + BRepGraph_OccurrenceRefId aOccRefId; + const BRepGraph_OccurrenceId aOccId = + aProds.LinkProducts(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); + ASSERT_TRUE(aOccId.IsValid()); + ASSERT_TRUE(aOccRefId.IsValid()); + + bool aFoundBefore = false; + for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aChild)) + if (anId == aOccId) + aFoundBefore = true; + EXPECT_TRUE(aFoundBefore) << "Occurrence must be indexed under referenced product before remove"; + + EXPECT_TRUE(aGraph.Editor().Gen().RemoveRef(BRepGraph_RefId(aOccRefId))); + + for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aChild)) + EXPECT_NE(anId, aOccId) << "RemoveRef must drop the referenced-product entry"; + + // The wrong-key bug would also leave a stale entry under the parent product id. + for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aParent)) + EXPECT_NE(anId, aOccId) << "Bug: Unbind happened against parent product key"; +} + +// B2: removing one CoEdgeRef must leave the wire's other usages of the same edge +// reflected in the deduplicated Edge->Wire reverse index. +// +// Constructs a wire with two CoEdgeRefs whose CoEdges share the same edge so the +// dedup-aware unbind path is exercised; removing one ref must keep the +// (Edge -> Wire) reverse entry alive because the sibling still references it. +TEST(BRepGraph_PermissionUpdateTest, RemoveRef_CoEdge_PreservesEdgeToWireWhenSiblingPresent) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const occ::handle aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0)); + const BRepGraph_EdgeId aEdge = aGraph.Editor().Edges().Add(aV1, aV2, aLine, 0.0, 1.0, 1.0e-7); + ASSERT_TRUE(aEdge.IsValid()); + + NCollection_DynamicArray> anEdges; + anEdges.Append(std::make_pair(aEdge, TopAbs_FORWARD)); + anEdges.Append(std::make_pair(aEdge, TopAbs_REVERSED)); + const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(anEdges); + ASSERT_TRUE(aWireId.IsValid()); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + const BRepGraphInc::WireDef& aWireDef = aGraph.Topo().Wires().Definition(aWireId); + ASSERT_EQ(aWireDef.CoEdgeRefIds.Size(), 2u); + const BRepGraph_CoEdgeRefId aFirstRef = aWireDef.CoEdgeRefIds.First(); + ASSERT_TRUE(aFirstRef.IsValid()); + + auto containsWire = [&](const BRepGraph_EdgeId theE) -> bool { + for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().Wires(theE)) + if (aW == aWireId) + return true; + return false; + }; + ASSERT_TRUE(containsWire(aEdge)) << "Edge->Wire reverse must be present before removal"; + + EXPECT_TRUE(aGraph.Editor().Gen().RemoveRef(BRepGraph_RefId(aFirstRef))); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(containsWire(aEdge)) + << "Sibling CoEdgeRef still references the edge: Edge->Wire entry must survive"; +} + +// B3: SetRefVertexDefId on a self-loop edge (start == end == V_old) must keep +// the V_old->edge entry while the sibling slot still references it. +TEST(BRepGraph_PermissionUpdateTest, SetRefVertexDefId_SelfLoopSibling_KeepsRevIndexValid) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + const BRepGraph_EdgeId aEdge = makeSelfLoopEdge(aGraph); + ASSERT_TRUE(aEdge.IsValid()); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + const BRepGraphInc::EdgeDef& aDef = aGraph.Topo().Edges().Definition(aEdge); + const BRepGraph_VertexRefId aStartRefId = aDef.StartVertexRefId; + ASSERT_TRUE(aStartRefId.IsValid()); + + const BRepGraph_VertexId aNewV = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + + aGraph.Editor().Vertices().SetRefVertexDefId(aStartRefId, aNewV); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + // EndVertexRef still references the original vertex, so the (Vold -> aEdge) + // reverse entry must survive the start-side rebind. + const BRepGraph_VertexId aOldV = + aGraph.Topo().Edges().Definition(aEdge).EndVertexRefId.IsValid() + ? aGraph.Refs() + .Vertices() + .Entry(aGraph.Topo().Edges().Definition(aEdge).EndVertexRefId) + .VertexDefId + : BRepGraph_VertexId(); + ASSERT_TRUE(aOldV.IsValid()); + bool aOldStillIndexed = false; + for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aOldV)) + if (aE == aEdge) + aOldStillIndexed = true; + EXPECT_TRUE(aOldStillIndexed) + << "Vold must remain in EdgesOfVertex(Vold) after start-side rebind"; +} + +// CopyNode must not loop infinitely on a self-referencing compound. +TEST(BRepGraph_PermissionUpdateTest, CopyNode_SelfReferencingCompound_Terminates) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + const BRepGraph_VertexId aV = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + NCollection_DynamicArray aChildren; + aChildren.Append(BRepGraph_NodeId(aV)); + const BRepGraph_CompoundId aRoot = aGraph.Editor().Compounds().Add(aChildren); + ASSERT_TRUE(aRoot.IsValid()); + + // Splice the compound into itself by rewriting its first child ref. + const BRepGraphInc::CompoundDef& aDef = aGraph.Topo().Compounds().Definition(aRoot); + ASSERT_FALSE(aDef.ChildRefIds.IsEmpty()); + const BRepGraph_ChildRefId aChildRefId = aDef.ChildRefIds.First(); + aGraph.Editor().Gen().SetChildRefChildDefId(aChildRefId, BRepGraph_NodeId(aRoot)); + + const BRepGraph aCopy = BRepGraph_Copy::CopyNode(aGraph, + BRepGraph_NodeId(aRoot), + /*copyGeom*/ true, + /*copyMesh*/ false, + /*reserveCache*/ false); + EXPECT_TRUE(aCopy.IsDone()); + EXPECT_TRUE(aCopy.ValidateReverseIndex()); +} + +// TransformNode with copyGeom on an assembly node must reject (returns invalid graph) +// rather than silently produce inconsistent geometry/location state. +TEST(BRepGraph_PermissionUpdateTest, TransformNode_AssemblyWithCopyGeom_Rejected) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + const BRepGraph_ProductId aProd = aGraph.Editor().Products().CreateEmptyProduct(); + ASSERT_TRUE(aProd.IsValid()); + + gp_Trsf aT; + aT.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); + const BRepGraph aResult = + BRepGraph_Transform::TransformNode(aGraph, BRepGraph_NodeId(aProd), aT, true, false); + EXPECT_FALSE(aResult.IsDone()); +} + +// Companion to the above: Occurrence node with copyGeom must be rejected too. +TEST(BRepGraph_PermissionUpdateTest, TransformNode_OccurrenceWithCopyGeom_Rejected) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + ASSERT_TRUE(aParent.IsValid()); + ASSERT_TRUE(aChild.IsValid()); + const BRepGraph_OccurrenceId aOccId = aProds.LinkProducts(aParent, aChild, TopLoc_Location()); + ASSERT_TRUE(aOccId.IsValid()); + + gp_Trsf aT; + aT.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); + const BRepGraph aResult = + BRepGraph_Transform::TransformNode(aGraph, BRepGraph_NodeId(aOccId), aT, true, false); + EXPECT_FALSE(aResult.IsDone()); +} + +// LinkProducts with the unified signature returns the freshly inserted OccurrenceRef +// id through the optional out pointer. +TEST(BRepGraph_PermissionUpdateTest, LinkProducts_OutOccurrenceRefId_Populated) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + ASSERT_TRUE(aParent.IsValid()); + ASSERT_TRUE(aChild.IsValid()); + + BRepGraph_OccurrenceRefId aOccRefId; + const BRepGraph_OccurrenceId aOccId = + aProds.LinkProducts(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); + ASSERT_TRUE(aOccId.IsValid()); + ASSERT_TRUE(aOccRefId.IsValid()); + EXPECT_EQ(aGraph.Refs().Occurrences().Entry(aOccRefId).OccurrenceDefId, aOccId); + + // Default out-pointer = nullptr is also legal. + const BRepGraph_OccurrenceId aOccId2 = aProds.LinkProducts(aParent, aChild, TopLoc_Location()); + EXPECT_TRUE(aOccId2.IsValid()); +} + +// MoveRef: a pure rotation/translation must succeed; a scaled trsf must fail with no +// state change. +TEST(BRepGraph_PermissionUpdateTest, MoveRef_ScaledTrsf_RejectedWithoutMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + + // Find any face ref to test against. + BRepGraph_FaceRefId aFaceRef; + for (BRepGraph_FullFaceRefIterator aIt(aGraph); aIt.More(); aIt.Next()) + { + aFaceRef = aIt.CurrentId(); + break; + } + ASSERT_TRUE(aFaceRef.IsValid()); + + const TopLoc_Location aBefore = aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation; + + gp_Trsf aScaled; + aScaled.SetScaleFactor(2.0); + EXPECT_FALSE(BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aFaceRef), aScaled)); + EXPECT_TRUE(aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation == aBefore); + + gp_Trsf aTrans; + aTrans.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); + EXPECT_TRUE(BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aFaceRef), aTrans)); + EXPECT_FALSE(aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation == aBefore); +} + +// RemoveSubgraph cascade rebuilds the reverse index exactly once at the outermost +// scope; nested calls during recursion must not trigger intermediate rebuilds, and +// the final state must validate. +TEST(BRepGraph_PermissionUpdateTest, RemoveSubgraph_NestedCascade_FinalStateValid) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); + + BRepGraph_SolidId aSolidId; + for (BRepGraph_SolidIterator aIt(aGraph); aIt.More(); aIt.Next()) + { + aSolidId = aIt.CurrentId(); + break; + } + ASSERT_TRUE(aSolidId.IsValid()); + + aGraph.Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aSolidId)); + EXPECT_TRUE(aGraph.ValidateReverseIndex()); +} + +// MutGuard: a guard that observes but never writes must not bump OwnGen on destruction; +// MarkDirty() must force the bump even when no setter was called. +TEST(BRepGraph_PermissionUpdateTest, MutGuard_DirtyFlag_RespectsExplicitMarkAndCleanScope) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_TRUE(aGraph.IsDone()); + + BRepGraph_EdgeId aEdgeId; + for (BRepGraph_EdgeIterator anIt(aGraph); anIt.More(); anIt.Next()) + { + aEdgeId = anIt.CurrentId(); + break; + } + ASSERT_TRUE(aEdgeId.IsValid()); + + const uint32_t aOwnGenBefore = aGraph.Topo().Edges().Definition(aEdgeId).OwnGen; + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Edges().Mut(aEdgeId); + (void)aGuard->IsClosed; // read-only access only + } + EXPECT_EQ(aOwnGenBefore, aGraph.Topo().Edges().Definition(aEdgeId).OwnGen) + << "Read-only guard scope must not bump OwnGen"; + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Edges().Mut(aEdgeId); + aGuard.MarkDirty(); + } + EXPECT_GT(aGraph.Topo().Edges().Definition(aEdgeId).OwnGen, aOwnGenBefore) + << "MarkDirty() must bump OwnGen on guard destruction"; +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx index 68a7ada2b8..50204b3e3b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx @@ -575,7 +575,8 @@ TEST(BRepGraph_ReconstructTest, AfterVertexMutation_ModifiedFlagAndPointChanged) { BRepGraph_MutGuard aMutVtx = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId(aVertIdx)); - aMutVtx->Point = gp_Pnt(anOldPt.X(), anOldPt.Y(), anOldPt.Z() + 5.0); + aGraph.Editor().Vertices().SetPoint(aMutVtx, + gp_Pnt(anOldPt.X(), anOldPt.Y(), anOldPt.Z() + 5.0)); } // Verify the OwnGen is incremented on the vertex def (directly mutated). @@ -606,7 +607,7 @@ TEST(BRepGraph_ReconstructTest, AfterToleranceMutation_NewTShape) { BRepGraph_MutGuard aMutEdge = aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMutEdge->Tolerance = aMutEdge->Tolerance + 1.0; + aGraph.Editor().Edges().SetTolerance(aMutEdge, aMutEdge->Tolerance + 1.0); } // After mutation, Shape() should return a reconstructed shape with a different TShape. diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx index 0be355a9e0..c9b9b6c178 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx @@ -446,7 +446,9 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_UpdatesRefStampAndParentModifiedFlag) { BRepGraph_MutGuard aMut = aGraph.Editor().Faces().MutRef(aFaceRefId); - aMut->Orientation = (anBeforeOri == TopAbs_FORWARD) ? TopAbs_REVERSED : TopAbs_FORWARD; + aGraph.Editor().Faces().SetRefOrientation(aMut, + (anBeforeOri == TopAbs_FORWARD) ? TopAbs_REVERSED + : TopAbs_FORWARD); } const BRepGraphInc::FaceRef& aAfterEntry = aGraph.Refs().Faces().Entry(aFaceRefId); @@ -477,10 +479,7 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_MarkRemoved_PersistsAndInvalidatesStamp) ASSERT_TRUE(aBeforeStamp.IsValid()); ASSERT_TRUE(aBeforeStamp.IsRefStamp()); - { - BRepGraph_MutGuard aMut = aGraph.Editor().Faces().MutRef(aFaceRefId); - aMut->IsRemoved = true; - } + aGraph.Editor().Gen().RemoveRef(aFaceRefId); const BRepGraphInc::FaceRef& aAfterEntry = aGraph.Refs().Faces().Entry(aFaceRefId); EXPECT_TRUE(aAfterEntry.IsRemoved); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx index d50006a7bd..c00303b844 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx @@ -226,7 +226,7 @@ TEST_F(BRepGraph_RefsIteratorTest, AuxChildRefsOfShellAndSolid_EnumerateInjected for (const BRepGraph_ChildRefId& aRefId : myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) { - aShell->AuxChildRefIds.Append(aRefId); + aShell.Internal().AuxChildRefIds.Append(aRefId); } } @@ -251,7 +251,7 @@ TEST_F(BRepGraph_RefsIteratorTest, AuxChildRefsOfShellAndSolid_EnumerateInjected for (const BRepGraph_ChildRefId& aRefId : myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) { - aSolid->AuxChildRefIds.Append(aRefId); + aSolid.Internal().AuxChildRefIds.Append(aRefId); } } @@ -271,11 +271,7 @@ TEST_F(BRepGraph_RefsIteratorTest, RemovedWireRef_IsSkipped) myGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()); ASSERT_EQ(aWireRefs.Length(), 1); - { - BRepGraph_MutGuard aWireRef = - myGraph.Editor().Wires().MutRef(aWireRefs.Value(0)); - aWireRef->IsRemoved = true; - } + myGraph.Editor().Gen().RemoveRef(aWireRefs.Value(0)); EXPECT_EQ(countIterator(BRepGraph_RefsWireOfFace(myGraph, BRepGraph_FaceId::Start())), 0); } \ No newline at end of file diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx index 191bad262a..2af8b92732 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx @@ -118,7 +118,7 @@ TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRound { BRepGraph_MutGuard aMut = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); - aMut->Point = gp_Pnt(anOldPt.X() + 50.0, anOldPt.Y(), anOldPt.Z()); + aGraph.Editor().Vertices().SetPoint(aMut, gp_Pnt(anOldPt.X() + 50.0, anOldPt.Y(), anOldPt.Z())); } // --- Audit again: structural invariants must survive a point-data mutation --- @@ -233,7 +233,7 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsis const double anOldTol = aGraph.Topo().Edges().Definition(aSeamEdgeId).Tolerance; { BRepGraph_MutGuard aMut = aGraph.Editor().Edges().Mut(aSeamEdgeId); - aMut->Tolerance = anOldTol + 0.5; + aGraph.Editor().Edges().SetTolerance(aMut, anOldTol + 0.5); } // --- Validate(Audit): structural integrity must survive a tolerance mutation --- @@ -342,7 +342,7 @@ TEST(BRepGraph_ScenarioMatrix, CompSolid_TwoBoxes_BothSubsystemsMutateReconstruc { BRepGraph_MutGuard aMut = aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - aMut->Tolerance = aMut->Tolerance + 1.0; + aGraph.Editor().Edges().SetTolerance(aMut, aMut->Tolerance + 1.0); } // --- Validate(Audit) after mutation --- @@ -616,7 +616,7 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe { BRepGraph_MutGuard aMut = aGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aMut->Tolerance = aMut->Tolerance + 0.5; + aGraph.Editor().Faces().SetTolerance(aMut, aMut->Tolerance + 0.5); } // --- Validate(Audit) after mutation --- diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx index 834f8a3436..8dab418d9c 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx @@ -1259,7 +1259,7 @@ TEST_F(BRepGraphTest, MutableEdge_ModifyTolerance) double anOrigTol = BRepGraph_Tool::Edge::Tolerance(myGraph, BRepGraph_EdgeId::Start()); BRepGraph_MutGuard anEdge = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - anEdge->Tolerance = anOrigTol * 2.0; + myGraph.Editor().Edges().SetTolerance(anEdge, anOrigTol * 2.0); EXPECT_NEAR(BRepGraph_Tool::Edge::Tolerance(myGraph, BRepGraph_EdgeId::Start()), anOrigTol * 2.0, 1.0e-15); @@ -1552,7 +1552,7 @@ TEST_F(BRepGraphTest, OwnGen_MutableEdge_PropagatesSubtreeGenUp) { EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); - (void)myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); + myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()).MarkDirty(); // Edge was directly mutated: OwnGen incremented. EXPECT_GT(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); @@ -1619,7 +1619,7 @@ TEST_F(BRepGraphTest, Shape_InvalidatedAfterMutation) TopoDS_Shape aBefore = myGraph.Shapes().Shape(anEdgeId); EXPECT_FALSE(aBefore.IsNull()); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.123; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.123); TopoDS_Shape anAfter = myGraph.Shapes().Shape(anEdgeId); EXPECT_FALSE(anAfter.IsNull()); @@ -2242,7 +2242,7 @@ TEST_F(BRepGraphTest, MutableVertex_ChangePoint_Verified) { BRepGraph_MutGuard aMutVert = myGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); - aMutVert->Point = gp_Pnt(99.0, 99.0, 99.0); + myGraph.Editor().Vertices().SetPoint(aMutVert, gp_Pnt(99.0, 99.0, 99.0)); const BRepGraphInc::VertexDef& aVert = myGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()); @@ -2335,8 +2335,8 @@ TEST_F(BRepGraphTest, DetectToleranceConflicts_ManualConflict_Detected) continue; // Set very different tolerances on two edges sharing the same curve. - myGraph.Editor().Edges().Mut(anEdgeId)->Tolerance = 0.001; - myGraph.Editor().Edges().Mut(anOtherId)->Tolerance = 1.0; + myGraph.Editor().Edges().SetTolerance(anEdgeId, 0.001); + myGraph.Editor().Edges().SetTolerance(anOtherId, 1.0); isConflictSetUp = true; break; @@ -2604,12 +2604,12 @@ TEST_F(BRepGraphTest, MutableWireDef_ModifyClosure_Verified) BRepGraph_MutGuard aMutWD = myGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()); bool anOrigClosed = aMutWD->IsClosed; - aMutWD->IsClosed = !anOrigClosed; + myGraph.Editor().Wires().SetIsClosed(aMutWD, !anOrigClosed); EXPECT_EQ(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).IsClosed, !anOrigClosed); // Restore original state. - myGraph.Editor().Wires().Mut(BRepGraph_WireId::Start())->IsClosed = anOrigClosed; + myGraph.Editor().Wires().SetIsClosed(BRepGraph_WireId::Start(), anOrigClosed); EXPECT_EQ(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).IsClosed, anOrigClosed); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx index 89212cca62..ffffbe7030 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx @@ -14,6 +14,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -22,10 +29,17 @@ #include #include #include +#include +#include +#include +#include #include #include #include +#include +#include #include +#include #include #include #include @@ -202,8 +216,449 @@ TEST(BRepGraph_TransformTest, TransformSingleFace) gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(10.0, 20.0, 30.0)); - BRepGraph aResultGraph = - BRepGraph_Transform::TransformFace(aGraph, BRepGraph_FaceId::Start(), aTrsf, true); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); + BRepGraph aResultGraph = BRepGraph_Transform::TransformNode(aGraph, aFaceNode, aTrsf, true); ASSERT_TRUE(aResultGraph.IsDone()); EXPECT_EQ(aResultGraph.Topo().Faces().Nb(), 1); } + +TEST(BRepGraph_TransformTest, CopyMesh_TriangulationNodesTransformed) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = BRepGraph_Builder::Add(aGraph, aBox); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + // Manually create a triangulation with known node positions on the first face. + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aSrcTri = new Poly_Triangulation(3, 1, false); + aSrcTri->SetNode(1, gp_Pnt(1.0, 2.0, 3.0)); + aSrcTri->SetNode(2, gp_Pnt(4.0, 5.0, 6.0)); + aSrcTri->SetNode(3, gp_Pnt(7.0, 8.0, 9.0)); + aSrcTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aSrcTri->Deflection(0.1); + + const BRepGraph_TriangulationRepId aTriRepId = + BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aSrcTri); + aGraph.Editor().Faces().SetTriangulationRep(aFaceId, aTriRepId); + BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aTriRepId); + + const double aDx = 5.0, aDy = 10.0, aDz = 15.0; + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(aDx, aDy, aDz)); + + // Transform with theCopyMesh = true. + BRepGraph aResult = BRepGraph_Transform::Perform(aGraph, aTrsf, true, true); + ASSERT_TRUE(aResult.IsDone()); + + // The persistent triangulation on the first face must be present and transformed. + const BRepGraph_TriangulationRepId aNewTriRepId = + aResult.Topo().Faces().Definition(aFaceId).TriangulationRepId; + ASSERT_TRUE(aNewTriRepId.IsValid(aResult.Mesh().Poly().NbTriangulations())); + + const occ::handle& aNewTri = + aResult.Mesh().Poly().TriangulationRep(aNewTriRepId).Triangulation; + ASSERT_FALSE(aNewTri.IsNull()); + ASSERT_EQ(aNewTri->NbNodes(), 3); + + EXPECT_NEAR(aNewTri->Node(1).X(), 1.0 + aDx, Precision::Confusion()); + EXPECT_NEAR(aNewTri->Node(1).Y(), 2.0 + aDy, Precision::Confusion()); + EXPECT_NEAR(aNewTri->Node(1).Z(), 3.0 + aDz, Precision::Confusion()); + EXPECT_NEAR(aNewTri->Node(3).X(), 7.0 + aDx, Precision::Confusion()); + + // The source triangulation must not be mutated. + EXPECT_NEAR(aSrcTri->Node(1).X(), 1.0, Precision::Confusion()); + + // Deflection must scale (translation has scale = 1, so no change). + EXPECT_NEAR(aNewTri->Deflection(), 0.1, Precision::Confusion()); +} + +TEST(BRepGraph_TransformTest, CopyMesh_False_TriangulationInvalidated) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = BRepGraph_Builder::Add(aGraph, aBox); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aTri = new Poly_Triangulation(3, 1, false); + aTri->SetNode(1, gp_Pnt(0, 0, 0)); + aTri->SetNode(2, gp_Pnt(1, 0, 0)); + aTri->SetNode(3, gp_Pnt(0, 1, 0)); + aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + const BRepGraph_TriangulationRepId aTriRepId = + BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aTri); + aGraph.Editor().Faces().SetTriangulationRep(aFaceId, aTriRepId); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); + + // Default: theCopyMesh = false -> triangulations are discarded. + BRepGraph aResult = BRepGraph_Transform::Perform(aGraph, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + const BRepGraph_TriangulationRepId aResultTriRepId = + aResult.Topo().Faces().Definition(aFaceId).TriangulationRepId; + EXPECT_FALSE(aResultTriRepId.IsValid()); + EXPECT_FALSE(aResult.Mesh().Faces().HasTriangulation(aFaceId)); +} + +TEST(BRepGraph_TransformTest, MoveRef_FaceRef_LocationComposed) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + // The solid has shell refs; grab the first one. + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + ASSERT_GE(aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Length(), 1); + const BRepGraph_ShellRefId aShellRef = + aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Value(0); + + // Verify that the location starts as identity. + EXPECT_TRUE(aGraph.Refs().Shells().Entry(aShellRef).LocalLocation.IsIdentity()); + + const double aDx = 42.0; + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(aDx, 0.0, 0.0)); + + const bool aOk = BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aTrsf); + EXPECT_TRUE(aOk); + + const TopLoc_Location& aLoc = aGraph.Refs().Shells().Entry(aShellRef).LocalLocation; + EXPECT_FALSE(aLoc.IsIdentity()); + EXPECT_NEAR(aLoc.Transformation().Value(1, 4), aDx, Precision::Confusion()); + + // A second MoveRef composes (doubles the translation). + BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aTrsf); + const TopLoc_Location& aLoc2 = aGraph.Refs().Shells().Entry(aShellRef).LocalLocation; + EXPECT_NEAR(aLoc2.Transformation().Value(1, 4), 2.0 * aDx, Precision::Confusion()); +} + +TEST(BRepGraph_TransformTest, MoveRef_ScaleRejected) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_ShellRefId aShellRef = + aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Value(0); + + gp_Trsf aScale; + aScale.SetScale(gp_Pnt(), 2.0); // scale factor != 1 + + const bool aOk = BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aScale); + EXPECT_FALSE(aOk); + // Location must remain unchanged (identity). + EXPECT_TRUE(aGraph.Refs().Shells().Entry(aShellRef).LocalLocation.IsIdentity()); +} + +TEST(BRepGraph_TransformTest, TransformNode_FaceKind_VertexPointsShifted) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const double aDx = 5.0, aDy = 10.0, aDz = 15.0; + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(aDx, aDy, aDz)); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aFaceNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + EXPECT_EQ(aResult.Topo().Faces().Nb(), 1); + + const int aNbV = aResult.Topo().Vertices().Nb(); + ASSERT_GT(aNbV, 0); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.X(), anOrig.X() + aDx, Precision::Confusion()); + EXPECT_NEAR(aTrans.Y(), anOrig.Y() + aDy, Precision::Confusion()); + EXPECT_NEAR(aTrans.Z(), anOrig.Z() + aDz, Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_ShellKind) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); + + const BRepGraph_ShellId aShellId = BRepGraph_ShellId::Start(); + const BRepGraph_NodeId aShellNode(BRepGraph_NodeId::Kind::Shell, aShellId.Index); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aShellNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + // The shell copy should have the same number of faces as the source shell. + EXPECT_EQ(aResult.Topo().Shells().Nb(), 1); + EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); + + // All vertices must be shifted by the translation. + const int aNbV = aGraph.Topo().Vertices().Nb(); + EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.X(), anOrig.X() + 100.0, Precision::Confusion()); + EXPECT_NEAR(aTrans.Y(), anOrig.Y(), Precision::Confusion()); + EXPECT_NEAR(aTrans.Z(), anOrig.Z(), Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_SolidKind) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Solids().Nb(), 1); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_NodeId aSolidNode(BRepGraph_NodeId::Kind::Solid, aSolidId.Index); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(0.0, 50.0, 0.0)); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + EXPECT_EQ(aResult.Topo().Solids().Nb(), 1); + EXPECT_EQ(aResult.Topo().Shells().Nb(), aGraph.Topo().Shells().Nb()); + EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); + + const int aNbV = aGraph.Topo().Vertices().Nb(); + EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.Y(), anOrig.Y() + 50.0, Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_VertexKind) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Vertices().Nb(), 1); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + const BRepGraph_NodeId aVertexNode(BRepGraph_NodeId::Kind::Vertex, aVertexId.Index); + const gp_Pnt anOrigPt = BRepGraph_Tool::Vertex::Pnt(aGraph, aVertexId); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aVertexNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + EXPECT_EQ(aResult.Topo().Vertices().Nb(), 1); + const gp_Pnt aTransPt = BRepGraph_Tool::Vertex::Pnt(aResult, BRepGraph_VertexId::Start()); + EXPECT_NEAR(aTransPt.X(), anOrigPt.X() + 1.0, Precision::Confusion()); + EXPECT_NEAR(aTransPt.Y(), anOrigPt.Y() + 2.0, Precision::Confusion()); + EXPECT_NEAR(aTransPt.Z(), anOrigPt.Z() + 3.0, Precision::Confusion()); +} + +TEST(BRepGraph_TransformTest, TransformNode_CompoundKind) +{ + // ensureCompound must walk children, transform each, and preserve count. + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + BRepPrimAPI_MakeBox aBox1(5.0, 5.0, 5.0); + BRepPrimAPI_MakeBox aBox2(3.0, 3.0, 3.0); + aBB.Add(aCompound, aBox1.Shape()); + aBB.Add(aCompound, aBox2.Shape()); + + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCompound); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Compounds().Nb(), 1); + + const BRepGraph_CompoundId aCompoundId = BRepGraph_CompoundId::Start(); + const BRepGraph_NodeId aCompoundNode(BRepGraph_NodeId::Kind::Compound, aCompoundId.Index); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aCompoundNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + EXPECT_EQ(aResult.Topo().Compounds().Nb(), 1); + EXPECT_EQ(aResult.Topo().Solids().Nb(), aGraph.Topo().Solids().Nb()); + EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); + + const int aNbV = aGraph.Topo().Vertices().Nb(); + EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.X(), anOrig.X() + 20.0, Precision::Confusion()); + EXPECT_NEAR(aTrans.Y(), anOrig.Y(), Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_CompSolidKind) +{ + // ensureCompSolid must walk solid refs and transform each. + BRep_Builder aBB; + TopoDS_CompSolid aCompSolid; + aBB.MakeCompSolid(aCompSolid); + BRepPrimAPI_MakeBox aBoxMaker(4.0, 4.0, 4.0); + aBB.Add(aCompSolid, aBoxMaker.Shape()); + + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aCompSolid); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().CompSolids().Nb(), 1); + + const BRepGraph_CompSolidId aCompSolidId = BRepGraph_CompSolidId::Start(); + const BRepGraph_NodeId aCompSolidNode(BRepGraph_NodeId::Kind::CompSolid, aCompSolidId.Index); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(0.0, 0.0, 7.0)); + + BRepGraph aResult = + BRepGraph_Transform::TransformNode(aGraph, aCompSolidNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + + EXPECT_EQ(aResult.Topo().CompSolids().Nb(), 1); + EXPECT_EQ(aResult.Topo().Solids().Nb(), aGraph.Topo().Solids().Nb()); + + const int aNbV = aGraph.Topo().Vertices().Nb(); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.Z(), anOrig.Z() + 7.0, Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_NegativeScale_VertexPointsMirrored) +{ + // Smoke-tests the negative-scale geometry path through TransformNode: every + // vertex point should be mirrored about the origin and the result graph must + // remain coherent (same face/edge/vertex counts, valid IsDone). + BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_NodeId aSolidNode(BRepGraph_NodeId::Kind::Solid, aSolidId.Index); + + gp_Trsf aTrsf; + aTrsf.SetMirror(gp_Pnt(0.0, 0.0, 0.0)); + + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, false); + ASSERT_TRUE(aResult.IsDone()); + EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); + EXPECT_EQ(aResult.Topo().Vertices().Nb(), aGraph.Topo().Vertices().Nb()); + + const int aNbV = aGraph.Topo().Vertices().Nb(); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); + EXPECT_NEAR(aTrans.X(), -anOrig.X(), Precision::Confusion()); + EXPECT_NEAR(aTrans.Y(), -anOrig.Y(), Precision::Confusion()); + EXPECT_NEAR(aTrans.Z(), -anOrig.Z(), Precision::Confusion()); + } +} + +TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_LODCacheSurvives) +{ + // Regression test for self-aliasing bug in applyMeshCopy: when TransformNode is called + // with theCopyGeom=true and theCopyMesh=true, the LOD face cache entries must survive. + // Previously, applyMeshCopy(aSubgraph, aSubgraph, ...) would clear the face cache before + // reading from it (source==dest), silently dropping all cached LOD triangulation entries. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = + BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + ASSERT_TRUE(aGraph.IsDone()); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + // Attach a LOD cache entry on the first face. + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLodTri1 = new Poly_Triangulation(3, 1, false); + aLodTri1->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aLodTri1->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); + aLodTri1->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); + aLodTri1->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aLodTri1->Deflection(0.5); + occ::handle aLodTri2 = new Poly_Triangulation(3, 1, false); + aLodTri2->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aLodTri2->SetNode(2, gp_Pnt(2.0, 0.0, 0.0)); + aLodTri2->SetNode(3, gp_Pnt(0.0, 2.0, 0.0)); + aLodTri2->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aLodTri2->Deflection(1.0); + + const BRepGraph_TriangulationRepId aRep1 = + BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aLodTri1); + const BRepGraph_TriangulationRepId aRep2 = + BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aLodTri2); + BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aRep1); + BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aRep2); + BRepGraph_Tool::Mesh::SetCachedActiveIndex(aGraph, aFaceId, 1); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_NodeId aSolidNode = + BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, aSolidId.Index); + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(5.0, 0.0, 0.0)); + + // This is the previously failing path: theCopyGeom=true, theCopyMesh=true. + BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, true); + ASSERT_TRUE(aResult.IsDone()); + + // Both LOD cache entries must survive the transform. + const BRepGraph_MeshCache::FaceMeshEntry* aEntry = aResult.Mesh().Faces().CachedMesh(aFaceId); + ASSERT_NE(aEntry, nullptr); + EXPECT_TRUE(aEntry->IsPresent()); + EXPECT_EQ(aEntry->TriangulationRepIds.Length(), 2); + EXPECT_EQ(aEntry->ActiveTriangulationIndex, 1); + + // The cached triangulation nodes must be shifted by the translation. + const BRepGraph_TriangulationRepId aResRep1 = aEntry->TriangulationRepIds.Value(0); + ASSERT_TRUE(aResRep1.IsValid(aResult.Mesh().Poly().NbTriangulations())); + const occ::handle& aResTri1 = + aResult.Mesh().Poly().TriangulationRep(aResRep1).Triangulation; + ASSERT_FALSE(aResTri1.IsNull()); + EXPECT_NEAR(aResTri1->Node(2).X(), 1.0 + 5.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx index 73fefc3915..bb410f3db1 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx @@ -212,7 +212,7 @@ TEST(BRepGraph_ValidateTest, WireConnectivity_DisconnectedEdges) { BRepGraph_MutGuard aMutEndRef = aGraph.Editor().Vertices().MutRef(aFirstEdge->EndVertexRefId); - aMutEndRef->VertexDefId = aVertexId; + aMutEndRef.Internal().VertexDefId = aVertexId; break; } } @@ -256,7 +256,7 @@ TEST(BRepGraph_ValidateTest, BoundsCheck_InvalidIndex) // Corrupt edge's Curve3d to null. BRepGraph_MutGuard anEdge = aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - anEdge->Curve3DRepId = BRepGraph_Curve3DRepId(); + anEdge.Internal().Curve3DRepId = BRepGraph_Curve3DRepId(); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -335,7 +335,7 @@ TEST(BRepGraph_ValidateTest, CorruptedPCurve_FaceDefIdOutOfBounds) ASSERT_GT(aGraph.Topo().CoEdges().Nb(), 0); BRepGraph_MutGuard aCoEdgeDef = aGraph.Editor().CoEdges().Mut(BRepGraph_CoEdgeId::Start()); - aCoEdgeDef->FaceDefId = BRepGraph_FaceId(aGraph.Topo().Faces().Nb() + 999); + aCoEdgeDef.Internal().FaceDefId = BRepGraph_FaceId(aGraph.Topo().Faces().Nb() + 999); const BRepGraph_Validate::Result aDefaultResult = BRepGraph_Validate::Perform(aGraph); EXPECT_FALSE(aDefaultResult.IsValid()); @@ -363,7 +363,7 @@ TEST(BRepGraph_ValidateTest, LightweightAndAudit_DetectActiveCountDrift) // Intentionally bypass RemoveNode() to simulate counter drift bug class. BRepGraph_MutGuard aFaceDef = aGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aFaceDef->IsRemoved = true; + aFaceDef.Internal().IsRemoved = true; const BRepGraph_Validate::Result aLightResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); @@ -509,7 +509,7 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_CorruptedOccurrenceChildDefId_Detecte // Corrupt the first occurrence's ChildDefId to an out-of-bounds value. BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId::Start()); - anOccDef->ChildDefId = + anOccDef.Internal().ChildDefId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, aGraph.Topo().Solids().Nb() + 999); const BRepGraph_Validate::Result aAuditResult = @@ -559,7 +559,7 @@ TEST(BRepGraph_ValidateTest, // Corrupt the occurrence's ChildDefId to an invalid index. BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(anOccId); - anOccDef->ChildDefId = + anOccDef.Internal().ChildDefId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Product, aGraph.Topo().Products().Nb() + 999); const BRepGraph_Validate::Result aAuditResult = @@ -596,7 +596,7 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_OccurrenceChildRefersToOccurrence_Det BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId::Start()); - anOccDef->ChildDefId = BRepGraph_OccurrenceId::Start(); + anOccDef.Internal().ChildDefId = BRepGraph_OccurrenceId::Start(); const BRepGraph_Validate::Result aAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -698,8 +698,8 @@ TEST(BRepGraph_ValidateTest, Audit_DetectsOrphanWireRef_AfterFaceRemoval) if (aRef.IsRemoved) continue; BRepGraph_MutGuard aMut = aGraph.Editor().Wires().MutRef(aRefId); - aMut->ParentId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 9999); - aDidCorrupt = true; + aMut.Internal().ParentId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 9999); + aDidCorrupt = true; } ASSERT_TRUE(aDidCorrupt); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx index 7e0e25f9ea..87a3803188 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx @@ -77,7 +77,7 @@ TEST_F(BRepGraph_VersionStampTest, IsStale_MutatedNode_ReturnsTrue) const BRepGraph_VersionStamp aStamp = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); // Mutate the face. - myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start())->NaturalRestriction = true; + myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); EXPECT_TRUE(myGraph.UIDs().IsStale(aStamp)); } @@ -111,7 +111,7 @@ TEST_F(BRepGraph_VersionStampTest, IsStale_DeferredMode_TracksCorrectly) // Mutate in deferred mode. myGraph.Editor().BeginDeferredInvalidation(); - myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start())->Tolerance = 0.5; + myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); myGraph.Editor().EndDeferredInvalidation(); EXPECT_TRUE(myGraph.UIDs().IsStale(aStamp)); @@ -150,7 +150,7 @@ TEST_F(BRepGraph_VersionStampTest, IsSameNode_DifferentVersion_StillSameNode) { const BRepGraph_VersionStamp aStampBefore = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); - myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start())->NaturalRestriction = true; + myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); const BRepGraph_VersionStamp aStampAfter = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); @@ -212,7 +212,7 @@ TEST_F(BRepGraph_VersionStampTest, ToGUID_DifferentMutationGen_DifferentGUID) const Standard_GUID& aGraph = myGraph.UIDs().GraphGUID(); const Standard_GUID aGUIDBefore = aStampBefore.ToGUID(aGraph); - myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start())->NaturalRestriction = true; + myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); const BRepGraph_VersionStamp aStampAfter = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); const Standard_GUID aGUIDAfter = aStampAfter.ToGUID(aGraph); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx index 874d1a4cc3..556d2362a1 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx @@ -485,7 +485,7 @@ TEST_F(BRepGraph_ViewsTest, TopoView_GroupedProductAndOccurrenceOps_Parity) { BRepGraph_MutGuard anOccurrenceRef = myGraph.Editor().Occurrences().MutRef(aOccurrenceRefs.Value(0)); - anOccurrenceRef->IsRemoved = true; + myGraph.Editor().Gen().RemoveRef(aOccurrenceRefs.Value(0)); } EXPECT_EQ(myGraph.Topo().Products().NbComponents(aSubAssembly), 0); @@ -683,7 +683,7 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_MutFace_InvalidatesEntry) { BRepGraph_MutGuard aFace = myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aFace->Tolerance += 0.1; + myGraph.Editor().Faces().SetTolerance(aFace, aFace->Tolerance + 0.1); } EXPECT_FALSE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); @@ -714,7 +714,7 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_MutFaceRef_InvalidatesEntry) { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aRef); - aFaceRef->Orientation = TopAbs::Reverse(aFaceRef->Orientation); + myGraph.Editor().Faces().SetRefOrientation(aFaceRef, TopAbs::Reverse(aFaceRef->Orientation)); } EXPECT_FALSE(myGraph.Cache().Has(aRef, testUserAttrKind())); @@ -730,7 +730,7 @@ TEST_F(BRepGraph_ViewsTest, AttrsView_RemoveFaceRef_HidesCacheKindIterator) { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aRef); - aFaceRef->IsRemoved = true; + myGraph.Editor().Gen().RemoveRef(aRef); } EXPECT_FALSE(myGraph.Cache().Has(aRef, testUserAttrKind())); @@ -792,7 +792,7 @@ TEST_F(BRepGraph_ViewsTest, RefsView_FaceRefIdsOf_LocalFilteringHandlesRemoved) { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aFaceRefs.Value(0)); - aFaceRef->IsRemoved = true; + myGraph.Editor().Gen().RemoveRef(aFaceRefs.Value(0)); } EXPECT_EQ( @@ -831,8 +831,8 @@ TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_RoundTripForTypedRef) { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aFaceRefId); - aFaceRef->LocalLocation = TopLoc_Location(aTrsf); - aFaceRef->Orientation = TopAbs_REVERSED; + myGraph.Editor().Faces().SetRefLocalLocation(aFaceRef, TopLoc_Location(aTrsf)); + myGraph.Editor().Faces().SetRefOrientation(aFaceRef, TopAbs_REVERSED); } const BRepGraphInc::FaceRef& aFaceRefEntry = myGraph.Refs().Faces().Entry(aFaceRefId); @@ -907,7 +907,7 @@ TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_InvalidAndRemoved) { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aFaceRefId); - aFaceRef->IsRemoved = true; + myGraph.Editor().Gen().RemoveRef(aFaceRefId); } EXPECT_TRUE(myGraph.Refs().IsRemoved(aFaceRefId)); @@ -998,6 +998,7 @@ TEST_F(BRepGraph_ViewsTest, MutView_EdgeDef_IncrementsOwnGen) { BRepGraph_MutGuard anEdge = myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); + anEdge.MarkDirty(); } EXPECT_GT(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, 0u); } diff --git a/src/ModelingData/TKBRep/GTests/FILES.cmake b/src/ModelingData/TKBRep/GTests/FILES.cmake index d7453e57df..cd132501fd 100644 --- a/src/ModelingData/TKBRep/GTests/FILES.cmake +++ b/src/ModelingData/TKBRep/GTests/FILES.cmake @@ -19,6 +19,7 @@ set(OCCT_TKBRep_GTests_FILES BRepGraph_Fuzz_Test.cxx BRepGraph_MeshCache_Test.cxx BRepGraph_MutGuard_Test.cxx + BRepGraph_PermissionUpdate_Test.cxx BRepGraph_ReplaceVertex_Test.cxx BRepGraph_MutationGen_Test.cxx BRepGraph_Convenience_Test.cxx