TopologyGraph
TopologyGraph is OCCTSwift’s graph-based view of B-Rep topology, wrapping OCCT’s BRepGraph package. It indexes all faces, edges, vertices, wires, shells, solids, coedges, and compounds of a Shape as flat integer-indexed entity vectors with O(1) cross-references, enabling cache-friendly traversal, fast adjacency queries, and parallel geometry extraction. Obtain one via TopologyGraph(shape:).
TopologyGraph is large — documented across several pages. This is the core (construction, counts, topology queries, explorers, validate/compact/deduplicate, statistics, geometry readback); see the other TopologyGraph — … pages for topology detail/history/mesh, builders & editor mutation, editor geometry/sampling/durable-identity, and attributes/snapshots/references.
Topics
- Lifecycle · Topology Counts · Geometry Counts · Face Queries · Edge Queries · Vertex Queries · Explorers · Node Status · Root Nodes · Validate · Compact · Deduplicate · Statistics · Shape Reconstruction · Vertex Geometry · Edge Geometry · Face Geometry · Wire Queries
Lifecycle
attributes
Per-node attribute store for arbitrary typed metadata keyed by NodeRef.
public var attributes = NodeAttributeStore()
Holds fit residuals, provenance, mesh-region sets, and other sidecar data. Pure Swift; never touches the underlying C++ graph. Serialized via snapshot() / init(snapshot:).
init?(shape:parallel:)
Build a topology graph from a shape.
public init?(shape: Shape, parallel: Bool = false)
Ingests the shape’s full B-Rep topology into an indexed graph. Pass parallel: true to build using multi-threaded traversal (faster for large shapes; not safe to call concurrently with other graph ops).
- Parameters:
shape— the shape to analyze;parallel— whether to use parallel construction (default:false). - Returns: A fully built
TopologyGraph, ornilif ingestion fails. - OCCT:
BRepGraph::ShapesView::Add(shape, opts)withopts.Parallelset accordingly. - Example:
if let box = Shape.box(width: 10, height: 10, depth: 10), let graph = TopologyGraph(shape: box) { print(graph.faceCount) // 6 print(graph.edgeCount) // 12 print(graph.vertexCount) // 8 }
deinit
Releases the underlying OCCT graph handle.
deinit
- OCCT:
OCCTBRepGraphRelease→deletetheOCCTBRepGraphC++ struct.
Topology Counts
nodeCount
Total number of nodes in the graph (all entity kinds).
public var nodeCount: Int { get }
- OCCT:
BRepGraph::TopoView::Gen().NbNodes().
faceCount
Number of faces.
public var faceCount: Int { get }
- OCCT:
BRepGraph::TopoView::Faces().Nb().
activeFaceCount
Number of active (non-removed) faces.
public var activeFaceCount: Int { get }
Removed faces (soft-deleted by editor operations) are excluded. See compact() to permanently purge them.
- OCCT:
BRepGraph::TopoView::Faces().NbActive().
edgeCount
Number of edges.
public var edgeCount: Int { get }
- OCCT:
BRepGraph::TopoView::Edges().Nb().
activeEdgeCount
Number of active (non-removed) edges.
public var activeEdgeCount: Int { get }
- OCCT:
BRepGraph::TopoView::Edges().NbActive().
vertexCount
Number of vertices.
public var vertexCount: Int { get }
- OCCT:
BRepGraph::TopoView::Vertices().Nb().
activeVertexCount
Number of active (non-removed) vertices.
public var activeVertexCount: Int { get }
- OCCT:
BRepGraph::TopoView::Vertices().NbActive().
wireCount
Number of wires.
public var wireCount: Int { get }
- OCCT:
BRepGraph::TopoView::Wires().Nb().
shellCount
Number of shells.
public var shellCount: Int { get }
- OCCT:
BRepGraph::TopoView::Shells().Nb().
solidCount
Number of solids.
public var solidCount: Int { get }
- OCCT:
BRepGraph::TopoView::Solids().Nb().
coedgeCount
Number of coedges (half-edges).
public var coedgeCount: Int { get }
Each coedge is the directed use of an edge within a specific wire/face context; a manifold edge has exactly two coedges.
- OCCT:
BRepGraph::TopoView::CoEdges().Nb().
compoundCount
Number of compounds.
public var compoundCount: Int { get }
- OCCT:
BRepGraph::TopoView::Compounds().Nb().
Geometry Counts
surfaceCount
Number of distinct surfaces.
public var surfaceCount: Int { get }
Counts unique surface definitions after deduplication (if deduplicate() has been called, duplicates are collapsed to one canonical surface).
- OCCT:
BRepGraph::TopoView::Geometry().NbFaceSurfaces().
curve3DCount
Number of distinct 3D curves.
public var curve3DCount: Int { get }
- OCCT:
BRepGraph::TopoView::Geometry().NbEdgeCurves3D().
curve2DCount
Number of distinct 2D curves (pcurves).
public var curve2DCount: Int { get }
- OCCT:
BRepGraph::TopoView::Geometry().NbCoEdgeCurves2D().
Face Queries
adjacentFaces(of:)
Indices of faces adjacent to a given face (sharing an edge).
public func adjacentFaces(of faceIndex: Int) -> [Int]
- Parameters:
faceIndex— zero-based face index. - Returns: Array of face indices that share at least one edge with the given face.
- OCCT: Derived from
BRepGraph_FacesOfEdgeiteration over all edges — two faces are adjacent iff they share an edge. - Example:
if let box = Shape.box(width: 10, height: 10, depth: 10), let graph = TopologyGraph(shape: box) { let neighbors = graph.adjacentFaces(of: 0) // neighbors has 4 entries for a box face }
sharedEdges(between:and:)
Indices of edges shared between two faces.
public func sharedEdges(between faceA: Int, and faceB: Int) -> [Int]
- Parameters:
faceA,faceB— zero-based face indices. - Returns: Edge indices belonging to both faces.
- OCCT: Derived from
BRepGraph_FacesOfEdgeiteration. - Example:
let shared = graph.sharedEdges(between: 0, and: 1)
outerWire(of:)
Index of the outer wire of a face.
public func outerWire(of faceIndex: Int) -> Int
- Parameters:
faceIndex— zero-based face index. - Returns: Wire index of the outer (boundary) wire, or
-1if not found. - OCCT:
BRepGraph_Tool::Face::OuterWire().
Edge Queries
faceCount(of:)
Number of faces an edge belongs to.
public func faceCount(of edgeIndex: Int) -> Int
- Parameters:
edgeIndex— zero-based edge index. - Returns: 1 for boundary edges, 2 for manifold edges, ≥3 for non-manifold.
- OCCT:
BRepGraph::TopoView::Edges().NbFaces().
faces(of:)
Indices of faces an edge belongs to.
public func faces(of edgeIndex: Int) -> [Int]
- Parameters:
edgeIndex— zero-based edge index. - Returns: Array of face indices that own this edge.
- OCCT:
BRepGraph_FacesOfEdgeiterator.
isBoundaryEdge(_:)
Whether an edge is a boundary edge (belongs to only one face).
public func isBoundaryEdge(_ edgeIndex: Int) -> Bool
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::Edge::IsBoundary().
isManifoldEdge(_:)
Whether an edge is manifold (belongs to exactly two faces).
public func isManifoldEdge(_ edgeIndex: Int) -> Bool
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::Edge::IsManifold().
adjacentEdges(of:)
Indices of edges adjacent to a given edge (sharing a vertex).
public func adjacentEdges(of edgeIndex: Int) -> [Int]
- Parameters:
edgeIndex— zero-based edge index. - Returns: Edge indices that share at least one vertex with the given edge.
- OCCT: Derived from
BRepGraph::TopoView::Vertices().Edges()incidence.
Vertex Queries
edges(of:)
Indices of edges connected to a vertex.
public func edges(of vertexIndex: Int) -> [Int]
- Parameters:
vertexIndex— zero-based vertex index. - Returns: Edge indices incident on this vertex.
- OCCT:
BRepGraph::TopoView::Vertices().Edges(). - Example:
let edgesAtCorner = graph.edges(of: 0) // 3 edges for a box corner vertex
Explorers
NodeKind
Node kind enumeration matching BRepGraph_NodeId::Kind.
public enum NodeKind: Int32, Sendable, Codable {
case solid = 0
case shell = 1
case face = 2
case wire = 3
case edge = 4
case vertex = 5
case compound = 6
case compSolid = 7
case coedge = 8
case product = 10
case occurrence = 11
}
Topology kinds 0–5 are the core B-Rep hierarchy; 6/7 are containers; 8 is the face-context coedge entity. Assembly kinds (product, occurrence) start at 10; slot 9 is reserved. Matches BRepGraph_NodeId::Kind raw values.
childCount(rootKind:rootIndex:targetKind:)
Count descendant nodes of a given kind from a root node.
public func childCount(rootKind: NodeKind, rootIndex: Int, targetKind: NodeKind) -> Int
Traverses the topology hierarchy downward from the specified root node and counts all reachable nodes of targetKind.
- Parameters:
rootKind— kind of the starting node;rootIndex— its zero-based index;targetKind— kind to count. - Returns: Number of reachable descendant nodes.
- OCCT:
BRepGraph_ChildExplorer(graph, root, targetKind). - Example:
// How many faces does solid 0 contain? let faceCount = graph.childCount(rootKind: .solid, rootIndex: 0, targetKind: .face)
parentCount(nodeKind:nodeIndex:)
Count parent nodes of a given node.
public func parentCount(nodeKind: NodeKind, nodeIndex: Int) -> Int
Traverses upward in the topology hierarchy and counts all parent nodes of the given node.
- Parameters:
nodeKind— kind of the node;nodeIndex— its zero-based index. - Returns: Number of parent nodes.
- OCCT:
BRepGraph_ParentExplorer(graph, node).
Node Status
isRemoved(nodeKind:nodeIndex:)
Check if a node has been soft-removed.
public func isRemoved(nodeKind: NodeKind, nodeIndex: Int) -> Bool
Soft-removed nodes are logically deleted but remain in the flat index until compact() is called. Active counts (activeFaceCount, etc.) exclude them.
- Parameters:
nodeKind— kind of the node;nodeIndex— its zero-based index. - OCCT:
BRepGraph::TopoView::Gen().IsRemoved(nid).
Root Nodes
RootNode
Root node as a (kind, index) pair.
public struct RootNode: Sendable {
public let kind: NodeKind
public let index: Int
}
Returned by rootNodes to identify the top-level product or topology entries of the graph.
rootNodes
Root nodes of the graph.
public var rootNodes: [RootNode] { get }
For a shape built without assembly context (CreateAutoProduct: false), root nodes reflect the top-level topology of the original shape (solids, shells, or compounds). Each element carries both a NodeKind and its zero-based index.
- OCCT:
BRepGraph::RootProductIds(). - Example:
for root in graph.rootNodes { print("\(root.kind) at index \(root.index)") }
Validate
ValidationResult
Validation result returned by validate().
public struct ValidationResult: Sendable {
public let isValid: Bool
public let errorCount: Int
public let warningCount: Int
}
isValid—trueif the graph has no structural errors.errorCount— number of error-severity issues.warningCount— number of warning-severity issues.
validate()
Validate the graph structure, returning detailed counts.
public func validate() -> ValidationResult
Runs BRepGraph_Validate::Perform and reports errors and warnings. Use when you need issue counts; use isValid for a fast boolean check.
- Returns:
ValidationResultwith error and warning counts. - OCCT:
BRepGraph_Validate::Perform(graph)withNbIssues(Severity::Error/Warning). - Example:
let result = graph.validate() if !result.isValid { print("\(result.errorCount) errors, \(result.warningCount) warnings") }
isValid
Whether the graph is structurally valid.
public var isValid: Bool { get }
Fast boolean validity check. Prefer validate() when you need error/warning counts.
- OCCT:
BRepGraph_Validate::Perform(graph).IsValid().
Compact
CompactResult
Compaction result returned by compact().
public struct CompactResult: Sendable {
public let removedVertices: Int
public let removedEdges: Int
public let removedFaces: Int
public let nodesAfter: Int
}
removedVertices/Edges/Faces— count of nodes purged per entity type.nodesAfter— total node count after compaction.
compact()
Compact the graph by permanently removing all soft-deleted nodes.
@discardableResult
public func compact() -> CompactResult
After editor operations soft-remove nodes, call compact() to reclaim memory and renumber indices. Note: any previously captured indices are invalid after compaction.
- Returns:
CompactResultdescribing what was removed. - OCCT:
BRepGraph_Compact::Perform(graph). - Example:
let result = graph.compact() print("Removed \(result.removedFaces) faces; \(result.nodesAfter) nodes remain")
Deduplicate
DeduplicateResult
Deduplication result returned by deduplicate().
public struct DeduplicateResult: Sendable {
public let canonicalSurfaces: Int
public let canonicalCurves: Int
public let surfaceRewrites: Int
public let curveRewrites: Int
}
canonicalSurfaces/Curves— number of unique geometry handles retained.surfaceRewrites/curveRewrites— number of face/edge geometry references updated to point at the canonical handle.
deduplicate()
Deduplicate shared geometry in the graph.
@discardableResult
public func deduplicate() -> DeduplicateResult
Finds surfaces and curves that are geometrically identical and collapses them to a single canonical handle, reducing memory use and enabling same-domain face detection.
- Returns:
DeduplicateResultdescribing the canonicalization. - OCCT:
BRepGraph_Deduplicate::Perform(graph). - Example:
let dedup = graph.deduplicate() print("\(dedup.surfaceRewrites) surface refs collapsed to \(dedup.canonicalSurfaces) unique")
Statistics
Stats
Comprehensive graph statistics.
public struct Stats: Sendable, CustomStringConvertible {
public let solids: Int
public let shells: Int
public let faces: Int
public let wires: Int
public let edges: Int
public let vertices: Int
public let coedges: Int
public let compounds: Int
public let totalNodes: Int
public let surfaces: Int
public let curves3D: Int
public let curves2D: Int
public var description: String { get }
}
All counts in one allocation. description formats the struct as a readable summary string.
stats
Get comprehensive graph statistics.
public var stats: Stats { get }
Reads all topology and geometry counts in a single call.
- OCCT:
BRepGraph::TopoViewfields —Solids/Shells/Faces/Wires/Edges/Vertices/CoEdges/Compounds().Nb(),Gen().NbNodes(),Geometry().NbFaceSurfaces/NbEdgeCurves3D/NbCoEdgeCurves2D(). - Example:
if let box = Shape.box(width: 10, height: 10, depth: 10), let graph = TopologyGraph(shape: box) { print(graph.stats) // TopologyGraph.Stats(solids: 1, shells: 1, faces: 6, wires: 6, edges: 12, vertices: 8, ...) }
Shape Reconstruction
shape(nodeKind:nodeIndex:)
Reconstruct a Shape from a graph node.
public func shape(nodeKind: NodeKind, nodeIndex: Int) -> Shape?
Returns the TopoDS_Shape stored for the specified node, reconstructed as an OCCTSwift Shape.
- Parameters:
nodeKind— entity kind;nodeIndex— zero-based index. - Returns: Reconstructed
Shape, ornilif the node is invalid or null. - OCCT:
BRepGraph::ShapesView::Shape(nid). - Example:
if let faceShape = graph.shape(nodeKind: .face, nodeIndex: 0) { // faceShape is a TopoDS_Face wrapped as a Shape }
findNode(for:)
Find the node (kind, index) for a shape.
public func findNode(for shape: Shape) -> (kind: NodeKind, index: Int)?
Looks up the graph node that corresponds to the given Shape. Useful for round-tripping from a Shape to its graph index.
- Parameters:
shape— the shape to look up. - Returns: A
(kind, index)tuple if the shape is known to the graph,nilotherwise. - OCCT:
BRepGraph::ShapesView::FindNode(shape).
hasNode(for:)
Check if a shape is known to the graph.
public func hasNode(for shape: Shape) -> Bool
- Parameters:
shape— the shape to check. - OCCT:
BRepGraph::ShapesView::HasNode(shape).
Vertex Geometry
vertexPoint(_:)
Get the 3D point of a vertex.
public func vertexPoint(_ vertexIndex: Int) -> (x: Double, y: Double, z: Double)
- Parameters:
vertexIndex— zero-based vertex index. - Returns: The XYZ coordinates of the vertex point.
- OCCT:
BRepGraph_Tool::Vertex::Pnt(graph, BRepGraph_VertexId(index)). - Example:
let pt = graph.vertexPoint(0) print("(\(pt.x), \(pt.y), \(pt.z))")
vertexTolerance(_:)
Get the tolerance of a vertex.
public func vertexTolerance(_ vertexIndex: Int) -> Double
- Parameters:
vertexIndex— zero-based vertex index. - Returns: The vertex tolerance (modelling precision for this vertex).
- OCCT:
BRepGraph_Tool::Vertex::Tolerance(graph, BRepGraph_VertexId(index)).
Edge Geometry
edgeTolerance(_:)
Get the tolerance of an edge.
public func edgeTolerance(_ edgeIndex: Int) -> Double
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::Edge::Tolerance(graph, BRepGraph_EdgeId(index)).
isEdgeDegenerated(_:)
Check if an edge is degenerated.
public func isEdgeDegenerated(_ edgeIndex: Int) -> Bool
Degenerated edges have zero-length 3D curves (e.g. the apex edge of a cone). They carry a pcurve but no meaningful 3D geometry.
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::Edge::Degenerated(graph, BRepGraph_EdgeId(index)).
isEdgeSameParameter(_:)
Check if an edge has the SameParameter flag.
public func isEdgeSameParameter(_ edgeIndex: Int) -> Bool
SameParameter means the 3D curve and all pcurves share the same parameter range. Resolved via the first coedge of the edge; returns true for free edges with no coedge.
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::CoEdge::SameParameter(graph, coEdgeId)(p1: per-coedge property).
isEdgeSameRange(_:)
Check if an edge has the SameRange flag.
public func isEdgeSameRange(_ edgeIndex: Int) -> Bool
SameRange means all pcurves of an edge have the same parameter range as the 3D curve. Resolved via the first coedge.
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::CoEdge::SameRange(graph, coEdgeId)(p1: per-coedge property).
edgeRange(_:)
Get the parameter range of an edge.
public func edgeRange(_ edgeIndex: Int) -> (first: Double, last: Double)
- Parameters:
edgeIndex— zero-based edge index. - Returns: The (first, last) parameter values of the edge’s 3D curve.
- OCCT:
BRepGraph_Tool::Edge::Range(graph, BRepGraph_EdgeId(index)).
edgeHasCurve(_:)
Check if an edge has a 3D curve.
public func edgeHasCurve(_ edgeIndex: Int) -> Bool
Degenerated edges may lack a 3D curve. Use before accessing curve geometry.
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph_Tool::Edge::HasCurve(graph, BRepGraph_EdgeId(index)).
isEdgeClosedOnFace(edgeIndex:faceIndex:)
Check if an edge is a seam (closed) on a face.
public func isEdgeClosedOnFace(edgeIndex: Int, faceIndex: Int) -> Bool
A seam edge appears twice in the wire of a closed face (e.g. the longitudinal seam of a cylinder). In OCCT 8.0.0 p1, this is IsSeamOnFace.
- Parameters:
edgeIndex— zero-based edge index;faceIndex— zero-based face index. - OCCT:
BRepGraph_Tool::Edge::IsSeamOnFace(graph, edgeId, faceId).
edgeHasPolygon3D(_:)
Check if an edge has a 3D polygon (mesh discretization).
public func edgeHasPolygon3D(_ edgeIndex: Int) -> Bool
Returns true if the edge has a cached or persistent Poly_Polygon3D in the graph’s mesh layer.
- Parameters:
edgeIndex— zero-based edge index. - OCCT:
BRepGraph::MeshView::Effective().Edges().Has(BRepGraph_EdgeId(index)).
edgeMaxContinuity(_:)
Get the maximum continuity order of an edge (as GeomAbs_Shape raw int).
public func edgeMaxContinuity(_ edgeIndex: Int) -> Int
Returns the GeomAbs_Shape continuity enum raw value (0 = C0, 1 = C1, 2 = C2, …). Currently returns 0 always — BRepGraph_LayerRegularity is unavailable in OCCT 8.0.0 p1 due to an upstream header bug. Use Shape.maxContinuity (BRep_Tool::MaxContinuity) as an alternative.
- Parameters:
edgeIndex— zero-based edge index. - Note: Always returns 0 in OCCT 8.0.0 p1 —
BRepGraph_LayerRegularitydoes not compile/link. UseShape.maxContinuityinstead.
Face Geometry
faceTolerance(_:)
Get the tolerance of a face.
public func faceTolerance(_ faceIndex: Int) -> Double
- Parameters:
faceIndex— zero-based face index. - OCCT:
BRepGraph_Tool::Face::Tolerance(graph, BRepGraph_FaceId(index)).
isFaceNaturalRestriction(_:)
Check if a face has the natural restriction flag.
public func isFaceNaturalRestriction(_ faceIndex: Int) -> Bool
In OCCT p1, this is derived: a face with no bounding wires (NbWires == 0) is considered naturally restricted. In practice, p1 always materializes bounding wires, so this typically returns false for real graphs.
- Parameters:
faceIndex— zero-based face index. - OCCT:
BRepGraph_Tool::Face::NbWires(graph, faceId) == 0.
faceHasSurface(_:)
Check if a face has a surface.
public func faceHasSurface(_ faceIndex: Int) -> Bool
- Parameters:
faceIndex— zero-based face index. - OCCT:
BRepGraph_Tool::Face::HasSurface(graph, BRepGraph_FaceId(index)).
faceHasTriangulation(_:)
Check if a face has a triangulation.
public func faceHasTriangulation(_ faceIndex: Int) -> Bool
Returns true if the face has a cached or persistent Poly_Triangulation in the graph’s mesh layer.
- Parameters:
faceIndex— zero-based face index. - OCCT:
BRepGraph::MeshView::Effective().Faces().Has(BRepGraph_FaceId(index)).
Wire Queries
isWireClosed(_:)
Check if a wire is topologically closed.
public func isWireClosed(_ wireIndex: Int) -> Bool
- Parameters:
wireIndex— zero-based wire index. - OCCT:
BRepGraph_Tool::Wire::IsClosed(graph, BRepGraph_WireId(index)).
wireCoEdgeCount(_:)
Number of coedges in a wire.
public func wireCoEdgeCount(_ wireIndex: Int) -> Int
- Parameters:
wireIndex— zero-based wire index. - OCCT:
BRepGraph_Tool::Wire::NbCoEdges(graph, BRepGraph_WireId(index)).
wireFaceCount(_:)
Number of faces a wire belongs to.
public func wireFaceCount(_ wireIndex: Int) -> Int
- Parameters:
wireIndex— zero-based wire index. - OCCT:
BRepGraph_FacesOfWireiterator over the wire’sParentWireRefIds(p1 replacement forWireOps::Faces()).
wireFaces(_:)
Indices of faces a wire belongs to.
public func wireFaces(_ wireIndex: Int) -> [Int]
- Parameters:
wireIndex— zero-based wire index. - Returns: Face indices that own this wire.
- OCCT:
BRepGraph_FacesOfWireiterator overBRepGraph::TopoView::Wires().Relations().ParentWireRefIds. - Example:
let faces = graph.wireFaces(0) // outer wire of face 0 belongs to exactly face 0