Files
OCCT/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx
T
Pasukhin Dmitry ea3fc1f8bd Modeling, BRepGraph - Identity copy path, lock-free cache reads, per-CoEdge queries (#1306)
Identity copy path:
- copyFullGraphIdentity with PrepareForLoad() + direct slot assignment
- CopyDerivedRelationsFrom for O(K) relation vector copy
- CopyRemovedFlagsFrom for bulk removed-flag copy
- CopyShapeBindingsFrom for TShape/Original shape binding copy
- PrepareForLoad skips Clear() when storage already empty (IsEmpty fast path)
- appendRelationIdDirect for O(1) append in rebuild loop (duplicates structurally impossible)
- ChangeCompoundRefsOfNodeInternal uses TryBound for single lookup
- Counts() and ActiveCounts() accessors for Counts struct construction

Lock-free cache reads:
- EdgeEntry: packed atomic<uint8_t> (GeomStatus+IsClosed+IsComputed)
- CoEdgeSameRangeEntry: packed atomic<uint8_t> (SameRange+SameParameter+Computed)
- WireEntry: packed atomic<uint8_t> (IsClosed+IsComputed)
- ShellEntry: atomic<ClosureStatus> with Invalid sentinel
- All entries use memory_order_acquire/release pairs
- ensureSize handles lazy array growth (no myPreSized)
- Enums (GeomStatus, ClosureStatus) moved inside entry structs

Per-CoEdge queries:
- SameRange/SameParameter are per-CoEdge properties, bound to CoEdge OwnGen
- Edge class no longer has SameParameter/SameRange/IsClosed coedge overloads
- CoEdge class owns SameParameter/SameRange queries
- Test files use local edgeSameParameter/edgeSameRange helpers

Lock-free storage and registry:
- BRepGraphInc_Storage: atomic dirty flags for UID reverse indexes
- BRepGraph_LayerRegistry: atomic subscription masks for lock-free dispatch
- Ensure/EnsureCache/EnsureLayer: double-checked locking with shared fast path

Other optimizations:
- Iterator Next() fast-path: check next element validity inline
- NbFaces O(K) inline dedup with NCollection_LocalArray
- GeomAdaptor_Curve/Surface/Geom2dAdaptor_Curve for cached geometry eval
- Value() calls replaced with EvalD0() for clearer naming
- TargetItem returns value instead of pointer (no null-pointer risk)
- copyTopologyDefinitionsIdentity clears Curve3DRepId on GeomPolicy::Drop
- ensureEdgeEntry concurrency: fast-path validation + selective field merge
2026-06-14 14:15:32 +01:00

1187 lines
41 KiB
C++

// Copyright (c) 2026 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <BRepGraph_ShapesView.hxx>
#include <BRepGraph_Data.hxx>
#include <BRepGraph_EditorView.hxx>
#include <BRepGraph_Iterator.hxx>
#include <BRepGraph_LayerHistory.hxx>
#include <BRepGraph_Layer.hxx>
#include <BRepGraph_LayerTopoSupplement.hxx>
#include <BRepGraph_LayerRegistry.hxx>
#include <BRepGraph_RefsIterator.hxx>
#include <BRepGraphInc_Reconstruct.hxx>
#include <BRepGraphInc_Populate.hxx>
#include <BRepGraphInc_Storage.hxx>
#include <BRepTools_History.hxx>
#include <TCollection_AsciiString.hxx>
#include <TopAbs_ShapeEnum.hxx>
#include <TopExp_Explorer.hxx>
#include <BRep_Builder.hxx>
#include <NCollection_FlatMap.hxx>
#include <NCollection_Map.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Iterator.hxx>
#include <shared_mutex>
namespace
{
struct BRepGraph_ReconstructionContext
{
BRepGraph* Graph = nullptr;
const BRepGraphInc_Storage* Storage = nullptr;
BRepGraphInc_Reconstruct::Cache Cache;
NCollection_FlatMap<BRepGraph_ProductId> ActiveProducts;
};
enum class ShapeParentRoute
{
Reject,
Core,
Supplement
};
static void assertMutationBoundary(BRepGraph& theGraph, [[maybe_unused]] const char* theContext)
{
[[maybe_unused]] const bool isValid = theGraph.Editor().ValidateMutationBoundary();
Standard_ASSERT_VOID(isValid, theContext);
}
static bool isCoreParityOrientation(const TopAbs_Orientation theOrientation)
{
return theOrientation == TopAbs_FORWARD || theOrientation == TopAbs_REVERSED;
}
static ShapeParentRoute classifyShapeToParent(const TopAbs_ShapeEnum theShapeType,
const TopAbs_Orientation theOrientation,
const BRepGraph_NodeId& theParent)
{
if (!theParent.IsValid())
{
return ShapeParentRoute::Reject;
}
switch (theParent.NodeKind)
{
case BRepGraph_NodeId::Kind::Product:
return theShapeType == TopAbs_SHAPE ? ShapeParentRoute::Reject : ShapeParentRoute::Core;
case BRepGraph_NodeId::Kind::Compound:
if (theShapeType == TopAbs_SHAPE)
{
return ShapeParentRoute::Reject;
}
return isCoreParityOrientation(theOrientation) ? ShapeParentRoute::Core
: ShapeParentRoute::Supplement;
case BRepGraph_NodeId::Kind::Solid:
if (theShapeType == TopAbs_SHAPE)
{
return ShapeParentRoute::Reject;
}
return theShapeType == TopAbs_SHELL && isCoreParityOrientation(theOrientation)
? ShapeParentRoute::Core
: ShapeParentRoute::Supplement;
case BRepGraph_NodeId::Kind::Shell:
if (theShapeType == TopAbs_SHAPE)
{
return ShapeParentRoute::Reject;
}
return theShapeType == TopAbs_FACE && isCoreParityOrientation(theOrientation)
? ShapeParentRoute::Core
: ShapeParentRoute::Supplement;
case BRepGraph_NodeId::Kind::Face:
return theShapeType == TopAbs_VERTEX ? ShapeParentRoute::Supplement
: ShapeParentRoute::Reject;
case BRepGraph_NodeId::Kind::Edge:
return theShapeType == TopAbs_VERTEX ? ShapeParentRoute::Supplement
: ShapeParentRoute::Reject;
case BRepGraph_NodeId::Kind::CompSolid:
if (theShapeType == TopAbs_SHAPE)
{
return ShapeParentRoute::Reject;
}
return theShapeType == TopAbs_SOLID && isCoreParityOrientation(theOrientation)
? ShapeParentRoute::Core
: ShapeParentRoute::Supplement;
case BRepGraph_NodeId::Kind::CoEdge:
case BRepGraph_NodeId::Kind::Wire:
case BRepGraph_NodeId::Kind::Vertex:
case BRepGraph_NodeId::Kind::Occurrence:
return ShapeParentRoute::Reject;
}
return ShapeParentRoute::Reject;
}
static uint64_t attachSupplementToParent(BRepGraph& theGraph,
const TopoDS_Shape& theShape,
const BRepGraph_NodeId theParent)
{
TopoDS_Shape aSuppShape = theShape;
aSuppShape.Orientation(theShape.Orientation());
switch (theParent.NodeKind)
{
case BRepGraph_NodeId::Kind::Compound:
return theGraph.Editor().Supplement().AttachToCompound(
BRepGraph_CompoundId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape);
case BRepGraph_NodeId::Kind::CompSolid:
return theGraph.Editor().Supplement().AttachToCompSolid(
BRepGraph_CompSolidId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape);
case BRepGraph_NodeId::Kind::Solid:
return theGraph.Editor().Supplement().AttachToSolid(
BRepGraph_SolidId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape);
case BRepGraph_NodeId::Kind::Shell:
return theGraph.Editor().Supplement().AttachToShell(
BRepGraph_ShellId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape);
case BRepGraph_NodeId::Kind::Face:
return theGraph.Editor().Supplement().AttachToFace(
BRepGraph_FaceId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex);
case BRepGraph_NodeId::Kind::Edge:
return theGraph.Editor().Supplement().AttachToEdge(
BRepGraph_EdgeId(theParent),
aSuppShape,
BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex);
default:
return 0;
}
}
} // namespace
//=================================================================================================
uint32_t BRepGraph::ShapesView::snapshotCountForKind(const BRepGraph& theGraph,
const TopAbs_ShapeEnum theShapeType)
{
const BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage;
switch (theShapeType)
{
case TopAbs_COMPOUND:
return static_cast<uint32_t>(aStorage.NbCompounds());
case TopAbs_COMPSOLID:
return static_cast<uint32_t>(aStorage.NbCompSolids());
case TopAbs_SOLID:
return static_cast<uint32_t>(aStorage.NbSolids());
case TopAbs_SHELL:
return static_cast<uint32_t>(aStorage.NbShells());
case TopAbs_FACE:
return static_cast<uint32_t>(aStorage.NbFaces());
case TopAbs_WIRE:
return static_cast<uint32_t>(aStorage.NbWires());
case TopAbs_EDGE:
return static_cast<uint32_t>(aStorage.NbEdges());
case TopAbs_VERTEX:
return static_cast<uint32_t>(aStorage.NbVertices());
default:
return 0;
}
}
//=================================================================================================
BRepGraph_NodeId BRepGraph::ShapesView::detectTopologyRoot(const BRepGraph& theGraph,
const TopAbs_ShapeEnum theShapeType,
const uint32_t theOldCountOfShapeKind)
{
const uint32_t aNewCount = snapshotCountForKind(theGraph, theShapeType);
if (aNewCount <= theOldCountOfShapeKind)
{
return BRepGraph_NodeId();
}
// BRepGraphInc_Populate::Append appends entities in declaration order, so the first entity
// appended for a given shape type is always the shape root (index == pre-append count).
// This assumption holds as long as no intermediate entities of the same type are inserted
// before the root node during population.
switch (theShapeType)
{
case TopAbs_COMPOUND:
return BRepGraph_CompoundId(theOldCountOfShapeKind);
case TopAbs_COMPSOLID:
return BRepGraph_CompSolidId(theOldCountOfShapeKind);
case TopAbs_SOLID:
return BRepGraph_SolidId(theOldCountOfShapeKind);
case TopAbs_SHELL:
return BRepGraph_ShellId(theOldCountOfShapeKind);
case TopAbs_FACE:
return BRepGraph_FaceId(theOldCountOfShapeKind);
case TopAbs_WIRE:
return BRepGraph_WireId(theOldCountOfShapeKind);
case TopAbs_EDGE:
return BRepGraph_EdgeId(theOldCountOfShapeKind);
case TopAbs_VERTEX:
return BRepGraph_VertexId(theOldCountOfShapeKind);
default:
return BRepGraph_NodeId();
}
}
void BRepGraph::ShapesView::populateUIDsIncremental(BRepGraph& theGraph,
const uint32_t theOldVtx,
const uint32_t theOldEdge,
const uint32_t theOldCoEdge,
const uint32_t theOldWire,
const uint32_t theOldFace,
const uint32_t theOldShell,
const uint32_t theOldSolid,
const uint32_t theOldComp,
const uint32_t theOldCS,
const uint32_t theOldProduct,
const uint32_t theOldOccurrence,
const uint32_t theOldShellRef,
const uint32_t theOldFaceRef,
const uint32_t theOldWireRef,
const uint32_t theOldVertexRef,
const uint32_t theOldSolidRef,
const uint32_t theOldChildRef)
{
for (BRepGraph_FullVertexIterator aVertexIt(theGraph, BRepGraph_VertexId(theOldVtx));
aVertexIt.More();
aVertexIt.Next())
{
theGraph.allocateUID(aVertexIt.CurrentId());
}
for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph, BRepGraph_EdgeId(theOldEdge)); anEdgeIt.More();
anEdgeIt.Next())
{
theGraph.allocateUID(anEdgeIt.CurrentId());
}
for (BRepGraph_FullCoEdgeIterator aCoEdgeIt(theGraph, BRepGraph_CoEdgeId(theOldCoEdge));
aCoEdgeIt.More();
aCoEdgeIt.Next())
{
theGraph.allocateUID(aCoEdgeIt.CurrentId());
}
for (BRepGraph_FullWireIterator aWireIt(theGraph, BRepGraph_WireId(theOldWire)); aWireIt.More();
aWireIt.Next())
{
theGraph.allocateUID(aWireIt.CurrentId());
}
for (BRepGraph_FullFaceIterator aFaceIt(theGraph, BRepGraph_FaceId(theOldFace)); aFaceIt.More();
aFaceIt.Next())
{
theGraph.allocateUID(aFaceIt.CurrentId());
}
for (BRepGraph_FullShellIterator aShellIt(theGraph, BRepGraph_ShellId(theOldShell));
aShellIt.More();
aShellIt.Next())
{
theGraph.allocateUID(aShellIt.CurrentId());
}
for (BRepGraph_FullSolidIterator aSolidIt(theGraph, BRepGraph_SolidId(theOldSolid));
aSolidIt.More();
aSolidIt.Next())
{
theGraph.allocateUID(aSolidIt.CurrentId());
}
for (BRepGraph_FullCompoundIterator aCompoundIt(theGraph, BRepGraph_CompoundId(theOldComp));
aCompoundIt.More();
aCompoundIt.Next())
{
theGraph.allocateUID(aCompoundIt.CurrentId());
}
for (BRepGraph_FullCompSolidIterator aCompSolidIt(theGraph, BRepGraph_CompSolidId(theOldCS));
aCompSolidIt.More();
aCompSolidIt.Next())
{
theGraph.allocateUID(aCompSolidIt.CurrentId());
}
for (BRepGraph_FullProductIterator aProductIt(theGraph, BRepGraph_ProductId(theOldProduct));
aProductIt.More();
aProductIt.Next())
{
theGraph.allocateUID(aProductIt.CurrentId());
}
for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theGraph,
BRepGraph_OccurrenceId(theOldOccurrence));
anOccurrenceIt.More();
anOccurrenceIt.Next())
{
theGraph.allocateUID(anOccurrenceIt.CurrentId());
}
for (BRepGraph_FullShellRefIterator aShellRefIt(theGraph, BRepGraph_ShellRefId(theOldShellRef));
aShellRefIt.More();
aShellRefIt.Next())
{
theGraph.allocateRefUID(aShellRefIt.CurrentId());
}
for (BRepGraph_FullFaceRefIterator aFaceRefIt(theGraph, BRepGraph_FaceRefId(theOldFaceRef));
aFaceRefIt.More();
aFaceRefIt.Next())
{
theGraph.allocateRefUID(aFaceRefIt.CurrentId());
}
for (BRepGraph_FullWireRefIterator aWireRefIt(theGraph, BRepGraph_WireRefId(theOldWireRef));
aWireRefIt.More();
aWireRefIt.Next())
{
theGraph.allocateRefUID(aWireRefIt.CurrentId());
}
for (BRepGraph_FullVertexRefIterator aVertexRefIt(theGraph,
BRepGraph_VertexRefId(theOldVertexRef));
aVertexRefIt.More();
aVertexRefIt.Next())
{
theGraph.allocateRefUID(aVertexRefIt.CurrentId());
}
for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph, BRepGraph_SolidRefId(theOldSolidRef));
aSolidRefIt.More();
aSolidRefIt.Next())
{
theGraph.allocateRefUID(aSolidRefIt.CurrentId());
}
for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph, BRepGraph_ChildRefId(theOldChildRef));
aChildRefIt.More();
aChildRefIt.Next())
{
theGraph.allocateRefUID(aChildRefIt.CurrentId());
}
}
BRepGraph::ShapesView::AddStatus BRepGraph::ShapesView::appendImpl(
BRepGraph& theGraph,
const TopoDS_Shape& theShape,
const BRepGraph::ShapesView::Options& theOptions,
NCollection_LinearVector<BRepGraph_NodeId>* theOutFlatRoots)
{
BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage;
const uint32_t anOldVtx = aStorage.NbVertices();
const uint32_t anOldEdge = aStorage.NbEdges();
const uint32_t anOldCoEdge = aStorage.NbCoEdges();
const uint32_t anOldWire = aStorage.NbWires();
const uint32_t anOldFace = aStorage.NbFaces();
const uint32_t anOldShell = aStorage.NbShells();
const uint32_t anOldSolid = aStorage.NbSolids();
const uint32_t anOldComp = aStorage.NbCompounds();
const uint32_t anOldCS = aStorage.NbCompSolids();
const uint32_t anOldProduct = aStorage.NbProducts();
const uint32_t anOldOccurrence = aStorage.NbOccurrences();
const uint32_t anOldShellRef = aStorage.NbShellRefs();
const uint32_t anOldFaceRef = aStorage.NbFaceRefs();
const uint32_t anOldWireRef = aStorage.NbWireRefs();
const uint32_t anOldVertexRef = aStorage.NbVertexRefs();
const uint32_t anOldSolidRef = aStorage.NbSolidRefs();
const uint32_t anOldChildRef = aStorage.NbChildRefs();
BRepGraphInc_Populate::BuildStatus aBuildStatus;
if (theOptions.Flatten)
{
NCollection_LinearVector<BRepGraph_NodeId> aAppendedRoots(8);
aBuildStatus = BRepGraphInc_Populate::AppendFlattened(theGraph,
theShape,
theOptions.Parallel,
aAppendedRoots,
theOptions.Populate);
if (theOutFlatRoots != nullptr)
{
for (const BRepGraph_NodeId& anId : aAppendedRoots)
{
theOutFlatRoots->Append(anId);
}
}
}
else
{
aBuildStatus =
BRepGraphInc_Populate::Append(theGraph, theShape, theOptions.Parallel, theOptions.Populate);
}
// Map internal BuildStatus to public AddStatus.
AddStatus aAddStatus = AddStatus::Failed;
switch (aBuildStatus)
{
case BRepGraphInc_Populate::BuildStatus::Failed:
aAddStatus = AddStatus::Failed;
break;
case BRepGraphInc_Populate::BuildStatus::SuccessWithWarnings:
aAddStatus = AddStatus::SuccessWithWarnings;
break;
case BRepGraphInc_Populate::BuildStatus::Success:
aAddStatus = AddStatus::Success;
break;
}
theGraph.myData->myIncStorage.ClearCurrentShapes();
populateUIDsIncremental(theGraph,
anOldVtx,
anOldEdge,
anOldCoEdge,
anOldWire,
anOldFace,
anOldShell,
anOldSolid,
anOldComp,
anOldCS,
anOldProduct,
anOldOccurrence,
anOldShellRef,
anOldFaceRef,
anOldWireRef,
anOldVertexRef,
anOldSolidRef,
anOldChildRef);
assertMutationBoundary(theGraph, "BRepGraph::ShapesView::Add: post-append mutation boundary");
return aAddStatus;
}
//=================================================================================================
void BRepGraph::ShapesView::collectAddedNodes(
const BRepGraph& theGraph,
const TopoDS_Shape& theShape,
NCollection_DataMap<TopoDS_Shape, BRepGraph_NodeId, TopTools_ShapeMapHasher>& theMap)
{
if (theShape.IsNull())
{
return;
}
const BRepGraph::ShapesView& aShapes = theGraph.Shapes();
// Helper: record (shape -> NodeId) when the shape resolves to a node.
auto bind = [&](const TopoDS_Shape& aSub) {
if (aSub.IsNull() || theMap.IsBound(aSub))
{
return;
}
const BRepGraph_NodeId aNode = aShapes.FindNode(aSub);
if (aNode.IsValid())
{
theMap.Bind(aSub, aNode);
}
};
// Bind the root then enumerate every subshape kind.
bind(theShape);
static const TopAbs_ShapeEnum aKinds[] = {TopAbs_COMPOUND,
TopAbs_COMPSOLID,
TopAbs_SOLID,
TopAbs_SHELL,
TopAbs_FACE,
TopAbs_WIRE,
TopAbs_EDGE,
TopAbs_VERTEX};
for (const TopAbs_ShapeEnum aKind : aKinds)
{
for (TopExp_Explorer anExp(theShape, aKind); anExp.More(); anExp.Next())
{
bind(anExp.Current());
}
}
}
//=================================================================================================
namespace
{
static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& theContext,
const BRepGraph_ProductId theProduct);
static TopoDS_Shape reconstructOccurrenceLocal(BRepGraph_ReconstructionContext& theContext,
const BRepGraph_OccurrenceId theOccurrence,
const TopLoc_Location& theLocalLocation)
{
const BRepGraphInc_Storage& aStorage = *theContext.Storage;
if (!theOccurrence.IsValid(aStorage.NbOccurrences()))
{
return TopoDS_Shape();
}
const BRepGraphInc::OccurrenceDef& anOccurrence = aStorage.Occurrence(theOccurrence);
if (aStorage.IsRemoved(theOccurrence) || !anOccurrence.ChildNodeId.IsValid())
{
return TopoDS_Shape();
}
TopoDS_Shape aShape;
if (anOccurrence.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product)
{
aShape = reconstructProductLocal(theContext, BRepGraph_ProductId(anOccurrence.ChildNodeId));
}
else
{
aShape =
BRepGraphInc_Reconstruct::Node(*theContext.Graph, anOccurrence.ChildNodeId, theContext.Cache);
}
if (!aShape.IsNull() && !theLocalLocation.IsIdentity())
{
aShape.Move(theLocalLocation);
}
return aShape;
}
static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& theContext,
const BRepGraph_ProductId theProduct)
{
const BRepGraphInc_Storage& aStorage = *theContext.Storage;
if (!theProduct.IsValid(aStorage.NbProducts()))
{
return TopoDS_Shape();
}
const BRepGraph_NodeId aProductNode = theProduct;
if (const TopoDS_Shape* aCached = theContext.Cache.Seek(aProductNode))
{
return *aCached;
}
if (aStorage.IsRemoved(theProduct) || theContext.ActiveProducts.Contains(theProduct))
{
return TopoDS_Shape();
}
theContext.ActiveProducts.Add(theProduct);
// Find the shape root: scan occurrence refs for a topology (non-product) child.
TopoDS_Shape aResult;
BRepGraph_NodeId aShapeRootNode;
TopLoc_Location aRootLocation;
for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*theContext.Graph, theProduct); anOccIt.More();
anOccIt.Next())
{
const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(anOccIt.CurrentId());
const BRepGraphInc::OccurrenceDef& aDef = aStorage.Occurrence(aRef.ChildOccurrenceId);
if (aDef.ChildNodeId.IsValid() && aDef.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product)
{
aShapeRootNode = aDef.ChildNodeId;
aRootLocation = aRef.LocalLocation;
break;
}
}
if (aShapeRootNode.IsValid())
{
aResult = BRepGraphInc_Reconstruct::Node(*theContext.Graph, aShapeRootNode, theContext.Cache);
if (!aResult.IsNull() && !aRootLocation.IsIdentity())
{
aResult.Move(aRootLocation);
}
}
else
{
BRep_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*theContext.Graph, theProduct); anOccIt.More();
anOccIt.Next())
{
const BRepGraphInc::OccurrenceRef& anOccurrenceRef =
aStorage.OccurrenceRef(anOccIt.CurrentId());
if (!anOccurrenceRef.ChildOccurrenceId.IsValid(aStorage.NbOccurrences()))
{
continue;
}
if (aStorage.IsRemoved(anOccurrenceRef.ChildOccurrenceId))
{
continue;
}
TopoDS_Shape aChild = reconstructOccurrenceLocal(theContext,
anOccurrenceRef.ChildOccurrenceId,
anOccurrenceRef.LocalLocation);
if (!aChild.IsNull())
{
aBuilder.Add(aCompound, aChild);
}
}
aResult = aCompound;
}
theContext.ActiveProducts.Remove(theProduct);
if (!aResult.IsNull())
{
theContext.Cache.Bind(aProductNode, aResult);
}
return aResult;
}
static TopoDS_Shape reconstructShape(BRepGraph_ReconstructionContext& theContext,
const BRepGraph_NodeId theNode)
{
if (!theNode.IsValid())
{
return TopoDS_Shape();
}
const BRepGraphInc_Storage& aStorage = *theContext.Storage;
switch (theNode.NodeKind)
{
case BRepGraph_NodeId::Kind::Product:
return reconstructProductLocal(theContext, BRepGraph_ProductId(theNode));
case BRepGraph_NodeId::Kind::Occurrence: {
const BRepGraph_OccurrenceId anOccurrence(theNode);
if (!anOccurrence.IsValid(aStorage.NbOccurrences()))
{
return TopoDS_Shape();
}
const BRepGraphInc::OccurrenceDef& anOccurrenceDef = aStorage.Occurrence(anOccurrence);
if (aStorage.IsRemoved(anOccurrence) || !anOccurrenceDef.ChildNodeId.IsValid())
{
return TopoDS_Shape();
}
TopoDS_Shape aShape;
if (anOccurrenceDef.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product)
{
aShape =
reconstructProductLocal(theContext, BRepGraph_ProductId(anOccurrenceDef.ChildNodeId));
}
else
{
aShape = BRepGraphInc_Reconstruct::Node(*theContext.Graph,
anOccurrenceDef.ChildNodeId,
theContext.Cache);
}
if (!aShape.IsNull())
{
const TopLoc_Location aGlobalLocation =
theContext.Graph->Topo().Occurrences().OccurrenceLocation(anOccurrence);
if (!aGlobalLocation.IsIdentity())
{
aShape.Move(aGlobalLocation);
}
}
return aShape;
}
default:
return BRepGraphInc_Reconstruct::Node(*theContext.Graph, theNode, theContext.Cache);
}
}
static BRepGraph_ReconstructionContext makeReconstructionContext(
BRepGraph& theGraph,
const BRepGraphInc_Storage& theStorage)
{
BRepGraph_ReconstructionContext aContext;
aContext.Graph = &theGraph;
aContext.Storage = &theStorage;
return aContext;
}
} // namespace
//=================================================================================================
void BRepGraph::ShapesView::CollectHistoryInputs(
const NCollection_Array1<BRepGraph_NodeId>& theRoots,
NCollection_DataMap<TopoDS_Shape, BRepGraph_NodeId, TopTools_ShapeMapHasher>& theOutInputs) const
{
const BRepGraph& theGraph = *myGraph;
for (const BRepGraph_NodeId& aRootId : theRoots)
{
const TopoDS_Shape aRoot = theGraph.Shapes().Shape(aRootId);
collectAddedNodes(theGraph, aRoot, theOutInputs);
}
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory(
const TopoDS_Shape& theResultShape,
const NCollection_DataMap<TopoDS_Shape, BRepGraph_NodeId, TopTools_ShapeMapHasher>& theInputs,
const occ::handle<BRepTools_History>& theHistory,
const TCollection_AsciiString& theOpLabel)
{
Options anOptions;
anOptions.CreateAutoProduct = false;
return AddWithHistory(theResultShape, theInputs, theHistory, theOpLabel, anOptions);
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory(
const TopoDS_Shape& theResultShape,
const NCollection_DataMap<TopoDS_Shape, BRepGraph_NodeId, TopTools_ShapeMapHasher>& theInputs,
const occ::handle<BRepTools_History>& theHistory,
const TCollection_AsciiString& theOpLabel,
const BRepGraph::ShapesView::Options& theOptions)
{
BRepGraph& theGraph = *myGraph;
BRepGraph_LayerHistory& aHistory = *theGraph.LayerRegistry().Ensure<BRepGraph_LayerHistory>();
const bool shouldAbsorbHistory = aHistory.IsEnabled() && !theHistory.IsNull();
Options aBuildOptions = theOptions;
aBuildOptions.TrackAddedNodes = theOptions.TrackAddedNodes || shouldAbsorbHistory;
Result aResult = Add(theResultShape, aBuildOptions);
if (aResult.IsOk() && shouldAbsorbHistory)
{
aHistory.Absorb(theInputs, aResult.AddedNodes, theHistory, theOpLabel);
}
return aResult;
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory(
const TopoDS_Shape& theResultShape,
const NCollection_Array1<BRepGraph_NodeId>& theInputRoots,
const occ::handle<BRepTools_History>& theHistory,
const TCollection_AsciiString& theOpLabel)
{
Options anOptions;
anOptions.CreateAutoProduct = false;
return AddWithHistory(theResultShape, theInputRoots, theHistory, theOpLabel, anOptions);
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory(
const TopoDS_Shape& theResultShape,
const NCollection_Array1<BRepGraph_NodeId>& theInputRoots,
const occ::handle<BRepTools_History>& theHistory,
const TCollection_AsciiString& theOpLabel,
const BRepGraph::ShapesView::Options& theOptions)
{
NCollection_DataMap<TopoDS_Shape, BRepGraph_NodeId, TopTools_ShapeMapHasher> anInputs;
CollectHistoryInputs(theInputRoots, anInputs);
return AddWithHistory(theResultShape, anInputs, theHistory, theOpLabel, theOptions);
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(const TopoDS_Shape& theShape)
{
return Add(theShape, Options{});
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(
const TopoDS_Shape& theShape,
const BRepGraph::ShapesView::Options& theOptions)
{
BRepGraph& theGraph = *myGraph;
Result aResult;
if (theShape.IsNull())
{
return aResult;
}
const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType());
NCollection_LinearVector<BRepGraph_NodeId> aFlatRoots;
const AddStatus aAddStatus =
appendImpl(theGraph, theShape, theOptions, theOptions.Flatten ? &aFlatRoots : nullptr);
aResult.Status = aAddStatus;
if (theOptions.Flatten && !aFlatRoots.IsEmpty())
{
aResult.TopologyRoot = aFlatRoots.First();
}
else
{
aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount);
}
if (theOptions.CreateAutoProduct && aResult.TopologyRoot.IsValid())
{
aResult.Product = theGraph.Editor().Products().Add(aResult.TopologyRoot, theShape.Location());
theGraph.Editor().Products().AppendDocumentRoot(aResult.Product);
if (aResult.Product.IsValid())
{
const BRepGraphInc::ProductRelations& aProductRel =
theGraph.myData->myIncStorage.ProductRelations(aResult.Product);
if (!aProductRel.OccurrenceRefIds.IsEmpty())
{
const BRepGraph_OccurrenceRefId anOccRefId = aProductRel.OccurrenceRefIds.First();
const BRepGraph_OccurrenceId anOccId =
theGraph.myData->myIncStorage.OccurrenceRef(anOccRefId).ChildOccurrenceId;
aResult.Occurrence = anOccId;
}
}
}
if (!aResult.TopologyRoot.IsValid()
&& !(theOptions.CreateAutoProduct && aResult.Product.IsValid()))
{
aResult.Status = AddStatus::Failed;
}
if (theOptions.TrackAddedNodes && aResult.TopologyRoot.IsValid())
{
collectAddedNodes(theGraph, theShape, aResult.AddedNodes);
}
return aResult;
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(const TopoDS_Shape& theShape,
const BRepGraph_NodeId theParent)
{
return Add(theShape, theParent, Options{});
}
//=================================================================================================
BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(
const TopoDS_Shape& theShape,
const BRepGraph_NodeId theParent,
const BRepGraph::ShapesView::Options& theOptions)
{
BRepGraph& theGraph = *myGraph;
Result aResult;
if (theShape.IsNull() || !theParent.IsValid())
{
return aResult;
}
const ShapeParentRoute aRoute =
classifyShapeToParent(theShape.ShapeType(), theShape.Orientation(), theParent);
if (aRoute == ShapeParentRoute::Reject)
{
return aResult;
}
if (aRoute == ShapeParentRoute::Supplement)
{
if (attachSupplementToParent(theGraph, theShape, theParent) != 0)
{
aResult.Status = AddStatus::Success;
}
return aResult;
}
const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType());
Options anInner = theOptions;
anInner.CreateAutoProduct = false;
NCollection_LinearVector<BRepGraph_NodeId> aFlatRoots;
const AddStatus aStatus =
appendImpl(theGraph, theShape, anInner, anInner.Flatten ? &aFlatRoots : nullptr);
if (aStatus == AddStatus::Failed)
{
return aResult;
}
if (anInner.Flatten && !aFlatRoots.IsEmpty())
{
aResult.TopologyRoot = aFlatRoots.First();
}
else
{
aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount);
}
if (!aResult.TopologyRoot.IsValid())
{
return aResult;
}
if (anInner.TrackAddedNodes)
{
collectAddedNodes(theGraph, theShape, aResult.AddedNodes);
}
switch (theParent.NodeKind)
{
case BRepGraph_NodeId::Kind::Product: {
const BRepGraph_ProductId aChildProduct =
theGraph.Editor().Products().Add(aResult.TopologyRoot, TopLoc_Location());
if (!aChildProduct.IsValid())
{
return aResult;
}
BRepGraph_OccurrenceRefId anOccRefId;
const BRepGraph_OccurrenceId anOccId =
theGraph.Editor().Products().Append(BRepGraph_ProductId(theParent),
aChildProduct,
theShape.Location(),
BRepGraph_OccurrenceId(),
&anOccRefId);
if (!anOccId.IsValid())
{
return aResult;
}
aResult.Product = aChildProduct;
aResult.Occurrence = anOccId;
aResult.InsertedRef = anOccRefId;
aResult.Status = AddStatus::Success;
return aResult;
}
case BRepGraph_NodeId::Kind::Compound: {
const TopAbs_Orientation aShapeOri = theShape.Orientation();
const BRepGraph_ChildRefId aRid =
theGraph.Editor().Compounds().Append(BRepGraph_CompoundId(theParent),
aResult.TopologyRoot,
aShapeOri);
if (!aRid.IsValid())
{
return aResult;
}
aResult.InsertedRef = aRid;
aResult.Status = AddStatus::Success;
return aResult;
}
case BRepGraph_NodeId::Kind::Shell: {
const TopAbs_Orientation aShapeOri = theShape.Orientation();
const BRepGraph_ShellId aShell(theParent);
const BRepGraph_FaceRefId aRid =
theGraph.Editor().Shells().Append(aShell,
BRepGraph_FaceId(aResult.TopologyRoot),
aShapeOri);
if (!aRid.IsValid())
{
return aResult;
}
aResult.InsertedRef = aRid;
aResult.Status = AddStatus::Success;
return aResult;
}
case BRepGraph_NodeId::Kind::Solid: {
const BRepGraph_SolidId aSolid(theParent);
if (aResult.TopologyRoot.NodeKind == BRepGraph_NodeId::Kind::Shell)
{
const TopAbs_Orientation aShapeOri = theShape.Orientation();
const BRepGraph_ShellRefId aRid =
theGraph.Editor().Solids().Append(aSolid,
BRepGraph_ShellId(aResult.TopologyRoot),
aShapeOri);
if (!aRid.IsValid())
{
return aResult;
}
aResult.InsertedRef = aRid;
aResult.Status = AddStatus::Success;
return aResult;
}
return aResult;
}
case BRepGraph_NodeId::Kind::CompSolid: {
if (aResult.TopologyRoot.NodeKind != BRepGraph_NodeId::Kind::Solid)
{
return aResult;
}
const TopAbs_Orientation aShapeOri = theShape.Orientation();
const BRepGraph_SolidRefId aRid =
theGraph.Editor().CompSolids().Append(BRepGraph_CompSolidId(theParent),
BRepGraph_SolidId(aResult.TopologyRoot),
aShapeOri);
if (!aRid.IsValid())
{
return aResult;
}
aResult.InsertedRef = aRid;
aResult.Status = AddStatus::Success;
return aResult;
}
default:
return aResult;
}
}
//=================================================================================================
void BRepGraph::ShapesView::ClearCached(const BRepGraph_NodeId theNode)
{
if (myGraph == nullptr || !theNode.IsValid() || myGraph->Topo().Gen().IsRemoved(theNode))
{
return;
}
myGraph->myData->myIncStorage.UnbindCurrentShape(theNode);
}
//=================================================================================================
void BRepGraph::ShapesView::ClearCached(const BRepGraph_RefId theRef)
{
if (myGraph == nullptr || !theRef.IsValid() || myGraph->Refs().Gen().IsRemoved(theRef))
{
return;
}
const BRepGraph_NodeId aChildNode = myGraph->Refs().Gen().ChildNode(theRef);
ClearCached(aChildNode);
}
//=================================================================================================
TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const
{
if (!theNode.IsValid())
{
return TopoDS_Shape();
}
if (theNode.IsRemoved(*myGraph))
{
return TopoDS_Shape();
}
// Fast path: if entity was never mutated, return the original shape.
const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode);
if (aDef != nullptr && aDef->SubtreeGen == 0)
{
TopoDS_Shape anOrig = Original(theNode);
if (!anOrig.IsNull())
{
return anOrig;
}
}
// Check mutable cache under shared lock with SubtreeGen validation.
{
std::shared_lock<std::shared_mutex> aReadLock(
myGraph->myData->myIncStorage.CurrentShapesMutex());
const BRepGraphInc_Storage::CachedShape* aCached =
myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode);
if (aCached != nullptr && aDef != nullptr && aCached->StoredSubtreeGen == aDef->SubtreeGen)
{
return aCached->Shape;
}
}
// Reconstruct from incidence storage / assembly facade.
BRepGraph_ReconstructionContext aContext =
makeReconstructionContext(*myGraph, myGraph->myData->myIncStorage);
TopoDS_Shape aReconstructed = reconstructShape(aContext, theNode);
// Store under exclusive lock with double-check to avoid redundant writes
// when multiple threads reconstruct the same parent node concurrently.
if (!aReconstructed.IsNull() && aDef != nullptr)
{
std::unique_lock<std::shared_mutex> aWriteLock(
myGraph->myData->myIncStorage.CurrentShapesMutex());
const BRepGraphInc_Storage::CachedShape* aExisting =
myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode);
if (aExisting != nullptr && aExisting->StoredSubtreeGen == aDef->SubtreeGen)
{
return aExisting->Shape;
}
BRepGraphInc_Storage::CachedShape anEntry;
anEntry.Shape = aReconstructed;
anEntry.StoredSubtreeGen = aDef->SubtreeGen;
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);
}
}
return aReconstructed;
}
//=================================================================================================
bool BRepGraph::ShapesView::HasOriginal(const BRepGraph_NodeId theNode) const
{
return !Original(theNode).IsNull();
}
//=================================================================================================
TopoDS_Shape BRepGraph::ShapesView::Original(const BRepGraph_NodeId theNode) const
{
if (!theNode.IsValid())
{
return TopoDS_Shape();
}
if (theNode.IsRemoved(*myGraph))
{
return TopoDS_Shape();
}
const TopoDS_Shape* anOriginal = myGraph->myData->myIncStorage.FindOriginal(theNode);
return anOriginal != nullptr ? *anOriginal : TopoDS_Shape();
}
//=================================================================================================
TopoDS_Shape BRepGraph::ShapesView::Reconstruct(const BRepGraph_NodeId theRoot) const
{
if (theRoot.IsRemoved(*myGraph))
{
return TopoDS_Shape();
}
BRepGraph_ReconstructionContext aContext =
makeReconstructionContext(*myGraph, myGraph->myData->myIncStorage);
TopoDS_Shape aResult = reconstructShape(aContext, theRoot);
return aResult;
}
//=================================================================================================
//=================================================================================================
BRepGraph_NodeId BRepGraph::ShapesView::FindNode(const TopoDS_Shape& theShape) const
{
if (theShape.IsNull())
{
return BRepGraph_NodeId();
}
const BRepGraph_NodeId* aNodeId =
myGraph->myData->myIncStorage.FindNodeByTShape(theShape.TShape().get());
if (aNodeId != nullptr)
{
if ((*aNodeId).IsRemoved(*myGraph))
{
return BRepGraph_NodeId();
}
const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(*aNodeId);
if (aDef == nullptr)
{
return BRepGraph_NodeId();
}
return *aNodeId;
}
return BRepGraph_NodeId();
}
//=================================================================================================
bool BRepGraph::ShapesView::HasNode(const TopoDS_Shape& theShape) const
{
if (theShape.IsNull())
{
return false;
}
return FindNode(theShape).IsValid();
}
//=================================================================================================
bool BRepGraph::ShapesView::RemoveShape(const TopoDS_Shape& theShape)
{
const BRepGraph_NodeId aNode = FindNode(theShape);
if (!aNode.IsValid())
{
return false;
}
myGraph->Editor().Gen().RemoveNode(aNode);
return true;
}