From 03856df0b9e264f5538acb83d2f444508ca17798 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Mon, 15 Jun 2026 15:46:00 +0100 Subject: [PATCH] Modeling Data - Fix BRepGraph topology placement population (#1311) - Kept TopoDS definition data local while storing root and compound placements on graph references. - Preserved located triangulation and polygon-on-triangulation data during population. - Preserved degenerate natural boundary edges and cleared full-graph state consistently in Perform(). - Kept original TopoDS shape lookup bindings valid across population and compaction. - Rebuilt compacted compounds in two passes so nested compound references keep their local placement. - Added focused regression coverage for located meshes, nested compounds, shape lookup, compaction, degenerate boundaries, and reset behavior. --- .../TKBRep/BRepGraph/BRepGraph_Compact.cxx | 74 ++- .../TKBRep/BRepGraph/BRepGraph_ShapesView.cxx | 185 ++++++- .../TKBRep/BRepGraph/BRepGraph_ShapesView.hxx | 16 +- .../BRepGraphInc/BRepGraphInc_Populate.cxx | 402 ++++++++------ .../BRepGraphInc/BRepGraphInc_Populate.hxx | 4 +- .../BRepGraphInc/BRepGraphInc_Storage.cxx | 149 +++++- .../BRepGraphInc/BRepGraphInc_Storage.hxx | 72 ++- .../BRepGraphInc_PopulateRegression_Test.cxx | 501 ++++++++++++++++++ .../TKBRep/GTests/BRepGraphInc_Test.cxx | 44 +- .../TKBRep/GTests/BRepGraph_Assembly_Test.cxx | 40 +- .../TKBRep/GTests/BRepGraph_Geometry_Test.cxx | 17 +- .../TKBRep/GTests/BRepGraph_Sharing_Test.cxx | 38 +- src/ModelingData/TKBRep/GTests/FILES.cmake | 1 + 13 files changed, 1183 insertions(+), 360 deletions(-) create mode 100644 src/ModelingData/TKBRep/GTests/BRepGraphInc_PopulateRegression_Test.cxx diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx index d04d1e973f..6dc7e9e5f8 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx @@ -786,16 +786,29 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aNewDef.UID = anIt.Current().UID; } - // Compounds. - NCollection_LinearVector aCompChildren(64); - NCollection_LinearVector aCompOldChildRefs(64); + // Compounds may reference other compounds. Create all compound definitions first so parent + // compounds can attach child refs to compound children independently of storage order. for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { - aCompChildren.Clear(false); - aCompOldChildRefs.Clear(false); + const BRepGraph_CompoundId anOldCompoundId = anIt.CurrentId(); + const BRepGraph_CompoundId* aExpectedNewId = aCompoundMap.Seek(anOldCompoundId); + if (aExpectedNewId == nullptr) + { + continue; + } - const BRepGraph_CompoundId anOldCompoundId = anIt.CurrentId(); - if (aCompoundMap.Seek(anOldCompoundId) == nullptr) + const NCollection_Array1 anEmptyChildren; + [[maybe_unused]] const BRepGraph_CompoundId aNewCompoundId = + aNewGraph.Editor().Compounds().Add(anEmptyChildren); + Standard_ASSERT_RAISE(aNewCompoundId == *aExpectedNewId, + "BRepGraph_Compact: unexpected compound id"); + } + + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CompoundId anOldCompoundId = anIt.CurrentId(); + const BRepGraph_CompoundId* aNewCompoundId = aCompoundMap.Seek(anOldCompoundId); + if (aNewCompoundId == nullptr) { continue; } @@ -807,26 +820,13 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const const BRepGraph_NodeId aNewChild = remapId(aCR.ChildNodeId); if (aNewChild.IsValid()) { - aCompChildren.Append(aNewChild); - aCompOldChildRefs.Append(aRefIt.CurrentId()); - } - } - const BRepGraph_CompoundId aNewCompoundId = - aNewGraph.Editor().Compounds().Add(aCompChildren.ToArray1()); - if (aNewCompoundId.IsValid()) - { - const NCollection_LinearVector& aNewChildRefs = - aNewGraph.Topo().Compounds().Relations(aNewCompoundId).ChildRefIds; - for (size_t aRefIdx = 0; aRefIdx < aCompOldChildRefs.Size() && aRefIdx < aNewChildRefs.Size(); - ++aRefIdx) - { - const BRepGraphInc::ChildRef& anOldRef = - theGraph.Refs().Children().Entry(aCompOldChildRefs.Value(aRefIdx)); - BRepGraph_MutGuard aNewRef = - aNewGraph.Editor().Gen().MutChildRef(aNewChildRefs.Value(aRefIdx)); - aNewGraph.Editor().Gen().SetChildRefOrientation(aNewRef, anOldRef.Orientation); - aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewRef, anOldRef.LocalLocation); - aChildRefMap.Bind(aCompOldChildRefs.Value(aRefIdx), aNewChildRefs.Value(aRefIdx)); + const BRepGraph_ChildRefId aNewRefId = + aNewGraph.Editor().Compounds().Append(*aNewCompoundId, aNewChild, aCR.Orientation); + if (aNewRefId.IsValid()) + { + aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewRefId, aCR.LocalLocation); + aChildRefMap.Bind(aRefIt.CurrentId(), aNewRefId); + } } } } @@ -1258,12 +1258,12 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_LayerRegistry aSavedLayerRegistry = std::move(theGraph.layerRegistry()); BRepGraph_CacheRegistry aSavedCacheRegistry = std::move(theGraph.cacheRegistry()); - // Transfer TShape-to-NodeId and NodeId-to-OriginalShape bindings: the rebuilt graph has none. - NCollection_LinearVector> aTShapeBindings; - NCollection_LinearVector> aOriginalBindings; - aGraphData->myIncStorage.ForEachTShapeBinding( - [&](const TopoDS_TShape* theTShape, const BRepGraph_NodeId& theNodeId) { - aTShapeBindings.Append({theTShape, theNodeId}); + // Transfer shape-to-NodeId and NodeId-to-OriginalShape bindings: the rebuilt graph has none. + NCollection_LinearVector> aShapeBindings; + NCollection_LinearVector> aOriginalBindings; + aGraphData->myIncStorage.ForEachShapeBinding( + [&](const TopoDS_Shape& theShape, const BRepGraph_NodeId& theNodeId) { + aShapeBindings.Append({theShape, theNodeId}); }); aGraphData->myIncStorage.ForEachOriginalBinding( [&](const BRepGraph_NodeId& theNodeId, const TopoDS_Shape& theShape) { @@ -1421,14 +1421,14 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aRemapMap.Bind(anOldId, aNewId); } - // Restore TShape-to-NodeId and NodeId-to-OriginalShape bindings, remapping NodeIds through + // Restore shape-to-NodeId and NodeId-to-OriginalShape bindings, remapping NodeIds through // aRemapMap. Nodes that were removed (dead, no entry in aRemapMap) are simply dropped. - for (const auto& [aTShape, aOldId] : aTShapeBindings) + for (const auto& [aShape, aOldId] : aShapeBindings) { const BRepGraph_NodeId* aNewId = aRemapMap.Seek(aOldId); if (aNewId != nullptr) { - theGraph.incStorage().BindTShapeToNode(aTShape, *aNewId); + theGraph.incStorage().SetDefinitionShapeBinding(aShape, *aNewId); } } for (const auto& [aOldId, aShape] : aOriginalBindings) @@ -1449,8 +1449,6 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aWireOldCoEdges.Clear(true); aFaceNextWires.Clear(true); aFaceOldWireRefs.Clear(true); - aCompChildren.Clear(true); - aCompOldChildRefs.Clear(true); aCSSolids.Clear(true); aCSOldSolidRefs.Clear(true); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx index 4cd4e298f2..a0c0b0cf21 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx @@ -64,6 +64,24 @@ static bool isCoreParityOrientation(const TopAbs_Orientation theOrientation) return theOrientation == TopAbs_FORWARD || theOrientation == TopAbs_REVERSED; } +static TopoDS_Shape shapeWithoutOwnLocation(const TopoDS_Shape& theShape) +{ + TopoDS_Shape aShape = theShape; + aShape.Location(TopLoc_Location()); + return aShape; +} + +static bool shouldStoreRootLocationInRef(const BRepGraph::ShapesView::Options& theOptions, + const BRepGraph_NodeId& theParent) +{ + if (theParent.IsValid()) + { + return theParent.NodeKind == BRepGraph_NodeId::Kind::Product + || theParent.NodeKind == BRepGraph_NodeId::Kind::Compound; + } + return theOptions.CreateAutoProduct; +} + static ShapeParentRoute classifyShapeToParent(const TopAbs_ShapeEnum theShapeType, const TopAbs_Orientation theOrientation, const BRepGraph_NodeId& theParent) @@ -129,7 +147,6 @@ static uint64_t attachSupplementToParent(BRepGraph& theGraph, const BRepGraph_NodeId theParent) { TopoDS_Shape aSuppShape = theShape; - aSuppShape.Orientation(theShape.Orientation()); switch (theParent.NodeKind) { case BRepGraph_NodeId::Kind::Compound: @@ -495,6 +512,96 @@ void BRepGraph::ShapesView::collectAddedNodes( } } +static void bindSourceShapeAlias(BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + const TopoDS_Shape& theSourceShape, + const TopLoc_Location& theSourceLocation, + const TopoDS_Shape& thePopulatedShape, + const TopLoc_Location& theDefinitionLocation) +{ + if (theSourceShape.IsNull() || thePopulatedShape.IsNull()) + { + return; + } + + TopoDS_Shape aLookup = thePopulatedShape; + aLookup.Location(theDefinitionLocation); + const BRepGraph_NodeId aNodeId = theGraph.Shapes().FindNode(aLookup); + if (!aNodeId.IsValid()) + { + return; + } + + theStorage.SetDefinitionShapeBinding(theSourceShape, aNodeId); + if (!theSourceShape.Location().IsEqual(theSourceLocation)) + { + TopoDS_Shape aSourceAlias = theSourceShape; + aSourceAlias.Location(theSourceLocation); + theStorage.SetDefinitionShapeBinding(aSourceAlias, aNodeId); + } +} + +static void bindSourceShapeAliasesRecursive(BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + const TopoDS_Shape& theSourceShape, + const TopoDS_Shape& thePopulatedShape, + const TopLoc_Location& theSourceLocation, + const TopLoc_Location& theDefinitionLocation) +{ + bindSourceShapeAlias(theGraph, + theStorage, + theSourceShape, + theSourceLocation, + thePopulatedShape, + theDefinitionLocation); + + const bool isCompoundParent = thePopulatedShape.ShapeType() == TopAbs_COMPOUND; + TopoDS_Iterator aSourceIt(theSourceShape, false, false); + TopoDS_Iterator aPopulatedIt(thePopulatedShape, false, false); + for (; aSourceIt.More() && aPopulatedIt.More(); aSourceIt.Next(), aPopulatedIt.Next()) + { + const TopoDS_Shape& aSourceChild = aSourceIt.Value(); + const TopoDS_Shape& aPopulatedChild = aPopulatedIt.Value(); + if (aSourceChild.IsNull() || aPopulatedChild.IsNull()) + { + continue; + } + + const TopLoc_Location aChildSourceLocation = theSourceLocation * aSourceChild.Location(); + if (isCompoundParent) + { + const TopoDS_Shape aChildDefinition = shapeWithoutOwnLocation(aPopulatedChild); + bindSourceShapeAliasesRecursive(theGraph, + theStorage, + aSourceChild, + aChildDefinition, + aChildSourceLocation, + TopLoc_Location()); + } + else + { + bindSourceShapeAliasesRecursive(theGraph, + theStorage, + aSourceChild, + aPopulatedChild, + aChildSourceLocation, + theDefinitionLocation * aPopulatedChild.Location()); + } + } +} + +void BRepGraph::ShapesView::bindSourceShapeAliases(BRepGraph& theGraph, + const TopoDS_Shape& theSourceShape, + const TopoDS_Shape& thePopulatedShape) +{ + bindSourceShapeAliasesRecursive(theGraph, + theGraph.myData->myIncStorage, + theSourceShape, + thePopulatedShape, + theSourceShape.Location(), + thePopulatedShape.Location()); +} + //================================================================================================= namespace @@ -787,10 +894,14 @@ BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( } const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); + const bool isRootLocationStoredInRef = + shouldStoreRootLocationInRef(theOptions, BRepGraph_NodeId()); + const TopoDS_Shape aPopulateShape = + isRootLocationStoredInRef ? shapeWithoutOwnLocation(theShape) : theShape; NCollection_LinearVector aFlatRoots; const AddStatus aAddStatus = - appendImpl(theGraph, theShape, theOptions, theOptions.Flatten ? &aFlatRoots : nullptr); + appendImpl(theGraph, aPopulateShape, theOptions, theOptions.Flatten ? &aFlatRoots : nullptr); aResult.Status = aAddStatus; @@ -802,6 +913,14 @@ BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( { aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); } + if (!aResult.TopologyRoot.IsValid()) + { + aResult.TopologyRoot = FindNode(aPopulateShape); + } + if (isRootLocationStoredInRef && aResult.TopologyRoot.IsValid()) + { + bindSourceShapeAliases(theGraph, theShape, aPopulateShape); + } if (theOptions.CreateAutoProduct && aResult.TopologyRoot.IsValid()) { @@ -870,14 +989,17 @@ BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( return aResult; } - const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); + const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); + const bool isRootLocationStoredInRef = shouldStoreRootLocationInRef(theOptions, theParent); + const TopoDS_Shape aPopulateShape = + isRootLocationStoredInRef ? shapeWithoutOwnLocation(theShape) : theShape; Options anInner = theOptions; anInner.CreateAutoProduct = false; NCollection_LinearVector aFlatRoots; const AddStatus aStatus = - appendImpl(theGraph, theShape, anInner, anInner.Flatten ? &aFlatRoots : nullptr); + appendImpl(theGraph, aPopulateShape, anInner, anInner.Flatten ? &aFlatRoots : nullptr); if (aStatus == AddStatus::Failed) { @@ -893,9 +1015,17 @@ BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); } if (!aResult.TopologyRoot.IsValid()) + { + aResult.TopologyRoot = FindNode(aPopulateShape); + } + if (!aResult.TopologyRoot.IsValid()) { return aResult; } + if (isRootLocationStoredInRef) + { + bindSourceShapeAliases(theGraph, theShape, aPopulateShape); + } if (anInner.TrackAddedNodes) { @@ -939,6 +1069,7 @@ BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( { return aResult; } + theGraph.Editor().Gen().SetChildRefLocalLocation(aRid, theShape.Location()); aResult.InsertedRef = aRid; aResult.Status = AddStatus::Success; return aResult; @@ -1040,7 +1171,12 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const } // Fast path: if entity was never mutated, return the original shape. const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef != nullptr && aDef->SubtreeGen == 0) + if (aDef == nullptr) + { + return TopoDS_Shape(); + } + const uint32_t aSubtreeGen = aDef->SubtreeGen; + if (aSubtreeGen == 0) { TopoDS_Shape anOrig = Original(theNode); if (!anOrig.IsNull()) @@ -1055,7 +1191,7 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const myGraph->myData->myIncStorage.CurrentShapesMutex()); const BRepGraphInc_Storage::CachedShape* aCached = myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode); - if (aCached != nullptr && aDef != nullptr && aCached->StoredSubtreeGen == aDef->SubtreeGen) + if (aCached != nullptr && aCached->StoredSubtreeGen == aSubtreeGen) { return aCached->Shape; } @@ -1068,24 +1204,31 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const // Store under exclusive lock with double-check to avoid redundant writes // when multiple threads reconstruct the same parent node concurrently. - if (!aReconstructed.IsNull() && aDef != nullptr) + if (!aReconstructed.IsNull()) { std::unique_lock aWriteLock( myGraph->myData->myIncStorage.CurrentShapesMutex()); - const BRepGraphInc_Storage::CachedShape* aExisting = - myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode); - if (aExisting != nullptr && aExisting->StoredSubtreeGen == aDef->SubtreeGen) + BRepGraphInc_Storage::CachedShape* aExisting = + myGraph->myData->myIncStorage.ChangeCurrentShapes().ChangeSeek(theNode); + if (aExisting != nullptr && aExisting->StoredSubtreeGen == aSubtreeGen) { return aExisting->Shape; } BRepGraphInc_Storage::CachedShape anEntry; anEntry.Shape = aReconstructed; - anEntry.StoredSubtreeGen = aDef->SubtreeGen; - myGraph->myData->myIncStorage.ChangeCurrentShapes().Bind(theNode, anEntry); + anEntry.StoredSubtreeGen = aSubtreeGen; + if (aExisting != nullptr) + { + *aExisting = anEntry; + } + else + { + myGraph->myData->myIncStorage.ChangeCurrentShapes().Bind(theNode, anEntry); + } if (theNode.NodeKind != BRepGraph_NodeId::Kind::Product && theNode.NodeKind != BRepGraph_NodeId::Kind::Occurrence) { - myGraph->myData->myIncStorage.BindTShapeToNode(aReconstructed.TShape().get(), theNode); + myGraph->myData->myIncStorage.SetDefinitionShapeBinding(aReconstructed, theNode); } } return aReconstructed; @@ -1112,15 +1255,14 @@ TopoDS_Shape BRepGraph::ShapesView::Original(const BRepGraph_NodeId theNode) con return TopoDS_Shape(); } - const TopoDS_Shape* anOriginal = myGraph->myData->myIncStorage.FindOriginal(theNode); - return anOriginal != nullptr ? *anOriginal : TopoDS_Shape(); + return myGraph->myData->myIncStorage.FindOriginal(theNode); } //================================================================================================= TopoDS_Shape BRepGraph::ShapesView::Reconstruct(const BRepGraph_NodeId theRoot) const { - if (theRoot.IsRemoved(*myGraph)) + if (!theRoot.IsValid() || theRoot.IsRemoved(*myGraph)) { return TopoDS_Shape(); } @@ -1141,20 +1283,19 @@ BRepGraph_NodeId BRepGraph::ShapesView::FindNode(const TopoDS_Shape& theShape) c return BRepGraph_NodeId(); } - const BRepGraph_NodeId* aNodeId = - myGraph->myData->myIncStorage.FindNodeByTShape(theShape.TShape().get()); - if (aNodeId != nullptr) + const BRepGraph_NodeId aNodeId = myGraph->myData->myIncStorage.FindDefinitionByShape(theShape); + if (aNodeId.IsValid()) { - if ((*aNodeId).IsRemoved(*myGraph)) + if (aNodeId.IsRemoved(*myGraph)) { return BRepGraph_NodeId(); } - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(*aNodeId); + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(aNodeId); if (aDef == nullptr) { return BRepGraph_NodeId(); } - return *aNodeId; + return aNodeId; } return BRepGraph_NodeId(); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx index abc6f74a3d..24b379c982 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx @@ -32,8 +32,9 @@ class TCollection_AsciiString; //! for repeated access. Topology nodes are delegated to the incidence-table //! reconstruction backend, while Product / Occurrence nodes are assembled at //! the facade level using product-local roots and occurrence placement chains. -//! Provides lookup from original construction-time shapes back to their graph -//! NodeIds via TShape pointer comparison. Shape() is the stable cached public +//! Provides lookup from construction-time shapes back to their graph NodeIds +//! using OCCT shape identity (TShape + Location, orientation ignored). +//! Shape() is the stable cached public //! route for repeated access; Reconstruct() forces a fresh rebuild with the //! same node-kind semantics and bypasses the persistent reconstructed-shape cache. //! Add() and Compact() clear the persistent reconstructed-shape cache. @@ -203,7 +204,7 @@ public: Standard_EXPORT void ClearCached(const BRepGraph_RefId theRef); //! Look up the definition NodeId for a shape from graph construction input. - //! Uses TShape pointer comparison (same semantics as IsSame()). + //! Uses OCCT IsSame() semantics (TShape + Location, orientation ignored). //! Synthetic Product / Occurrence reconstructions are not given dedicated //! TShape bindings, so lookup is only guaranteed for construction-time topology. //! Programmatically created Editor().Add*() nodes can still be located by @@ -213,7 +214,7 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_NodeId FindNode(const TopoDS_Shape& theShape) const; //! Check if a shape is known to the graph (was part of construction input). - //! Uses TShape pointer comparison (same semantics as IsSame()). + //! Uses OCCT IsSame() semantics (TShape + Location, orientation ignored). //! Synthetic Product / Occurrence reconstructions are not given dedicated //! TShape bindings, so this is only guaranteed for construction-time topology. //! Programmatically created Editor().Add*() nodes can still be located by @@ -254,6 +255,13 @@ private: const TopoDS_Shape& theShape, NCollection_DataMap& theMap); + //! Bind source shape keys to nodes populated from a location-stripped input shape. + //! This keeps ShapesView::FindNode() usable with the original TopoDS subshapes + //! when root placement is stored on a Product occurrence or Compound child ref. + static void bindSourceShapeAliases(BRepGraph& theGraph, + const TopoDS_Shape& theSourceShape, + const TopoDS_Shape& thePopulatedShape); + static BRepGraph_NodeId detectTopologyRoot(const BRepGraph& theGraph, const TopAbs_ShapeEnum theShapeType, const uint32_t theOldCountOfShapeKind); diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx index 04b289b9c7..645297b84f 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +55,7 @@ #include #include +#include #include #include "BRepGraphInc_BoundaryBuilder.pxx" @@ -248,7 +251,7 @@ struct ExtractedVertex TopoDS_Vertex Shape; gp_Pnt Point; double Tolerance = 0.0; - TopLoc_Location BakedLocation; + TopLoc_Location DefinitionLocation; bool IsGenerated = false; }; @@ -268,7 +271,7 @@ struct ExtractedEdge occ::handle Polygon3D; occ::handle PolyOnSurf; occ::handle PolyOnTri; - TopLoc_Location BakedLocation; + TopLoc_Location DefinitionLocation; bool IsGenerated = false; }; @@ -281,7 +284,7 @@ struct ExtractedWire TopoDS_Wire Shape; NCollection_DynamicArray Edges; - TopLoc_Location BakedLocation; + TopLoc_Location DefinitionLocation; bool IsGenerated = false; bool HasPartialExplorerOrder = false; }; @@ -299,7 +302,7 @@ struct FaceBuildData occ::handle Surface; occ::handle RawTriangulation; occ::handle ActiveTriangulation; - TopLoc_Location BakedLocation; + TopLoc_Location DefinitionLocation; double Tolerance = 0.0; bool UnsupportedNaturalBoundary = false; bool NeedsSynthesis = false; @@ -310,6 +313,7 @@ struct FacePCurveContext { occ::handle RawSurface; TopLoc_Location SurfaceLocation; + TopLoc_Location TriangulationLocation; }; static bool findStoredPCurve( @@ -375,7 +379,12 @@ bool rawVertexPoint(const TopoDS_Vertex& theVertex, gp_Pnt& thePoint) { return false; } - thePoint = static_cast(aTShape.get())->Pnt(); + const occ::handle aTVtx = occ::down_cast(aTShape); + if (aTVtx.IsNull()) + { + return false; + } + thePoint = aTVtx->Pnt(); return true; } @@ -422,8 +431,8 @@ BRepGraph_LayerTopoSupplement::AttachmentKind containerSupplementKind( } template -static occ::handle applyBakedLocation(const occ::handle& theGeom, - const TopLoc_Location& theLocation) +static occ::handle applyDefinitionLocation(const occ::handle& theGeom, + const TopLoc_Location& theLocation) { if (theGeom.IsNull() || theLocation.IsIdentity()) { @@ -432,12 +441,12 @@ static occ::handle applyBakedLocation(const occ::handle& theGeom, return occ::down_cast(theGeom->Transformed(theLocation.Transformation())); } -static gp_Pnt applyBakedLocation(const gp_Pnt& thePoint, const TopLoc_Location& theLocation) +static gp_Pnt applyDefinitionLocation(const gp_Pnt& thePoint, const TopLoc_Location& theLocation) { return theLocation.IsIdentity() ? thePoint : thePoint.Transformed(theLocation.Transformation()); } -static occ::handle applyBakedLocationToPolygon3D( +static occ::handle applyDefinitionLocationToPolygon3D( const occ::handle& thePolygon3D, const TopLoc_Location& theLocation) { @@ -472,29 +481,60 @@ static occ::handle applyBakedLocationToPolygon3D( return aTransPoly; } -static occ::handle applyBakedLocationToTriangulation( +static void invalidateTriangulationCachedMinMax( + const occ::handle& theTriangulation) +{ + if (!theTriangulation.IsNull() && theTriangulation->HasCachedMinMax()) + { + // Poly_Triangulation uses a void box as the public cache invalidation signal. + theTriangulation->SetCachedMinMax(Bnd_Box()); + } +} + +static occ::handle applyDefinitionLocationToTriangulation( const occ::handle& theTriangulation, const TopLoc_Location& theLocation) { - if (theTriangulation.IsNull() || theLocation.IsIdentity()) + if (theTriangulation.IsNull()) { return theTriangulation; } - occ::handle aCopy = theTriangulation->Copy(); - const gp_Trsf& aTrsf = theLocation.Transformation(); - Poly_ArrayOfNodes& aNodes = aCopy->InternalNodes(); - for (int aNodeIdx = 0; aNodeIdx < aNodes.Length(); ++aNodeIdx) + occ::handle aCopy = theTriangulation->Copy(); + if (theLocation.IsIdentity()) { - aNodes.SetValue(aNodeIdx, aNodes.Value(aNodeIdx).Transformed(aTrsf)); + return aCopy; + } + + const gp_Trsf& aTrsf = theLocation.Transformation(); + aCopy->Deflection(aCopy->Deflection() * std::abs(aTrsf.ScaleFactor())); + for (int aNodeIdx = 1; aNodeIdx <= aCopy->NbNodes(); ++aNodeIdx) + { + gp_Pnt aPoint = aCopy->Node(aNodeIdx); + aPoint.Transform(aTrsf); + aCopy->SetNode(aNodeIdx, aPoint); } if (aCopy->HasNormals()) { for (int aNodeIdx = 1; aNodeIdx <= aCopy->NbNodes(); ++aNodeIdx) { - aCopy->SetNormal(aNodeIdx, aCopy->Normal(aNodeIdx).Transformed(aTrsf)); + gp_Dir aNormal = aCopy->Normal(aNodeIdx); + aNormal.Transform(aTrsf); + aCopy->SetNormal(aNodeIdx, aNormal); } } + if (aTrsf.IsNegative()) + { + for (int aTriIdx = 1; aTriIdx <= aCopy->NbTriangles(); ++aTriIdx) + { + int aN1 = 0; + int aN2 = 0; + int aN3 = 0; + aCopy->Triangle(aTriIdx).Get(aN1, aN2, aN3); + aCopy->SetTriangle(aTriIdx, Poly_Triangle(aN1, aN3, aN2)); + } + } + invalidateTriangulationCachedMinMax(aCopy); return aCopy; } @@ -507,24 +547,18 @@ struct LocatedNodeBinding using LocatedNodeBindingIndex = uint32_t; -enum class RootRole -{ - Nested, - Root -}; - struct BuildContext; BRepGraph_NodeId findExistingNode(const BuildContext& theBuild, const TopoDS_Shape& theShape, BRepGraph_NodeId::Kind theExpectedKind, - const TopLoc_Location& theBakedLocation); + const TopLoc_Location& theDefinitionLocation); void bindLocatedNode(BuildContext& theBuild, const TopoDS_Shape& theShape, const BRepGraph_NodeId theNodeId, - const TopLoc_Location& theBakedLocation, - const bool theBindTShape); + const TopLoc_Location& theDefinitionLocation, + const bool theBindShape); enum class TopologyBuildMode { @@ -579,35 +613,49 @@ void recordWireOrderStatus(BuildContext& the theBuild.HasWireOrderWarnings = theBuild.HasWireOrderWarnings || isWireOrderWarning(theStatus); } +TopoDS_Shape definitionShapeKey(const TopoDS_Shape& theShape, + const TopLoc_Location& theDefinitionLocation) +{ + TopoDS_Shape aKey = theShape; + aKey.Location(theDefinitionLocation); + return aKey; +} + +TopoDS_Shape shapeWithoutOwnLocation(const TopoDS_Shape& theShape) +{ + TopoDS_Shape aShape = theShape; + aShape.Location(TopLoc_Location()); + return aShape; +} + BRepGraph_NodeId findExistingNode(const BuildContext& theBuild, const TopoDS_Shape& theShape, BRepGraph_NodeId::Kind theExpectedKind, - const TopLoc_Location& theBakedLocation) + const TopLoc_Location& theDefinitionLocation) { const TopoDS_TShape* aTShape = theShape.TShape().get(); - if (theBakedLocation.IsIdentity()) + + const BRepGraph_NodeId aBoundNode = + theBuild.Storage.FindDefinitionByShape(definitionShapeKey(theShape, theDefinitionLocation)); + if (aBoundNode.IsValid() && aBoundNode.NodeKind == theExpectedKind) { - const BRepGraph_NodeId* aStoredNode = theBuild.Storage.FindNodeByTShape(aTShape); - if (aStoredNode != nullptr && aStoredNode->NodeKind == theExpectedKind) - { - return *aStoredNode; - } + return aBoundNode; } + // Fallback: within-build LocatedNodes index for this build pass. const NCollection_LinearVector* anIndices = theBuild.LocatedNodeIndex.Seek(aTShape); - if (anIndices == nullptr) + if (anIndices != nullptr) { - return BRepGraph_NodeId(); - } - for (const LocatedNodeBindingIndex aBindingIndex : *anIndices) - { - const LocatedNodeBinding& aBinding = - theBuild.LocatedNodes.Value(static_cast(aBindingIndex)); - if (aBinding.TShape == aTShape && aBinding.Node.NodeKind == theExpectedKind - && aBinding.Location.IsEqual(theBakedLocation)) + for (const LocatedNodeBindingIndex aBindingIndex : *anIndices) { - return aBinding.Node; + const LocatedNodeBinding& aBinding = + theBuild.LocatedNodes.Value(static_cast(aBindingIndex)); + if (aBinding.Node.NodeKind == theExpectedKind + && aBinding.Location.IsEqual(theDefinitionLocation)) + { + return aBinding.Node; + } } } return BRepGraph_NodeId(); @@ -616,15 +664,15 @@ BRepGraph_NodeId findExistingNode(const BuildContext& theBuild, void bindLocatedNode(BuildContext& theBuild, const TopoDS_Shape& theShape, const BRepGraph_NodeId theNodeId, - const TopLoc_Location& theBakedLocation, - const bool theBindTShape) + const TopLoc_Location& theDefinitionLocation, + const bool theBindShape) { const TopoDS_TShape* aTShape = theShape.TShape().get(); const LocatedNodeBindingIndex aBindingIndex = static_cast(theBuild.LocatedNodes.Size()); LocatedNodeBinding& aBinding = theBuild.LocatedNodes.Appended(); aBinding.TShape = aTShape; - aBinding.Location = theBakedLocation; + aBinding.Location = theDefinitionLocation; aBinding.Node = theNodeId; if (!theBuild.LocatedNodeIndex.IsBound(aTShape)) @@ -633,9 +681,10 @@ void bindLocatedNode(BuildContext& theBuild, } theBuild.LocatedNodeIndex.ChangeFind(aTShape).Append(aBindingIndex); - if (theBindTShape && theBakedLocation.IsIdentity() && !theBuild.Storage.HasTShapeBinding(aTShape)) + if (theBindShape) { - theBuild.Storage.BindTShapeToNode(aTShape, theNodeId); + theBuild.Storage.SetDefinitionShapeBinding(definitionShapeKey(theShape, theDefinitionLocation), + theNodeId); } } @@ -643,7 +692,7 @@ BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, const TopoDS_Vertex& theVertex, const gp_Pnt& thePoint, const double theTolerance, - const TopLoc_Location& theBakedLocation, + const TopLoc_Location& theDefinitionLocation, const bool theIsGenerated = false) { if (theVertex.IsNull()) @@ -651,7 +700,7 @@ BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, return BRepGraph_VertexId(); } const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theVertex, BRepGraph_NodeId::Kind::Vertex, theBakedLocation); + findExistingNode(theBuild, theVertex, BRepGraph_NodeId::Kind::Vertex, theDefinitionLocation); if (anExisting.IsValid()) { return BRepGraph_VertexId(anExisting); @@ -663,15 +712,15 @@ BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, aVtxEnt.Tolerance = theTolerance; if (!theIsGenerated) { - bindLocatedNode(theBuild, theVertex, aVtxId, theBakedLocation, true); - theBuild.Storage.BindOriginal(aVtxId, theVertex); + bindLocatedNode(theBuild, theVertex, aVtxId, theDefinitionLocation, true); + theBuild.Storage.BindOriginal(aVtxId, definitionShapeKey(theVertex, theDefinitionLocation)); } return aVtxId; } BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, const TopoDS_Vertex& theVertex, - const TopLoc_Location& theBakedLocation) + const TopLoc_Location& theDefinitionLocation) { if (theVertex.IsNull()) { @@ -684,9 +733,9 @@ BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, } return registerOrReuseVertex(theBuild, theVertex, - applyBakedLocation(aPoint, theBakedLocation), + applyDefinitionLocation(aPoint, theDefinitionLocation), BRep_Tool::Tolerance(theVertex), - theBakedLocation); + theDefinitionLocation); } bool attachNonCoreContainerChild(BuildContext& theBuild, @@ -738,7 +787,7 @@ static BRepGraph_VertexRefId appendEdgeVertexRef(BuildContext& theBui theVertex.Shape, theVertex.Point, theVertex.Tolerance, - theVertex.BakedLocation, + theVertex.DefinitionLocation, theVertex.IsGenerated); return appendVertexRef(theBuild.Storage, aVertexId, theOrientation, theParentEdgeId); } @@ -747,30 +796,31 @@ BRepGraph_EdgeId registerEdge(BuildContext& const ExtractedEdge& theEdgeData, const occ::handle& theSupplementLayer) { - if (theEdgeData.IsGenerated) + // Generated edges are not deduplicated; skip the existing-node lookup + // so each generated edge always creates a fresh representation. + if (!theEdgeData.IsGenerated) { - // Generated edges are not deduplicated; each gets its own representation. - } - const BRepGraph_NodeId anExisting = findExistingNode(theBuild, - theEdgeData.Shape, - BRepGraph_NodeId::Kind::Edge, - theEdgeData.BakedLocation); - if (anExisting.IsValid()) - { - const BRepGraph_EdgeId anEdgeId(anExisting); - BRepGraphInc::EdgeDef& anEdgeEnt = theBuild.Storage.ChangeEdge(anEdgeId); - if (!anEdgeEnt.Polygon3DRepId.IsValid() && !theEdgeData.Polygon3D.IsNull()) + const BRepGraph_NodeId anExisting = findExistingNode(theBuild, + theEdgeData.Shape, + BRepGraph_NodeId::Kind::Edge, + theEdgeData.DefinitionLocation); + if (anExisting.IsValid()) { - const BRepGraph_EdgePolygon3DRepId aRepId = theBuild.Storage.AppendEdgePolygon3DRep(); - theBuild.Storage.ChangeEdgePolygon3DRep(aRepId).Polygon = theEdgeData.Polygon3D; - anEdgeEnt.Polygon3DRepId = aRepId; - if (anEdgeEnt.Polygon3DRepId.IsValid()) + const BRepGraph_EdgeId anEdgeId(anExisting); + BRepGraphInc::EdgeDef& anEdgeEnt = theBuild.Storage.ChangeEdge(anEdgeId); + if (!anEdgeEnt.Polygon3DRepId.IsValid() && !theEdgeData.Polygon3D.IsNull()) { - theBuild.Storage.ChangeEdgePolygon3DRep(anEdgeEnt.Polygon3DRepId).ParentEdgeId = anEdgeId; + const BRepGraph_EdgePolygon3DRepId aRepId = theBuild.Storage.AppendEdgePolygon3DRep(); + theBuild.Storage.ChangeEdgePolygon3DRep(aRepId).Polygon = theEdgeData.Polygon3D; + anEdgeEnt.Polygon3DRepId = aRepId; + if (anEdgeEnt.Polygon3DRepId.IsValid()) + { + theBuild.Storage.ChangeEdgePolygon3DRep(anEdgeEnt.Polygon3DRepId).ParentEdgeId = anEdgeId; + } } + return anEdgeId; } - return anEdgeId; - } + } // if (!theEdgeData.IsGenerated) const BRepGraph_EdgeId anEdgeId = theBuild.Storage.AppendEdge(); BRepGraphInc::EdgeDef& anEdgeEnt = theBuild.Storage.ChangeEdge(anEdgeId); @@ -820,8 +870,10 @@ BRepGraph_EdgeId registerEdge(BuildContext& if (!theEdgeData.IsGenerated) { - bindLocatedNode(theBuild, theEdgeData.Shape, anEdgeId, theEdgeData.BakedLocation, true); - theBuild.Storage.BindOriginal(anEdgeId, theEdgeData.Shape); + bindLocatedNode(theBuild, theEdgeData.Shape, anEdgeId, theEdgeData.DefinitionLocation, true); + theBuild.Storage.BindOriginal( + anEdgeId, + definitionShapeKey(theEdgeData.Shape, theEdgeData.DefinitionLocation)); attachSupplement(theSupplementLayer, anEdgeId, theEdgeData.Shape); } return anEdgeId; @@ -829,18 +881,14 @@ BRepGraph_EdgeId registerEdge(BuildContext& BRepGraph_WireId appendWireDef(BuildContext& theBuild, const TopoDS_Wire& theWire, - const bool theBindTShape, - const TopLoc_Location& theBakedLocation, + const TopLoc_Location& theDefinitionLocation, const bool theIsGenerated = false) { const BRepGraph_WireId aWireId = theBuild.Storage.AppendWire(); - if (!theIsGenerated && theBindTShape) - { - bindLocatedNode(theBuild, theWire, aWireId, theBakedLocation, true); - } if (!theIsGenerated) { - theBuild.Storage.BindOriginal(aWireId, theWire); + bindLocatedNode(theBuild, theWire, aWireId, theDefinitionLocation, true); + theBuild.Storage.BindOriginal(aWireId, definitionShapeKey(theWire, theDefinitionLocation)); } return aWireId; } @@ -926,10 +974,10 @@ static void extractEdgeDefinition(ExtractedEdge& theEdgeData, const TopoDS_Edge& theEdge, const TopLoc_Location& theParentLocation) { - theEdgeData.Shape = theEdge; - theEdgeData.Tolerance = BRep_Tool::Tolerance(theEdge); - theEdgeData.OrientationInWire = theEdge.Orientation(); - theEdgeData.BakedLocation = theParentLocation * theEdge.Location(); + theEdgeData.Shape = theEdge; + theEdgeData.Tolerance = BRep_Tool::Tolerance(theEdge); + theEdgeData.OrientationInWire = theEdge.Orientation(); + theEdgeData.DefinitionLocation = theParentLocation * theEdge.Location(); { double aFirst = 0.0, aLast = 0.0; @@ -938,7 +986,8 @@ static void extractEdgeDefinition(ExtractedEdge& theEdgeData, theEdgeData.ParamFirst = aFirst; theEdgeData.ParamLast = aLast; theEdgeData.Curve3d = - applyBakedLocation(theEdgeData.Curve3d, theParentLocation * aCurveCombinedLoc); + applyDefinitionLocation(theEdgeData.Curve3d, + theParentLocation * aCurveCombinedLoc); } TopoDS_Vertex aVFirst, aVLast; @@ -946,25 +995,26 @@ static void extractEdgeDefinition(ExtractedEdge& theEdgeData, gp_Pnt aVertexPoint; if (!aVFirst.IsNull() && rawVertexPoint(aVFirst, aVertexPoint)) { - theEdgeData.StartVertex.Shape = aVFirst; - theEdgeData.StartVertex.BakedLocation = theEdgeData.BakedLocation * aVFirst.Location(); + theEdgeData.StartVertex.Shape = aVFirst; + theEdgeData.StartVertex.DefinitionLocation = + theEdgeData.DefinitionLocation * aVFirst.Location(); theEdgeData.StartVertex.Point = - applyBakedLocation(aVertexPoint, theEdgeData.StartVertex.BakedLocation); + applyDefinitionLocation(aVertexPoint, theEdgeData.StartVertex.DefinitionLocation); theEdgeData.StartVertex.Tolerance = BRep_Tool::Tolerance(aVFirst); } if (!aVLast.IsNull() && rawVertexPoint(aVLast, aVertexPoint)) { - theEdgeData.EndVertex.Shape = aVLast; - theEdgeData.EndVertex.BakedLocation = theEdgeData.BakedLocation * aVLast.Location(); + theEdgeData.EndVertex.Shape = aVLast; + theEdgeData.EndVertex.DefinitionLocation = theEdgeData.DefinitionLocation * aVLast.Location(); theEdgeData.EndVertex.Point = - applyBakedLocation(aVertexPoint, theEdgeData.EndVertex.BakedLocation); + applyDefinitionLocation(aVertexPoint, theEdgeData.EndVertex.DefinitionLocation); theEdgeData.EndVertex.Tolerance = BRep_Tool::Tolerance(aVLast); } TopLoc_Location aPoly3DLoc; theEdgeData.Polygon3D = BRep_Tool::Polygon3D(theEdge, aPoly3DLoc); theEdgeData.Polygon3D = - applyBakedLocationToPolygon3D(theEdgeData.Polygon3D, theParentLocation * aPoly3DLoc); + applyDefinitionLocationToPolygon3D(theEdgeData.Polygon3D, theParentLocation * aPoly3DLoc); } static void extractEdgeInFace(ExtractedEdge& theEdgeData, @@ -988,9 +1038,14 @@ static void extractEdgeInFace(ExtractedEdge& theEdgeData theEdgeData.PolyOnSurf = BRep_Tool::PolygonOnSurface(theEdge, theForwardFace); if (!theTriangulation.IsNull()) { - TopLoc_Location aPolyTriLoc; + // This lookup uses the source OCCT representation key. BRep_Tool + // compares L.Predivided(E.Location()) with the location stored in the + // BRep_PolygonOnTriangulation representation, so pass the raw location + // returned by BRep_Tool::Triangulation(aFace, ...), not graph placement. theEdgeData.PolyOnTri = - BRep_Tool::PolygonOnTriangulation(theEdge, theTriangulation, aPolyTriLoc); + BRep_Tool::PolygonOnTriangulation(theEdge, + theTriangulation, + thePCurveContext.TriangulationLocation); } } @@ -1123,26 +1178,28 @@ static bool hasInfiniteRequiredBound(const occ::handle& theSurface void extractFaceData(FaceBuildData& theData) { - const TopoDS_Face& aFace = theData.Face; - const TopLoc_Location aFaceParentLocation = theData.BakedLocation * aFace.Location().Inverted(); + const TopoDS_Face& aFace = theData.Face; + const TopLoc_Location aFaceParentLocation = + theData.DefinitionLocation * aFace.Location().Inverted(); TopLoc_Location aSurfCombinedLoc; occ::handle aRawSurface = BRep_Tool::Surface(aFace, aSurfCombinedLoc); theData.Surface = - applyBakedLocation(aRawSurface, aFaceParentLocation * aSurfCombinedLoc); + applyDefinitionLocation(aRawSurface, aFaceParentLocation * aSurfCombinedLoc); TopLoc_Location aTriangulationLoc; theData.RawTriangulation = BRep_Tool::Triangulation(aFace, aTriangulationLoc); theData.ActiveTriangulation = - applyBakedLocationToTriangulation(theData.RawTriangulation, - aFaceParentLocation * aTriangulationLoc); + applyDefinitionLocationToTriangulation(theData.RawTriangulation, + aFaceParentLocation * aTriangulationLoc); theData.Tolerance = BRep_Tool::Tolerance(aFace); const TopoDS_Face aForwardFace = TopoDS::Face(aFace.Oriented(TopAbs_FORWARD)); FacePCurveContext aPCurveContext; - aPCurveContext.RawSurface = aRawSurface; - aPCurveContext.SurfaceLocation = aSurfCombinedLoc; + aPCurveContext.RawSurface = aRawSurface; + aPCurveContext.SurfaceLocation = aSurfCombinedLoc; + aPCurveContext.TriangulationLocation = aTriangulationLoc; for (TopoDS_Iterator aChildIt(aForwardFace, false, false); aChildIt.More(); aChildIt.Next()) { @@ -1153,9 +1210,9 @@ void extractFaceData(FaceBuildData& theData) } const TopoDS_Wire& aWire = TopoDS::Wire(aChild); - ExtractedWire& aWireData = theData.Wires.Appended(); - aWireData.Shape = aWire; - aWireData.BakedLocation = theData.BakedLocation * aWire.Location(); + ExtractedWire& aWireData = theData.Wires.Appended(); + aWireData.Shape = aWire; + aWireData.DefinitionLocation = theData.DefinitionLocation * aWire.Location(); for (TopoDS_Iterator anEdgeIt(aWire, false, false); anEdgeIt.More(); anEdgeIt.Next()) { @@ -1170,7 +1227,7 @@ void extractFaceData(FaceBuildData& theData) aForwardFace, theData.RawTriangulation, aPCurveContext, - aWireData.BakedLocation); + aWireData.DefinitionLocation); } aWireData.HasPartialExplorerOrder = !orderExtractedWire(aWireData, aRawSurface.IsNull() ? nullptr : &aForwardFace); @@ -1224,8 +1281,7 @@ void registerFaceData(BuildContext& theBuild { const BRepGraph_WireId aWireId = appendWireDef(theBuild, aWireData.Shape, - false, - aWireData.BakedLocation, + aWireData.DefinitionLocation, aWireData.IsGenerated); appendWireRef(theStorage, aFaceId, aWireId, aWireData.Shape.Orientation()); @@ -1298,7 +1354,10 @@ void synthesizeFaceBoundaries(BuildContext& theBuild, for (const BRepGraphInc_BoundaryBuilder::SurfaceBoundaryEdge& aBoundaryEdge : aBoundary.Edges) { BRepGraph_EdgeCurve3DRepId aCurveRepId; - if (!aBoundaryEdge.Curve3D.IsNull()) + // Skip storing 3D curve for degenerate edges so the derived-state cache + // correctly flags them as degenerate rather than treating the non-null + // 3D curve as evidence of a non-degenerate geometry edge. + if (!aBoundaryEdge.Curve3D.IsNull() && !aBoundaryEdge.IsDegenerate) { aCurveRepId = theStorage.AppendEdgeCurve3DRep(); theStorage.ChangeEdgeCurve3DRep(aCurveRepId).Curve = aBoundaryEdge.Curve3D; @@ -1381,37 +1440,35 @@ BRepGraph_NodeId enqueueFace(BuildContext& theBuild, const TopoDS_Face& theFace, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theFace.Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theFace.Location(); const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theFace, BRepGraph_NodeId::Kind::Face, aBakedLocation); + findExistingNode(theBuild, theFace, BRepGraph_NodeId::Kind::Face, aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; } const BRepGraph_FaceId aFaceId = theBuild.Storage.AppendFace(); - bindLocatedNode(theBuild, theFace, aFaceId, aBakedLocation, true); - theBuild.Storage.BindOriginal(aFaceId, theFace); + bindLocatedNode(theBuild, theFace, aFaceId, aDefinitionLocation, true); + theBuild.Storage.BindOriginal(aFaceId, definitionShapeKey(theFace, aDefinitionLocation)); attachSupplement(theBuild.SupplementLayer, aFaceId, theFace); - FaceBuildData& aFaceData = theBuild.PendingFaces.Appended(); - aFaceData.Face = theFace; - aFaceData.FaceId = aFaceId; - aFaceData.BakedLocation = aBakedLocation; + FaceBuildData& aFaceData = theBuild.PendingFaces.Appended(); + aFaceData.Face = theFace; + aFaceData.FaceId = aFaceId; + aFaceData.DefinitionLocation = aDefinitionLocation; return BRepGraph_NodeId(aFaceId); } BRepGraph_NodeId traverseTopology(BuildContext& theBuild, const TopoDS_Shape& theCurrentShape, - const TopLoc_Location& theParentLocation, - const RootRole theRootRole = RootRole::Nested); + const TopLoc_Location& theParentLocation); BRepGraph_NodeId traverseCompound(BuildContext& theBuild, const TopoDS_Compound& theCompound, - const RootRole theRootRole) + const TopLoc_Location& theParentLocation) { - const TopLoc_Location aDefinitionLocation = - theRootRole == RootRole::Root ? theCompound.Location() : TopLoc_Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theCompound.Location(); const BRepGraph_NodeId anExisting = findExistingNode(theBuild, theCompound, BRepGraph_NodeId::Kind::Compound, aDefinitionLocation); if (anExisting.IsValid()) @@ -1421,9 +1478,7 @@ BRepGraph_NodeId traverseCompound(BuildContext& theBuild, const BRepGraph_CompoundId aCompId = theBuild.Storage.AppendCompound(); bindLocatedNode(theBuild, theCompound, aCompId, aDefinitionLocation, true); - theBuild.Storage.BindOriginal(aCompId, theCompound); - - const TopLoc_Location aChildRootLocation = aDefinitionLocation; + theBuild.Storage.BindOriginal(aCompId, definitionShapeKey(theCompound, aDefinitionLocation)); for (TopoDS_Iterator aChildIt(theCompound, false, false); aChildIt.More(); aChildIt.Next()) { @@ -1435,15 +1490,16 @@ BRepGraph_NodeId traverseCompound(BuildContext& theBuild, attachNonCoreContainerChild(theBuild, BRepGraph_NodeId(aCompId), TopAbs_COMPOUND, aChild); continue; } + const TopoDS_Shape aDefinitionChild = shapeWithoutOwnLocation(aChild); const BRepGraph_NodeId aChildNode = - traverseTopology(theBuild, aChild, TopLoc_Location(), RootRole::Nested); + traverseTopology(theBuild, aDefinitionChild, TopLoc_Location()); if (aChildNode.IsValid()) { appendChildRef(theBuild.Storage, aCompId, aChildNode, aChildOri, - aChildRootLocation * aChild.Location()); + aDefinitionLocation * aChild.Location()); } } return BRepGraph_NodeId(aCompId); @@ -1453,17 +1509,19 @@ BRepGraph_NodeId traverseCompSolid(BuildContext& theBuild, const TopoDS_CompSolid& theCompSolid, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theCompSolid.Location(); - const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theCompSolid, BRepGraph_NodeId::Kind::CompSolid, aBakedLocation); + const TopLoc_Location aDefinitionLocation = theParentLocation * theCompSolid.Location(); + const BRepGraph_NodeId anExisting = findExistingNode(theBuild, + theCompSolid, + BRepGraph_NodeId::Kind::CompSolid, + aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; } const BRepGraph_CompSolidId aCSolidId = theBuild.Storage.AppendCompSolid(); - bindLocatedNode(theBuild, theCompSolid, aCSolidId, aBakedLocation, true); - theBuild.Storage.BindOriginal(aCSolidId, theCompSolid); + bindLocatedNode(theBuild, theCompSolid, aCSolidId, aDefinitionLocation, true); + theBuild.Storage.BindOriginal(aCSolidId, definitionShapeKey(theCompSolid, aDefinitionLocation)); for (TopoDS_Iterator aChildIt(theCompSolid, false, false); aChildIt.More(); aChildIt.Next()) { @@ -1476,7 +1534,7 @@ BRepGraph_NodeId traverseCompSolid(BuildContext& theBuild, continue; } - const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aBakedLocation); + const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aDefinitionLocation); if (!aChildNode.IsValid() || aChildNode.NodeKind != BRepGraph_NodeId::Kind::Solid) { continue; @@ -1494,17 +1552,17 @@ BRepGraph_NodeId traverseSolid(BuildContext& theBuild, const TopoDS_Solid& theSolid, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theSolid.Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theSolid.Location(); const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theSolid, BRepGraph_NodeId::Kind::Solid, aBakedLocation); + findExistingNode(theBuild, theSolid, BRepGraph_NodeId::Kind::Solid, aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; } const BRepGraph_SolidId aSolidId = theBuild.Storage.AppendSolid(); - bindLocatedNode(theBuild, theSolid, aSolidId, aBakedLocation, true); - theBuild.Storage.BindOriginal(aSolidId, theSolid); + bindLocatedNode(theBuild, theSolid, aSolidId, aDefinitionLocation, true); + theBuild.Storage.BindOriginal(aSolidId, definitionShapeKey(theSolid, aDefinitionLocation)); for (TopoDS_Iterator aChildIt(theSolid, false, false); aChildIt.More(); aChildIt.Next()) { @@ -1517,7 +1575,7 @@ BRepGraph_NodeId traverseSolid(BuildContext& theBuild, continue; } - const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aBakedLocation); + const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aDefinitionLocation); if (!aChildNode.IsValid() || aChildNode.NodeKind != BRepGraph_NodeId::Kind::Shell) { continue; @@ -1535,17 +1593,17 @@ BRepGraph_NodeId traverseShell(BuildContext& theBuild, const TopoDS_Shell& theShell, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theShell.Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theShell.Location(); const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theShell, BRepGraph_NodeId::Kind::Shell, aBakedLocation); + findExistingNode(theBuild, theShell, BRepGraph_NodeId::Kind::Shell, aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; } const BRepGraph_ShellId aShellId = theBuild.Storage.AppendShell(); - bindLocatedNode(theBuild, theShell, aShellId, aBakedLocation, true); - theBuild.Storage.BindOriginal(aShellId, theShell); + bindLocatedNode(theBuild, theShell, aShellId, aDefinitionLocation, true); + theBuild.Storage.BindOriginal(aShellId, definitionShapeKey(theShell, aDefinitionLocation)); for (TopoDS_Iterator aChildIt(theShell, false, false); aChildIt.More(); aChildIt.Next()) { @@ -1559,7 +1617,7 @@ BRepGraph_NodeId traverseShell(BuildContext& theBuild, } const TopoDS_Face aFace = TopoDS::Face(aChild); - const BRepGraph_NodeId aFaceNode = enqueueFace(theBuild, aFace, aBakedLocation); + const BRepGraph_NodeId aFaceNode = enqueueFace(theBuild, aFace, aDefinitionLocation); appendFaceRef(theBuild.Storage, aShellId, BRepGraph_FaceId::FromNodeId(aFaceNode), aChildOri); } return BRepGraph_NodeId(aShellId); @@ -1569,18 +1627,18 @@ BRepGraph_NodeId traverseWire(BuildContext& theBuild, const TopoDS_Wire& theWire, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theWire.Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theWire.Location(); const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theWire, BRepGraph_NodeId::Kind::Wire, aBakedLocation); + findExistingNode(theBuild, theWire, BRepGraph_NodeId::Kind::Wire, aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; } - const BRepGraph_WireId aWireId = appendWireDef(theBuild, theWire, true, aBakedLocation); + const BRepGraph_WireId aWireId = appendWireDef(theBuild, theWire, aDefinitionLocation); ExtractedWire aWireData; - aWireData.Shape = theWire; - aWireData.BakedLocation = aBakedLocation; + aWireData.Shape = theWire; + aWireData.DefinitionLocation = aDefinitionLocation; for (TopoDS_Iterator anEdgeIt(theWire, false, false); anEdgeIt.More(); anEdgeIt.Next()) { @@ -1591,7 +1649,7 @@ BRepGraph_NodeId traverseWire(BuildContext& theBuild, } ExtractedEdge& anEdgeData = aWireData.Edges.Appended(); - extractEdgeDefinition(anEdgeData, TopoDS::Edge(anEdgeShape), aBakedLocation); + extractEdgeDefinition(anEdgeData, TopoDS::Edge(anEdgeShape), aDefinitionLocation); } theBuild.HasWireOrderWarnings = @@ -1614,9 +1672,9 @@ BRepGraph_NodeId traverseEdge(BuildContext& theBuild, const TopoDS_Edge& theEdge, const TopLoc_Location& theParentLocation) { - const TopLoc_Location aBakedLocation = theParentLocation * theEdge.Location(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theEdge.Location(); const BRepGraph_NodeId anExisting = - findExistingNode(theBuild, theEdge, BRepGraph_NodeId::Kind::Edge, aBakedLocation); + findExistingNode(theBuild, theEdge, BRepGraph_NodeId::Kind::Edge, aDefinitionLocation); if (anExisting.IsValid()) { return anExisting; @@ -1629,13 +1687,12 @@ BRepGraph_NodeId traverseEdge(BuildContext& theBuild, BRepGraph_NodeId registerTopology(BuildContext& theBuild, const TopoDS_Shape& theCurrentShape, - const TopLoc_Location& theParentLocation, - const RootRole theRootRole) + const TopLoc_Location& theParentLocation) { switch (theCurrentShape.ShapeType()) { case TopAbs_COMPOUND: - return traverseCompound(theBuild, TopoDS::Compound(theCurrentShape), theRootRole); + return traverseCompound(theBuild, TopoDS::Compound(theCurrentShape), theParentLocation); case TopAbs_COMPSOLID: return traverseCompSolid(theBuild, TopoDS::CompSolid(theCurrentShape), theParentLocation); case TopAbs_SOLID: @@ -1662,22 +1719,22 @@ void traverseFlattenedChildren(BuildContext& theBuild, const TopoDS_Shape& theContainer, const TopLoc_Location& theParentLocation) { - const TopAbs_ShapeEnum aContainerType = theContainer.ShapeType(); - const TopLoc_Location aContainerLocation = theParentLocation * theContainer.Location(); + const TopAbs_ShapeEnum aContainerType = theContainer.ShapeType(); + const TopLoc_Location aDefinitionLocation = theParentLocation * theContainer.Location(); for (TopoDS_Iterator aChildIt(theContainer, false, false); aChildIt.More(); aChildIt.Next()) { const TopoDS_Shape& aChild = aChildIt.Value(); - if (isForwardChildType(aContainerType, aChild.ShapeType())) + if (isForwardChildType(aContainerType, aChild.ShapeType()) + && isCoreParityOrientation(aChild.Orientation())) { - traverseTopology(theBuild, aChild, aContainerLocation); + traverseTopology(theBuild, aChild, aDefinitionLocation); } } } BRepGraph_NodeId traverseTopology(BuildContext& theBuild, const TopoDS_Shape& theCurrentShape, - const TopLoc_Location& theParentLocation, - const RootRole theRootRole) + const TopLoc_Location& theParentLocation) { if (theCurrentShape.IsNull()) { @@ -1693,8 +1750,7 @@ BRepGraph_NodeId traverseTopology(BuildContext& theBuild, return BRepGraph_NodeId(); } - const BRepGraph_NodeId aNode = - registerTopology(theBuild, theCurrentShape, theParentLocation, theRootRole); + const BRepGraph_NodeId aNode = registerTopology(theBuild, theCurrentShape, theParentLocation); if (theBuild.Mode == TopologyBuildMode::Flattened) { appendBuildRoot(theBuild, aNode); @@ -1703,9 +1759,14 @@ BRepGraph_NodeId traverseTopology(BuildContext& theBuild, } BRepGraphInc_Populate::BuildStatus runTopologyBuild(BuildContext& theBuild, - const TopoDS_Shape& theRootShape) + const TopoDS_Shape& theRootShape, + BRepGraph_NodeId* theOutRootNodeId = nullptr) { - traverseTopology(theBuild, theRootShape, TopLoc_Location(), RootRole::Root); + const BRepGraph_NodeId aRootNode = traverseTopology(theBuild, theRootShape, TopLoc_Location()); + if (theOutRootNodeId != nullptr) + { + *theOutRootNodeId = aRootNode; + } const uint32_t aNbPendingFaces = static_cast(theBuild.PendingFaces.Size()); if (aNbPendingFaces == 0) @@ -1753,14 +1814,15 @@ BRepGraphInc_Populate::BuildStatus buildTopology( const TopologyBuildMode theMode, const BRepGraphInc_Populate::Options& theOptions, const BuildCounts& theOldCounts, - NCollection_LinearVector* theAppendedRoots = nullptr) + NCollection_LinearVector* theAppendedRoots = nullptr, + BRepGraph_NodeId* theOutRootNodeId = nullptr) { const occ::handle aSupplementLayer = theGraph.LayerRegistry().Find(); BuildContext aBuild(theStorage, theParallel, theMode, aSupplementLayer, theAppendedRoots); - BRepGraphInc_Populate::BuildStatus aStatus = runTopologyBuild(aBuild, theShape); + BRepGraphInc_Populate::BuildStatus aStatus = runTopologyBuild(aBuild, theShape, theOutRootNodeId); (void)theOptions; (void)theOldCounts; return aStatus; @@ -1775,7 +1837,7 @@ BRepGraphInc_Populate::BuildStatus BRepGraphInc_Populate::Perform(BRepGraph& bool theParallel, const Options& theOptions) { - theGraph.incStorage().Clear(); + theGraph.Clear(); if (theShape.IsNull()) { diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx index defcb4e25d..1cb4ff0150 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx @@ -47,7 +47,6 @@ public: //! Options controlling population. struct Options { - Options() = default; }; //! Build backend incidence storage from a TopoDS_Shape. @@ -81,7 +80,8 @@ public: //! Extend existing backend storage with additional shapes (no clear). //! Preserves the full shape hierarchy: Solid/Shell/Compound/CompSolid nodes //! are created alongside Face/Edge/Vertex nodes. Shapes already present in - //! the storage (same TShape pointer) are deduplicated and not re-added. + //! the storage with the same definition identity (TShape + Location, orientation ignored) + //! are deduplicated and not re-added. //! @param[in,out] theGraph graph whose storage to extend //! @param[in] theShape shape to append //! @param[in] theParallel if true, face-level extraction runs in parallel diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx index 605bf5623e..1fcd4cbb8d 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx @@ -17,6 +17,7 @@ #include #include +#include #include #include "BRepGraphInc_WireOrder.pxx" @@ -377,7 +378,7 @@ BRepGraphInc_Storage::BRepGraphInc_Storage() myFaceTriangulations(256, myAllocator), myUIDToNodeId(1), myRefUIDToRefId(1), - myTShapeToNodeId(1), + myShapeToNodeId(1), myOriginalShapes(1) { myAllocator->SetThreadSafe(true); @@ -786,7 +787,7 @@ void BRepGraphInc_Storage::ClearStorageForReuse() myFaceTriangulations.Clear(true); myNodeToCompounds.Clear(); myNodeToOccurrences.Clear(); - myTShapeToNodeId.Clear(); + myShapeToNodeId.Clear(); myOriginalShapes.Clear(); myAllocator->Reset(false); } @@ -2772,14 +2773,123 @@ void BRepGraphInc_Storage::ClearCurrentShapes() void BRepGraphInc_Storage::CopyShapeBindingsFrom(const BRepGraphInc_Storage& theSource) { - theSource.ForEachTShapeBinding( - [this](const TopoDS_TShape* aTShape, const BRepGraph_NodeId aNodeId) { - myTShapeToNodeId.Bind(aTShape, aNodeId); - }); - theSource.ForEachOriginalBinding( - [this](const BRepGraph_NodeId aNodeId, const TopoDS_Shape& aShape) { - myOriginalShapes.Bind(aNodeId, aShape); - }); + if (&theSource == this) + { + return; + } + + NCollection_LinearVector> aShapeBindings; + NCollection_LinearVector> aOriginalBindings; + theSource.ForEachShapeBinding([&](const TopoDS_Shape& aShape, const BRepGraph_NodeId aNodeId) { + aShapeBindings.Append({aShape, aNodeId}); + }); + theSource.ForEachOriginalBinding([&](const BRepGraph_NodeId aNodeId, const TopoDS_Shape& aShape) { + aOriginalBindings.Append({aNodeId, aShape}); + }); + + std::unique_lock aLock(myShapeBindingsMutex); + myShapeToNodeId.Clear(); + myOriginalShapes.Clear(); + for (const auto& [aShape, aNodeId] : aShapeBindings) + { + myShapeToNodeId.Bind(aShape, aNodeId); + } + for (const auto& [aNodeId, aShape] : aOriginalBindings) + { + myOriginalShapes.Bind(aNodeId, aShape); + } +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraphInc_Storage::FindDefinitionByShape(const TopoDS_Shape& theShape) const +{ + std::shared_lock aLock(myShapeBindingsMutex); + const BRepGraph_NodeId* aBound = myShapeToNodeId.Seek(theShape); + if (aBound == nullptr || !aBound->IsValid()) + { + return BRepGraph_NodeId(); + } + // Validate the bound node is still active in storage. + const auto aRemovedCheck = [this](const auto theTypedId) -> bool { + return !IsRemoved(theTypedId); + }; + return BRepGraph_NodeId::Visit(*aBound, aRemovedCheck) ? *aBound : BRepGraph_NodeId(); +} + +//================================================================================================= + +bool BRepGraphInc_Storage::HasShapeBinding(const TopoDS_Shape& theShape) const +{ + return FindDefinitionByShape(theShape).IsValid(); +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetDefinitionShapeBinding(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theNodeId) +{ + std::unique_lock aLock(myShapeBindingsMutex); + if (auto* aBound = myShapeToNodeId.ChangeSeek(theShape)) + { + *aBound = theNodeId; + return; + } + myShapeToNodeId.Bind(theShape, theNodeId); +} + +//================================================================================================= + +bool BRepGraphInc_Storage::RemoveDefinitionShapeBinding(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theExpectedNodeId) +{ + std::unique_lock aLock(myShapeBindingsMutex); + const BRepGraph_NodeId* aBound = myShapeToNodeId.Seek(theShape); + if (aBound == nullptr || *aBound != theExpectedNodeId) + { + return false; + } + myShapeToNodeId.UnBind(theShape); + return true; +} + +//================================================================================================= + +TopoDS_Shape BRepGraphInc_Storage::FindOriginal(const BRepGraph_NodeId theNodeId) const +{ + std::shared_lock aLock(myShapeBindingsMutex); + const TopoDS_Shape* anOriginal = myOriginalShapes.Seek(theNodeId); + return anOriginal != nullptr ? *anOriginal : TopoDS_Shape(); +} + +//================================================================================================= + +bool BRepGraphInc_Storage::HasOriginal(const BRepGraph_NodeId theNodeId) const +{ + std::shared_lock aLock(myShapeBindingsMutex); + return myOriginalShapes.IsBound(theNodeId); +} + +//================================================================================================= + +void BRepGraphInc_Storage::BindOriginal(const BRepGraph_NodeId theNodeId, + const TopoDS_Shape& theShape) +{ + std::unique_lock aLock(myShapeBindingsMutex); + if (TopoDS_Shape* anOriginal = myOriginalShapes.ChangeSeek(theNodeId)) + { + *anOriginal = theShape; + return; + } + myOriginalShapes.Bind(theNodeId, theShape); +} + +//================================================================================================= + +void BRepGraphInc_Storage::UnBindOriginal(const BRepGraph_NodeId theNodeId) +{ + std::unique_lock aLock(myShapeBindingsMutex); + myOriginalShapes.UnBind(theNodeId); } //================================================================================================= @@ -3116,17 +3226,16 @@ bool BRepGraphInc_Storage::MarkRemoved(const BRepGraph_NodeId theNodeId) theNodeId.IsValid() ? BRepGraph_NodeId::Visit(theNodeId, aMarkRemoved) : false; if (isRemoved) { - const TopoDS_Shape* aShape = myOriginalShapes.Seek(theNodeId); - const TopoDS_TShape* aTShapeToUnbind = - (aShape != nullptr && !aShape->IsNull()) ? aShape->TShape().get() : nullptr; - myOriginalShapes.UnBind(theNodeId); - if (aTShapeToUnbind != nullptr) + const TopoDS_Shape* aBoundOriginal = myOriginalShapes.Seek(theNodeId); + TopoDS_Shape aShapeToUnbind; + if (aBoundOriginal != nullptr) { - const BRepGraph_NodeId* aBound = myTShapeToNodeId.Seek(aTShapeToUnbind); - if (aBound != nullptr && *aBound == theNodeId) - { - myTShapeToNodeId.UnBind(aTShapeToUnbind); - } + aShapeToUnbind = *aBoundOriginal; + } + myOriginalShapes.UnBind(theNodeId); + if (!aShapeToUnbind.IsNull()) + { + RemoveDefinitionShapeBinding(aShapeToUnbind, theNodeId); } } return isRemoved; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx index fbc0119f6f..2f063563e5 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1220,55 +1221,46 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_RefId FindRefIdByUID(const BRepGraph_RefUID& theUID) const; - //! Returns the node id bound to the given TShape, or nullptr if not bound. - [[nodiscard]] const BRepGraph_NodeId* FindNodeByTShape(const TopoDS_TShape* theTShape) const - { - return myTShapeToNodeId.Seek(theTShape); - } + //! Returns the node id bound to the given shape definition key, or invalid if not bound. + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId + FindDefinitionByShape(const TopoDS_Shape& theShape) const; - //! Returns true if the given TShape is bound to a node. - [[nodiscard]] bool HasTShapeBinding(const TopoDS_TShape* theTShape) const - { - return myTShapeToNodeId.IsBound(theTShape); - } + //! Returns true if the given shape definition key is bound to a node. + [[nodiscard]] Standard_EXPORT bool HasShapeBinding(const TopoDS_Shape& theShape) const; - //! Binds the given TShape to a node id. - void BindTShapeToNode(const TopoDS_TShape* theTShape, const BRepGraph_NodeId theNodeId) - { - myTShapeToNodeId.Bind(theTShape, theNodeId); - } + //! Set or update the shape-to-node binding. Uses replacement semantics: + //! binds if absent, updates if already bound. + Standard_EXPORT void SetDefinitionShapeBinding(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theNodeId); - //! Back-reference to the source `TopoDS_Shape` a node was built from. - //! Only populated during Build; absent bindings are a valid state. + //! Remove the shape-to-node binding only if it points to the expected node. + //! Returns true if the binding was removed. + Standard_EXPORT bool RemoveDefinitionShapeBinding(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theExpectedNodeId); - //! Returns the original shape for the given node id, or nullptr if not bound. - [[nodiscard]] const TopoDS_Shape* FindOriginal(const BRepGraph_NodeId theNodeId) const - { - return myOriginalShapes.Seek(theNodeId); - } + //! Back-reference to the construction-time `TopoDS_Shape` key a node was built from. + //! Only populated during graph construction; absent bindings are a valid state. + + //! Returns the original shape for the given node id, or a null shape if not bound. + [[nodiscard]] Standard_EXPORT TopoDS_Shape FindOriginal(const BRepGraph_NodeId theNodeId) const; //! Returns true if the given node id has an original shape binding. - [[nodiscard]] bool HasOriginal(const BRepGraph_NodeId theNodeId) const - { - return myOriginalShapes.IsBound(theNodeId); - } + [[nodiscard]] Standard_EXPORT bool HasOriginal(const BRepGraph_NodeId theNodeId) const; - //! Binds the given node id to its original shape. - void BindOriginal(const BRepGraph_NodeId theNodeId, const TopoDS_Shape& theShape) - { - myOriginalShapes.Bind(theNodeId, theShape); - } + //! Binds the given node id to its construction-time shape key. + Standard_EXPORT void BindOriginal(const BRepGraph_NodeId theNodeId, const TopoDS_Shape& theShape); //! Removes the original shape binding for the given node id. - void UnBindOriginal(const BRepGraph_NodeId theNodeId) { myOriginalShapes.UnBind(theNodeId); } + Standard_EXPORT void UnBindOriginal(const BRepGraph_NodeId theNodeId); - //! Iterate all TShape-to-NodeId bindings, invoking theFunc(TShape*, NodeId) for each entry. + //! Iterate all shape-to-NodeId bindings, invoking theFunc(shape, NodeId) for each entry. //! Used by Compact to rebuild the map after the rebuild-and-swap. template - void ForEachTShapeBinding(FuncT&& theFunc) const + void ForEachShapeBinding(FuncT&& theFunc) const { - for (NCollection_FlatDataMap::Iterator anIt( - myTShapeToNodeId); + std::shared_lock aLock(myShapeBindingsMutex); + for (NCollection_FlatDataMap::Iterator + anIt(myShapeToNodeId); anIt.More(); anIt.Next()) { @@ -1281,6 +1273,7 @@ public: template void ForEachOriginalBinding(FuncT&& theFunc) const { + std::shared_lock aLock(myShapeBindingsMutex); for (NCollection_FlatDataMap::Iterator anIt(myOriginalShapes); anIt.More(); anIt.Next()) @@ -1289,7 +1282,7 @@ public: } } - //! Copy TShape-to-NodeId and Original shape bindings from another storage. + //! Copy shape-to-NodeId and Original shape bindings from another storage. //! Used by identity copy to preserve shape reconstruction bindings. Standard_EXPORT void CopyShapeBindingsFrom(const BRepGraphInc_Storage& theSource); @@ -1890,8 +1883,9 @@ private: mutable std::atomic myRefUIDToRefIdDirty{false}; //! Bindings from reconstructed / source OCCT shapes back to backend ids. - NCollection_FlatDataMap myTShapeToNodeId; - NCollection_FlatDataMap myOriginalShapes; + NCollection_FlatDataMap myShapeToNodeId; + NCollection_FlatDataMap myOriginalShapes; + mutable std::shared_mutex myShapeBindingsMutex; //! Persistent backend identity state. std::atomic myGeneration{0}; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraphInc_PopulateRegression_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraphInc_PopulateRegression_Test.cxx new file mode 100644 index 0000000000..6425e3c616 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraphInc_PopulateRegression_Test.cxx @@ -0,0 +1,501 @@ +// Copyright (c) 2026 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +TopLoc_Location translationLocation(const double theX, const double theY, const double theZ) +{ + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(theX, theY, theZ)); + return TopLoc_Location(aTrsf); +} + +Bnd_Box geometryBounds(const TopoDS_Shape& theShape) +{ + Bnd_Box aBox; + BRepBndLib::AddOptimal(theShape, aBox, false, false); + return aBox; +} + +Bnd_Box meshBounds(const TopoDS_Shape& theShape) +{ + Bnd_Box aBox; + BRepBndLib::Add(theShape, aBox, true); + return aBox; +} + +void expectBoxNear(const Bnd_Box& theActual, const Bnd_Box& theExpected, const double theTol) +{ + const Bnd_Box::Limits anActual = theActual.Get(); + const Bnd_Box::Limits anExpected = theExpected.Get(); + EXPECT_NEAR(anActual.Xmin, anExpected.Xmin, theTol); + EXPECT_NEAR(anActual.Ymin, anExpected.Ymin, theTol); + EXPECT_NEAR(anActual.Zmin, anExpected.Zmin, theTol); + EXPECT_NEAR(anActual.Xmax, anExpected.Xmax, theTol); + EXPECT_NEAR(anActual.Ymax, anExpected.Ymax, theTol); + EXPECT_NEAR(anActual.Zmax, anExpected.Zmax, theTol); +} + +TopoDS_Shape firstSubShape(const TopoDS_Shape& theShape, const TopAbs_ShapeEnum theType) +{ + TopExp_Explorer anExp(theShape, theType); + return anExp.More() ? anExp.Current() : TopoDS_Shape(); +} + +TopoDS_Face firstFaceOfBox(const double theSize) +{ + TopoDS_Shape aBox = BRepPrimAPI_MakeBox(theSize, theSize, theSize).Shape(); + TopoDS_Shape aFace = firstSubShape(aBox, TopAbs_FACE); + return TopoDS::Face(aFace); +} + +uint32_t countSourcePolygonOnTriangulation(const TopoDS_Shape& theShape, + const bool theUseFaceLocation, + bool& theHasLocatedTriangulation) +{ + uint32_t aCount = 0; + for (TopExp_Explorer aFaceExp(theShape, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face(aFaceExp.Current()); + TopLoc_Location aTriLoc; + const occ::handle aTri = BRep_Tool::Triangulation(aFace, aTriLoc); + if (aTri.IsNull()) + { + continue; + } + theHasLocatedTriangulation = theHasLocatedTriangulation || !aTriLoc.IsIdentity(); + + for (TopExp_Explorer anEdgeExp(aFace, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) + { + const TopoDS_Edge& anEdge = TopoDS::Edge(anEdgeExp.Current()); + const TopLoc_Location aLookupLoc = theUseFaceLocation ? aTriLoc : TopLoc_Location(); + if (!BRep_Tool::PolygonOnTriangulation(anEdge, aTri, aLookupLoc).IsNull()) + { + ++aCount; + } + } + } + return aCount; +} + +uint32_t countGraphPolygonOnTriangulation(const BRepGraph& theGraph) +{ + uint32_t aCount = 0; + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(theGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) + { + if (theGraph.Topo().CoEdges().Definition(aCoEdgeId).PolygonOnTriRepId.IsValid()) + { + ++aCount; + } + } + return aCount; +} + +TopoDS_Face makeNoWireNaturalSphereFace() +{ + BRep_Builder aBuilder; + TopoDS_Face aFace; + aBuilder.MakeFace( + aFace, + new Geom_SphericalSurface(gp_Ax3(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0)), 10.0), + Precision::Confusion()); + aBuilder.NaturalRestriction(aFace, true); + return aFace; +} + +uint32_t countDegeneratedEdges(const TopoDS_Shape& theShape) +{ + uint32_t aCount = 0; + for (TopExp_Explorer anEdgeExp(theShape, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) + { + if (BRep_Tool::Degenerated(TopoDS::Edge(anEdgeExp.Current()))) + { + ++aCount; + } + } + return aCount; +} + +TopoDS_Compound makeNestedCompound(const TopLoc_Location& theRootLocation, + const TopLoc_Location& theInnerLocation, + const TopLoc_Location& theSolidLocation) +{ + TopoDS_Shape aBox = BRepPrimAPI_MakeBox(2.0, 3.0, 4.0).Shape(); + BRepMesh_IncrementalMesh aMesher(aBox, 0.2); + EXPECT_TRUE(aMesher.IsDone()); + + BRep_Builder aBuilder; + const TopoDS_Solid aMovedSolid = TopoDS::Solid(TopoDS::Solid(aBox).Moved(theSolidLocation)); + + TopoDS_Compound anInner; + aBuilder.MakeCompound(anInner); + aBuilder.Add(anInner, aMovedSolid); + const TopoDS_Compound aMovedInner = TopoDS::Compound(anInner.Moved(theInnerLocation)); + + TopoDS_Compound aRoot; + aBuilder.MakeCompound(aRoot); + aBuilder.Add(aRoot, aMovedInner); + return TopoDS::Compound(aRoot.Moved(theRootLocation)); +} +} // namespace + +TEST(BRepGraphInc_PopulateRegressionTest, LocatedFacePolygonOnTriangulation_IsCaptured) +{ + TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + BRepMesh_IncrementalMesh aMesher(aBox, 0.5); + ASSERT_TRUE(aMesher.IsDone()); + const TopoDS_Shape aMovedBox = aBox.Moved(translationLocation(5.0, 6.0, 7.0)); + + bool hasLocatedTriangulation = false; + const uint32_t aSourceCount = + countSourcePolygonOnTriangulation(aMovedBox, true, hasLocatedTriangulation); + bool hasLocatedTriangulationForIdentityProbe = false; + const uint32_t anIdentityLookupCount = + countSourcePolygonOnTriangulation(aMovedBox, false, hasLocatedTriangulationForIdentityProbe); + + ASSERT_TRUE(hasLocatedTriangulation); + ASSERT_GT(aSourceCount, 0u); + EXPECT_EQ(anIdentityLookupCount, 0u); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aMovedBox, false), + BRepGraphInc_Populate::BuildStatus::Failed); + EXPECT_GT(countGraphPolygonOnTriangulation(aGraph), 0u); +} + +TEST(BRepGraphInc_PopulateRegressionTest, NoWireNaturalSphere_DegenerateBoundaryEdgesRoundTrip) +{ + const TopoDS_Face aFace = makeNoWireNaturalSphereFace(); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aFace, false), + BRepGraphInc_Populate::BuildStatus::Failed); + + uint32_t aDegenerateCount = 0; + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aGraph.Topo().Edges().Nb()); ++anEdgeId) + { + if (BRepGraph_Tool::Edge::Degenerated(aGraph, anEdgeId)) + { + ++aDegenerateCount; + EXPECT_FALSE(aGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId.IsValid()); + } + } + ASSERT_GE(aDegenerateCount, 2u); + + const TopoDS_Shape aReconstructed = + BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_FaceId::Start()); + ASSERT_FALSE(aReconstructed.IsNull()); + EXPECT_GE(countDegeneratedEdges(aReconstructed), 2u); +} + +TEST(BRepGraphInc_PopulateRegressionTest, CompoundReferencesKeepDirectSourceLocationsAtEveryLevel) +{ + const TopLoc_Location aRootLoc = translationLocation(0.0, 0.0, 11.0); + const TopLoc_Location anInnerLoc = translationLocation(0.0, 7.0, 0.0); + const TopLoc_Location aSolidLoc = translationLocation(5.0, 0.0, 0.0); + const TopoDS_Compound aMovedRoot = makeNestedCompound(aRootLoc, anInnerLoc, aSolidLoc); + + BRepGraph aGraph; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aMovedRoot); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_EQ(aResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Compound); + ASSERT_TRUE(aResult.Product.IsValid()); + + const BRepGraphInc::ProductRelations& aProductRelations = + aGraph.Topo().Products().Relations(aResult.Product); + ASSERT_EQ(aProductRelations.OccurrenceRefIds.Size(), 1u); + const BRepGraphInc::OccurrenceRef& anOccurrenceRef = + aGraph.Refs().Occurrences().Entry(aProductRelations.OccurrenceRefIds.Value(0)); + EXPECT_TRUE(anOccurrenceRef.LocalLocation.IsEqual(aRootLoc)); + + const BRepGraph_CompoundId aRootCompound(aResult.TopologyRoot); + const NCollection_LinearVector& aRootRefs = + aGraph.Topo().Compounds().Relations(aRootCompound).ChildRefIds; + ASSERT_EQ(aRootRefs.Size(), 1u); + const BRepGraph_ChildRefId aRootChildRef = aRootRefs.Value(0); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(aRootChildRef).IsEqual(anInnerLoc)); + + const BRepGraph_NodeId anInnerNode = aGraph.Refs().Children().Entry(aRootChildRef).ChildNodeId; + ASSERT_EQ(anInnerNode.NodeKind, BRepGraph_NodeId::Kind::Compound); + const NCollection_LinearVector& anInnerRefs = + aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId(anInnerNode)).ChildRefIds; + ASSERT_EQ(anInnerRefs.Size(), 1u); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(anInnerRefs.Value(0)).IsEqual(aSolidLoc)); +} + +TEST(BRepGraphInc_PopulateRegressionTest, ShapeViewFindNode_AcceptsOriginalPlacedSubshapes) +{ + const TopLoc_Location aRootLoc = translationLocation(0.0, 0.0, 11.0); + const TopLoc_Location anInnerLoc = translationLocation(0.0, 7.0, 0.0); + const TopLoc_Location aSolidLoc = translationLocation(5.0, 0.0, 0.0); + const TopoDS_Compound aMovedRoot = makeNestedCompound(aRootLoc, anInnerLoc, aSolidLoc); + + const TopoDS_Shape anOriginalSolid = firstSubShape(aMovedRoot, TopAbs_SOLID); + const TopoDS_Shape anOriginalFace = firstSubShape(aMovedRoot, TopAbs_FACE); + ASSERT_FALSE(anOriginalSolid.IsNull()); + ASSERT_FALSE(anOriginalFace.IsNull()); + + BRepGraph aGraph; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aMovedRoot); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_TRUE(aResult.TopologyRoot.IsValid()); + ASSERT_TRUE(aResult.Product.IsValid()); + + EXPECT_EQ(aGraph.Shapes().FindNode(aMovedRoot), aResult.TopologyRoot); + EXPECT_EQ(aGraph.Shapes().FindNode(anOriginalSolid).NodeKind, BRepGraph_NodeId::Kind::Solid); + EXPECT_EQ(aGraph.Shapes().FindNode(anOriginalFace).NodeKind, BRepGraph_NodeId::Kind::Face); + + const TopoDS_Shape aProductShape = aGraph.Shapes().Shape(aResult.Product); + ASSERT_FALSE(aProductShape.IsNull()); + expectBoxNear(geometryBounds(aProductShape), geometryBounds(aMovedRoot), 1.0e-6); + expectBoxNear(meshBounds(aProductShape), meshBounds(aMovedRoot), 1.0e-6); + + TopoDS_Shape aRootLocalShape = aMovedRoot; + aRootLocalShape.Location(TopLoc_Location()); + const TopoDS_Shape aTopologyShape = aGraph.Shapes().Shape(aResult.TopologyRoot); + ASSERT_FALSE(aTopologyShape.IsNull()); + expectBoxNear(geometryBounds(aTopologyShape), geometryBounds(aRootLocalShape), 1.0e-6); +} + +TEST(BRepGraphInc_PopulateRegressionTest, Compact_PreservesAssemblyShapeLookupAndPlacement) +{ + const TopLoc_Location aRootLoc = translationLocation(0.0, 0.0, 11.0); + const TopLoc_Location anInnerLoc = translationLocation(0.0, 7.0, 0.0); + const TopLoc_Location aSolidLoc = translationLocation(5.0, 0.0, 0.0); + const TopoDS_Compound aMovedRoot = makeNestedCompound(aRootLoc, anInnerLoc, aSolidLoc); + + const TopoDS_Shape anOriginalSolid = firstSubShape(aMovedRoot, TopAbs_SOLID); + const TopoDS_Shape anOriginalFace = firstSubShape(aMovedRoot, TopAbs_FACE); + ASSERT_FALSE(anOriginalSolid.IsNull()); + ASSERT_FALSE(anOriginalFace.IsNull()); + + BRepGraph aGraph; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aMovedRoot); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_TRUE(aResult.TopologyRoot.IsValid()); + ASSERT_TRUE(aResult.Product.IsValid()); + + BRepGraph::ShapesView::Options aNoProduct; + aNoProduct.CreateAutoProduct = false; + const BRepGraph::ShapesView::Result aJunk = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape(), aNoProduct); + ASSERT_TRUE(aJunk.IsOk()); + ASSERT_TRUE(aJunk.TopologyRoot.IsValid()); + aGraph.Editor().Gen().RemoveSubgraph(aJunk.TopologyRoot); + + const BRepGraph_Compact::Result aCompact = BRepGraph_Compact::Perform(aGraph); + ASSERT_GT(aCompact.NbNodesBefore, aCompact.NbNodesAfter); + + EXPECT_EQ(aGraph.Shapes().FindNode(aMovedRoot), aResult.TopologyRoot); + EXPECT_EQ(aGraph.Shapes().FindNode(anOriginalSolid).NodeKind, BRepGraph_NodeId::Kind::Solid); + EXPECT_EQ(aGraph.Shapes().FindNode(anOriginalFace).NodeKind, BRepGraph_NodeId::Kind::Face); + + const BRepGraph_CompoundId aRootCompound(aGraph.Shapes().FindNode(aMovedRoot)); + ASSERT_TRUE(aRootCompound.IsValid()); + const NCollection_LinearVector& aRootRefs = + aGraph.Topo().Compounds().Relations(aRootCompound).ChildRefIds; + ASSERT_EQ(aRootRefs.Size(), 1u); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(aRootRefs.Value(0)).IsEqual(anInnerLoc)); + + const BRepGraph_NodeId anInnerNode = + aGraph.Refs().Children().Entry(aRootRefs.Value(0)).ChildNodeId; + ASSERT_EQ(anInnerNode.NodeKind, BRepGraph_NodeId::Kind::Compound); + const NCollection_LinearVector& anInnerRefs = + aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId(anInnerNode)).ChildRefIds; + ASSERT_EQ(anInnerRefs.Size(), 1u); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(anInnerRefs.Value(0)).IsEqual(aSolidLoc)); + + TopoDS_Shape aRootLocalShape = aMovedRoot; + aRootLocalShape.Location(TopLoc_Location()); + const TopoDS_Shape aReconstructedRoot = BRepGraphInc_Reconstruct::Node(aGraph, aRootCompound); + ASSERT_FALSE(aReconstructedRoot.IsNull()); + { + SCOPED_TRACE("topology root reconstruction"); + expectBoxNear(geometryBounds(aReconstructedRoot), geometryBounds(aRootLocalShape), 1.0e-6); + } + + const TopoDS_Shape aProductShape = aGraph.Shapes().Shape(aResult.Product); + ASSERT_FALSE(aProductShape.IsNull()); + { + SCOPED_TRACE("product reconstruction"); + expectBoxNear(geometryBounds(aProductShape), geometryBounds(aMovedRoot), 1.0e-6); + } +} + +TEST(BRepGraphInc_PopulateRegressionTest, Append_ReusesSameLocatedDefinitionAcrossCalls) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(2.0, 3.0, 4.0).Shape(); + const TopoDS_Shape aMovedBox = aBox.Moved(translationLocation(5.0, 0.0, 0.0)); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Append(aGraph, aMovedBox, false), + BRepGraphInc_Populate::BuildStatus::Failed); + const uint32_t aNbSolidsAfterFirst = aGraph.Topo().Solids().Nb(); + const BRepGraph_NodeId aFirstNode = aGraph.Shapes().FindNode(aMovedBox); + ASSERT_TRUE(aFirstNode.IsValid()); + + ASSERT_NE(BRepGraphInc_Populate::Append(aGraph, aMovedBox, false), + BRepGraphInc_Populate::BuildStatus::Failed); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsAfterFirst); + EXPECT_EQ(aGraph.Shapes().FindNode(aMovedBox), aFirstNode); + + const TopoDS_Shape aSecondPlacement = aBox.Moved(translationLocation(9.0, 0.0, 0.0)); + ASSERT_NE(BRepGraphInc_Populate::Append(aGraph, aSecondPlacement, false), + BRepGraphInc_Populate::BuildStatus::Failed); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsAfterFirst + 1u); + EXPECT_NE(aGraph.Shapes().FindNode(aSecondPlacement), aFirstNode); +} + +TEST(BRepGraphInc_PopulateRegressionTest, PersistentTriangulationBounds_AgreeWithMovedNodes) +{ + TopoDS_Shape aBox = BRepPrimAPI_MakeBox(2.0, 3.0, 4.0).Shape(); + BRepMesh_IncrementalMesh aMesher(aBox, 0.2); + ASSERT_TRUE(aMesher.IsDone()); + for (TopExp_Explorer aFaceExp(aBox, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) + { + TopLoc_Location aTriLoc; + occ::handle aTri = + BRep_Tool::Triangulation(TopoDS::Face(aFaceExp.Current()), aTriLoc); + if (!aTri.IsNull()) + { + aTri->UpdateCachedMinMax(); + } + } + + const TopoDS_Shape aMovedBox = aBox.Moved(translationLocation(25.0, -3.0, 8.0)); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aMovedBox, false), + BRepGraphInc_Populate::BuildStatus::Failed); + + bool hasTriangulation = false; + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aGraph.Topo().Faces().Nb()); ++aFaceId) + { + const occ::handle& aTri = + aGraph.Mesh().Persistent().Faces().Triangulation(aFaceId); + if (aTri.IsNull()) + { + continue; + } + hasTriangulation = true; + EXPECT_FALSE(aTri->HasCachedMinMax()); + } + EXPECT_TRUE(hasTriangulation); + + const TopoDS_Shape aReconstructed = + BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); + ASSERT_FALSE(aReconstructed.IsNull()); + expectBoxNear(meshBounds(aReconstructed), meshBounds(aMovedBox), 1.0e-6); +} + +TEST(BRepGraphInc_PopulateRegressionTest, Perform_ReplacesGraphThroughClearSemantics) +{ + BRepGraph aGraph; + ASSERT_NE( + BRepGraphInc_Populate::Perform(aGraph, BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape(), false), + BRepGraphInc_Populate::BuildStatus::Failed); + + const occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + TopoDS_Vertex aVertex; + BRep_Builder().MakeVertex(aVertex, gp_Pnt(42.0, 0.0, 0.0), Precision::Confusion()); + ASSERT_NE(aLayer->AddAttachment(BRepGraph_NodeId(BRepGraph_SolidId::Start()), + BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape, + aVertex), + 0u); + + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + occ::handle aPolygon = new Poly_Polygon3D(2, false); + aGraph.Mesh().Editor().Edges().SetCachedPolygon3D(anEdgeId, aPolygon); + ASSERT_NE(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr); + + ASSERT_NE( + BRepGraphInc_Populate::Perform(aGraph, BRepPrimAPI_MakeBox(2.0, 2.0, 2.0).Shape(), false), + BRepGraphInc_Populate::BuildStatus::Failed); + EXPECT_TRUE(aLayer->AttachedTo(BRepGraph_NodeId(BRepGraph_SolidId::Start())).IsEmpty()); + EXPECT_EQ(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr); +} + +TEST(BRepGraphInc_PopulateRegressionTest, FlattenedTraversal_SkipsInternalExternalChildren) +{ + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + + TopoDS_Face aForwardFace = firstFaceOfBox(1.0); + aForwardFace.Orientation(TopAbs_FORWARD); + TopoDS_Face anInternalFace = firstFaceOfBox(2.0); + anInternalFace.Orientation(TopAbs_INTERNAL); + TopoDS_Face anExternalFace = firstFaceOfBox(3.0); + anExternalFace.Orientation(TopAbs_EXTERNAL); + + aBuilder.Add(aCompound, aForwardFace); + aBuilder.Add(aCompound, anInternalFace); + aBuilder.Add(aCompound, anExternalFace); + + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + anOptions.Flatten = true; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aCompound, anOptions); + ASSERT_TRUE(aResult.IsOk()); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1u); + ASSERT_TRUE(aResult.TopologyRoot.IsValid()); + EXPECT_EQ(aResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Face); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx index d36c5b3f02..ac755b69f8 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx @@ -755,7 +755,7 @@ TEST(BRepGraphIncTest, Compound_TranslatedChildren_VolumePreserved) EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), 12); } -TEST(BRepGraphIncTest, Populate_BakesNormalChildLocationsIntoDefinitions) +TEST(BRepGraphIncTest, Populate_AppliesNormalChildLocationsToDefinitions) { const TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); @@ -798,7 +798,7 @@ TEST(BRepGraphIncTest, Populate_BakesNormalChildLocationsIntoDefinitions) EXPECT_EQ(countSubShapes(aRecon, TopAbs_VERTEX), 4); } -TEST(BRepGraphIncTest, Populate_BakesStackedNormalLocationsIntoDefinitions) +TEST(BRepGraphIncTest, Populate_AppliesStackedNormalLocationsToDefinitions) { gp_Trsf anEdgeTrsf; anEdgeTrsf.SetTranslation(gp_Vec(2.0, 0.0, 0.0)); @@ -909,46 +909,6 @@ TEST(BRepGraphIncTest, Populate_BakesStackedNormalLocationsIntoDefinitions) EXPECT_NEAR(aMaxZ, 5.0, Precision::Confusion()); } -TEST(BRepGraphIncTest, Populate_RootCompoundLocationIsPreservedByChildRefs) -{ - BRep_Builder aBuilder; - TopoDS_Compound aCompound; - aBuilder.MakeCompound(aCompound); - aBuilder.Add(aCompound, BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0))); - - gp_Trsf aTrsf; - aTrsf.SetTranslation(gp_Vec(25.0, 0.0, 0.0)); - const TopLoc_Location aLoc(aTrsf); - const TopoDS_Compound aMovedCompound = TopoDS::Compound(aCompound.Moved(aLoc)); - - BRepGraph aGraph; - ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aMovedCompound, false), - BRepGraphInc_Populate::BuildStatus::Failed); - ASSERT_FALSE(aGraph.IsEmpty()); - - ASSERT_EQ(aGraph.Topo().Compounds().Nb(), 1u); - const NCollection_LinearVector& aChildRefs = - aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId::Start()).ChildRefIds; - ASSERT_EQ(aChildRefs.Size(), 1u); - const BRepGraph_ChildRefId aChildRef = aChildRefs.Value(0); - EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(aChildRef).IsEqual(aLoc)); - - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_CompoundId::Start()); - ASSERT_FALSE(aRecon.IsNull()); - - double aMinX = RealLast(); - double aMaxX = -RealLast(); - for (TopExp_Explorer anExp(aRecon, TopAbs_VERTEX); anExp.More(); anExp.Next()) - { - const gp_Pnt aPoint = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current())); - aMinX = std::min(aMinX, aPoint.X()); - aMaxX = std::max(aMaxX, aPoint.X()); - } - - EXPECT_NEAR(aMinX, 25.0, Precision::Confusion()); - EXPECT_NEAR(aMaxX, 26.0, Precision::Confusion()); -} - TEST(BRepGraphIncTest, Cylinder_RoundTrip_BRepDump) { BRepPrimAPI_MakeCylinder aCylMaker(5.0, 20.0); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx index b0cc22ff17..e720efb701 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx @@ -18,8 +18,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -40,6 +42,25 @@ static double translationX(const TopoDS_Shape& theShape) return theShape.Location().Transformation().TranslationPart().X(); } +Bnd_Box geometryBounds(const TopoDS_Shape& theShape) +{ + Bnd_Box aBox; + BRepBndLib::AddOptimal(theShape, aBox, false, false); + return aBox; +} + +void expectBoxNear(const Bnd_Box& theActual, const Bnd_Box& theExpected, const double theTol) +{ + const Bnd_Box::Limits anActual = theActual.Get(); + const Bnd_Box::Limits anExpected = theExpected.Get(); + EXPECT_NEAR(anActual.Xmin, anExpected.Xmin, theTol); + EXPECT_NEAR(anActual.Ymin, anExpected.Ymin, theTol); + EXPECT_NEAR(anActual.Zmin, anExpected.Zmin, theTol); + EXPECT_NEAR(anActual.Xmax, anExpected.Xmax, theTol); + EXPECT_NEAR(anActual.Ymax, anExpected.Ymax, theTol); + EXPECT_NEAR(anActual.Zmax, anExpected.Zmax, theTol); +} + NCollection_LinearVector collectRootProducts(const BRepGraph& theGraph) { NCollection_LinearVector aRoots(4); @@ -1231,7 +1252,8 @@ TEST(BRepGraph_AssemblyTest, OccurrenceLocation_AlwaysTerminates) TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) { BRepGraph aGraph; - TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + TopoDS_Shape aLocalBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + TopoDS_Shape aBox = aLocalBox; gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(5.0, 6.0, 7.0)); aBox.Location(TopLoc_Location(aTrsf)); @@ -1241,6 +1263,7 @@ TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) ASSERT_TRUE(aResult.Product.IsValid()); ASSERT_TRUE(aResult.TopologyRoot.IsValid()); ASSERT_TRUE(aResult.Occurrence.IsValid()); + EXPECT_EQ(aGraph.Shapes().FindNode(aBox), aResult.TopologyRoot); ASSERT_EQ(aGraph.RootProductIds().Size(), 1u); EXPECT_EQ(aGraph.RootProductIds().Value(0), aResult.Product); @@ -1248,9 +1271,16 @@ TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) const BRepGraph_OccurrenceRefId anOccRefId = aGraph.Topo().Products().Relations(aResult.Product).OccurrenceRefIds.Value(0); const TopLoc_Location& aLoc = aGraph.Refs().Occurrences().Entry(anOccRefId).LocalLocation; - EXPECT_NEAR(aLoc.Transformation().TranslationPart().X(), 5.0, Precision::Confusion()); - EXPECT_NEAR(aLoc.Transformation().TranslationPart().Y(), 6.0, Precision::Confusion()); - EXPECT_NEAR(aLoc.Transformation().TranslationPart().Z(), 7.0, Precision::Confusion()); + EXPECT_TRUE(aLoc.IsEqual(aBox.Location())); + + const TopoDS_Shape aProductShape = aGraph.Shapes().Shape(aResult.Product); + ASSERT_FALSE(aProductShape.IsNull()); + expectBoxNear(geometryBounds(aProductShape), geometryBounds(aBox), 1.0e-7); + + const TopoDS_Shape aTopologyShape = aGraph.Shapes().Shape(aResult.TopologyRoot); + ASSERT_FALSE(aTopologyShape.IsNull()); + EXPECT_TRUE(aTopologyShape.Location().IsIdentity()); + expectBoxNear(geometryBounds(aTopologyShape), geometryBounds(aLocalBox), 1.0e-7); } TEST(BRepGraph_AssemblyTest, Add_NoAutoProduct_TopologyOnly) @@ -1308,7 +1338,7 @@ TEST(BRepGraph_AssemblyTest, Add_ProductParent_CreatesChildPartAndOccurrence) ASSERT_EQ(aParentRelations.OccurrenceRefIds.Size(), 1u); const BRepGraph_OccurrenceRefId anOccRefId = aParentRelations.OccurrenceRefIds.Value(0); const TopLoc_Location& aLoc = aGraph.Refs().Occurrences().Entry(anOccRefId).LocalLocation; - EXPECT_NEAR(aLoc.Transformation().TranslationPart().X(), 2.0, Precision::Confusion()); + EXPECT_TRUE(aLoc.IsEqual(aSphere.Location())); } TEST(BRepGraph_AssemblyTest, Add_CompoundParent_AppendsAsChild) diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx index 0db9754415..2160be9aca 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -435,13 +436,19 @@ TEST(BRepGraph_GeometryTest, CompoundWithMovedChild_SharedSolidDef) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aCompound); + const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aCompound); + ASSERT_TRUE(aBuildRes12.IsOk()); ASSERT_FALSE(aGraph.IsEmpty()); - // Moved() preserves TShape, but locations are baked into topology definitions. - EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); - // Verify the graph was built successfully. - EXPECT_FALSE(aGraph.IsEmpty()); + // Moved() preserves TShape. The solid definition is shared and the moved + // usage keeps its placement on the compound child reference. + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); + const BRepGraph_CompoundId aCompoundId(aBuildRes12.TopologyRoot); + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(aCompoundId).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 2u); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(aChildRefs.Value(0)).IsIdentity()); + EXPECT_FALSE(aGraph.Refs().Gen().LocalLocation(aChildRefs.Value(1)).IsIdentity()); } TEST(BRepGraph_GeometryTest, FaceDef_Triangulation_NullForAnalyticNoCrash) diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx index c1661ec8f2..2bc3a9745c 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx @@ -16,6 +16,7 @@ #include #include #include "BRepGraph_RefTestTools.hxx" +#include #include #include #include @@ -345,14 +346,20 @@ TEST_F(BRepGraph_SharingTest, CompoundWithLocation_MoreUsagesThanDefs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aCompound); + const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aCompound); + ASSERT_TRUE(aBuildRes4.IsOk()); ASSERT_FALSE(aGraph.IsEmpty()); - // Same TShape with different locations: normal topology definitions are split - // because the location is baked into the definitions during Populate. - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); - EXPECT_EQ(aGraph.Topo().Edges().Nb(), 24); - EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 16); + // Same TShape with different compound usages: topology definitions are shared + // and usage placement is stored on ChildRef. + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId(aBuildRes4.TopologyRoot)).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 2u); + EXPECT_FALSE(aGraph.Refs().Gen().LocalLocation(aChildRefs.Value(1)).IsIdentity()); } TEST_F(BRepGraph_SharingTest, TranslatedCopy_SameTShape_SharedDefs) @@ -372,13 +379,18 @@ TEST_F(BRepGraph_SharingTest, TranslatedCopy_SameTShape_SharedDefs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aCompound); + const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aCompound); + ASSERT_TRUE(aBuildRes5.IsOk()); ASSERT_FALSE(aGraph.IsEmpty()); - // Moved() preserves TShape, but locations are baked into topology definitions. - EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); - - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); - EXPECT_EQ(aGraph.Topo().Edges().Nb(), 24); - EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 16); + // Moved() preserves TShape, so the definition stays shared while the moved + // usage is represented by a located compound child reference. + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId(aBuildRes5.TopologyRoot)).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 2u); + EXPECT_FALSE(aGraph.Refs().Gen().LocalLocation(aChildRefs.Value(1)).IsIdentity()); } diff --git a/src/ModelingData/TKBRep/GTests/FILES.cmake b/src/ModelingData/TKBRep/GTests/FILES.cmake index c7f485e56d..7546570d2e 100644 --- a/src/ModelingData/TKBRep/GTests/FILES.cmake +++ b/src/ModelingData/TKBRep/GTests/FILES.cmake @@ -9,6 +9,7 @@ set(OCCT_TKBRep_GTests_FILES BRepGraph_BatchOps_Test.cxx BRepGraph_DefsIterator_Test.cxx BRepGraphInc_Test.cxx + BRepGraphInc_PopulateRegression_Test.cxx BRepGraph_ShapesViewImport_Test.cxx BRepGraph_NodeId_Test.cxx BRepGraph_RefId_Test.cxx