BRepGraph. Editor Geometry, Sampling & Durable Identity
This page covers the final sections of BRepGraph: geometric setters for the EditorView, assembly-building ProductOps, in-place RepOps swaps, MeshView cache inspection, UV-grid and edge-curve sampling, and the durable-identity UID/RefUID/ItemUID API. See the other BRepGraph, … pages for the core read API, write helpers, and I/O.
Topics
- EditorView Geometric Setters & PCurve API · EditorView ProductOps Assembly Building · EditorView RepOps Non-Guard Setters · MeshView Cache Entry Inspection · UV-Grid Sampling · Edge Curve Sampling · Durable Identity (UID / RefUID / ItemUID)
EditorView Geometric Setters & PCurve API
(v0.162.0) Low-level geometric mutation of coedge, edge, face, and reference-entry nodes, used when reconstructing or repairing a BRepGraph from external data.
setCoEdgeUVBox(_:u1:v1:u2:v2:) (removed in #1652)
OCCT 8.0.1 stores no per-coedge UV box. BRepGraphInc::CoEdgeDef carries no UV field, and BRepGraph_Tool::CoEdge::UVPoints derives the endpoints from the PCurve, so this setter wrote nothing and no reader would have consulted it. Set the PCurve instead, and the UV endpoints follow it:
let graph = BRepGraph(shape: box)!
let pcurve = Curve2D.line(through: SIMD2(2, 3), direction: SIMD2(1, 0))!
graph.coEdgeSetPCurve(0, curve2D: pcurve)
Measured in Scripts/repro/1652-brepgraph-noop-setters/: rebinding the PCurve moves the reported UV endpoints from (0, 0)-(10, 0) to (2, 3)-(6, 3).
setEdgeRegularity(_:face1:face2:continuity:)
Set the geometric regularity (C^k continuity) for an edge across a pair of faces.
@discardableResult
public func setEdgeRegularity(_ edgeIndex: Int, face1: Int, face2: Int, continuity: Int) -> Bool
Pass the same index for face1 and face2 to set the seam continuity across a closed-surface seam line. In OCCT 8.0.0 GA the continuity model lives on the (edge, face1, face2) triple in BRepGraph_LayerRegularity; the earlier per-coedge setters were removed.
- Parameters:
edgeIndex: per-kind edge index.face1,face2, adjacent face indices (equal for seam).continuity: ignored; see the note below.
- Returns: Always
falseon OCCT 8.0.0p1. - OCCT: none; the GA write path
BRepGraph_LayerRegularitydoes not exist in the pinned kernel (viaOCCTBRepGraphSetEdgeRegularity).
This setter does not work on the pinned kernel.
BRepGraph_LayerRegularity, the only write path in the GA continuity model, does not compile in 8.0.0p1 and is absent fromlibOCCT, so the bridge function is a stub that reports failure without readingcontinuityat all. It had shipped that way silently since the GA upgrade: the one test covering it discarded the return value and asserted nothing (fixed in #490, tracked for resolution in #513). To read continuity, useShape.continuity(edge:face1:face2:)orShape.maxContinuity(edge:), which go through the shape-basedBRepLib/BRep_Toolpath and are unaffected.
- Example:
let ok = graph.setEdgeRegularity(2, face1: 0, face2: 1, continuity: 2) #expect(ok == false) // no write path in 8.0.0p1
setFaceTriangulationRep(_:triRepId:)
Set the active triangulation rep on a face, binding a fresh Triangulation to the persistent tier.
public func setFaceTriangulationRep(_ faceIndex: Int, triRepId: Int)
Also see appendCachedTriangulation for cache-tier writes.
- Parameters:
faceIndex, per-kind face index;triRepId, rep-store triangulation id. - OCCT:
BRepGraph::Mesh().Editor().Faces().SetCachedTriangulation(BRepGraph_FaceId, Handle(Poly_Triangulation))(viaOCCTBRepGraphSetFaceTriangulationRep).
coEdgeCreateCurve2DRep(_:)
Create a new Curve2DRep from a Curve2D and return its rep id.
public func coEdgeCreateCurve2DRep(_ curve2D: Curve2D) -> Int?
- Parameters:
curve2D, the 2D curve to wrap in a new rep entry. - Returns: Non-negative rep id on success, or
nilon failure. - OCCT: none; 8.0.0p1 removed standalone curve-2D rep creation, so the bridge stashes the
Geom2d_Curvehandle in a side registry and returns its index as the legacy rep id (viaOCCTBRepGraphCoEdgeCreateCurve2DRep). - Example:
if let repId = graph.coEdgeCreateCurve2DRep(myCurve2D) { graph.coEdgeSetPCurve(3, curve2D: myCurve2D) }
coEdgeSetPCurve(_:curve2D:)
Assign or clear the PCurve bound to an existing coedge.
public func coEdgeSetPCurve(_ coedgeIndex: Int, curve2D: Curve2D?)
- Parameters:
coedgeIndex, per-kind coedge index;curve2D, the curve to assign, ornilto clear the binding. - OCCT:
BRepGraph::Editor().CoEdges().SetPCurve(BRepGraph_CoEdgeId, Handle(Geom2d_Curve))(viaOCCTBRepGraphCoEdgeSetPCurve).
coEdgeAddPCurve(edgeIndex:faceIndex:curve2D:first:last:orientation:)
Attach a PCurve to an edge for a given face context, creating a new CoEdge entry.
public func coEdgeAddPCurve(edgeIndex: Int, faceIndex: Int, curve2D: Curve2D,
first: Double, last: Double, orientation: Int = 0)
- Parameters:
edgeIndex: the edge to attach the PCurve to.faceIndex: the face context.curve2D: the parametric curve.first,last, parameter range on the curve.orientation: 0 = forward, 1 = reversed (default0).
- OCCT:
BRepGraphcoedge construction (viaOCCTBRepGraphCoEdgeAddPCurve). - Example:
graph.coEdgeAddPCurve(edgeIndex: 1, faceIndex: 0, curve2D: pcurve, first: 0.0, last: 1.0, orientation: 0)
setVertexRefLocalLocation, setCoEdgeRefLocalLocation, setWireRefLocalLocation, setFaceRefLocalLocation, setShellRefLocalLocation, setSolidRefLocalLocation (removed in #1652)
A BRepGraph reference carries a location only if its storage struct declares one, and in OCCT 8.0.1 only BRepGraphInc::ChildRef and BRepGraphInc::OccurrenceRef do. The six per-topology setters had no field to write, and their six get-side siblings (vertexRefLocalLocation(_:) and family) had none to read, so each could only ever return nil. BRepGraph_RefId::Kind has no coedge case at all, so a coedge reference never existed to place.
Place topology through the occurrence or child reference that owns it, with setOccurrenceRefLocalLocation(_:matrix:) or setChildRefLocalLocation(_:matrix:) below.
Measured in Scripts/repro/1652-brepgraph-noop-setters/.
setOccurrenceRefLocalLocation(_:matrix:)
Set the local TopLoc_Location of an occurrence reference entry.
public func setOccurrenceRefLocalLocation(_ occurrenceRefIndex: Int, matrix: [Double])
- OCCT:
TopLoc_Location(viaOCCTBRepGraphSetOccurrenceRefLocalLocation).
setChildRefLocalLocation(_:matrix:)
Set the local TopLoc_Location of a child reference entry.
public func setChildRefLocalLocation(_ childRefIndex: Int, matrix: [Double])
- OCCT:
TopLoc_Location(viaOCCTBRepGraphSetChildRefLocalLocation).
identityLocationMatrix
Identity matrix (3×4) suitable for the set*LocalLocation calls and for the placement: arguments of the ProductOps methods below.
public static var identityLocationMatrix: [Double] { get }
Returns [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], a row-major identity with zero translation.
- Example:
graph.setChildRefLocalLocation(childRefIndex, matrix: BRepGraph.identityLocationMatrix)
EditorView ProductOps Assembly Building
(v0.163.0) Methods for building and editing the product/assembly graph layer of BRepGraph.
linkProductToTopology(shapeRootKind:shapeRootIndex:placement:)
Wrap an existing topology root in a new Product.
public func linkProductToTopology(shapeRootKind: Int, shapeRootIndex: Int,
placement: [Double]? = nil) -> Int?
- Parameters:
shapeRootKind:BRepGraph_NodeId::Kindordinal of the topology root (e.g. 0 = Solid).shapeRootIndex: per-kind node index of the root.placement: optional 12-element 3×4 row-major placement matrix; passnilfor identity.
- Returns: New product id on success, or
nilon failure. - OCCT:
BRepGraphproduct-layer creation (viaOCCTBRepGraphLinkProductToTopology). - Note: Precondition:
placement.count == 12if non-nil. - Example:
if let pid = graph.linkProductToTopology(shapeRootKind: 0, shapeRootIndex: 0) { print("Product id:", pid) }
createEmptyProduct()
Create an empty product (assembly node with no direct topology).
public func createEmptyProduct() -> Int?
- Returns: New product id, or
nilon failure. - OCCT:
BRepGraphproduct-layer (viaOCCTBRepGraphCreateEmptyProduct). - Example:
guard let assemblyId = graph.createEmptyProduct() else { return }
linkProducts(parentProductIndex:referencedProductIndex:placement:parentOccurrenceIndex:)
Link two products via a fresh occurrence (assembly reference).
public func linkProducts(parentProductIndex: Int, referencedProductIndex: Int,
placement: [Double], parentOccurrenceIndex: Int? = nil)
-> (occurrenceIndex: Int, occurrenceRefIndex: Int)?
- Parameters:
parentProductIndex: product that will own the new occurrence.referencedProductIndex: product being instanced.placement: 12-element 3×4 row-major transform for the instance.parentOccurrenceIndex: passnilfor an unparented occurrence.
- Returns: Tuple of
(occurrenceIndex, occurrenceRefIndex), ornilon failure. - OCCT:
BRepGraphoccurrence construction (viaOCCTBRepGraphLinkProducts). - Note: Precondition:
placement.count == 12. - Example:
let matrix = BRepGraph.identityLocationMatrix if let result = graph.linkProducts(parentProductIndex: 0, referencedProductIndex: 1, placement: matrix) { print("Occurrence:", result.occurrenceIndex, "OccurrenceRef:", result.occurrenceRefIndex) }
productRemoveOccurrence(_:occurrenceRefIndex:)
Detach an occurrence ref from a product.
public func productRemoveOccurrence(_ productIndex: Int, occurrenceRefIndex: Int) -> Bool
- Parameters:
productIndex, the owning product;occurrenceRefIndex, the occurrence-ref to remove. - Returns:
trueif the active usage was removed. - OCCT:
BRepGraphproduct-layer (viaOCCTBRepGraphProductRemoveOccurrence).
productRemoveShapeRoot(_:)
Detach the scalar shape-root from a product.
public func productRemoveShapeRoot(_ productIndex: Int) -> Bool
- Returns:
trueif a root was detached. - OCCT:
BRepGraphproduct-layer (viaOCCTBRepGraphProductRemoveShapeRoot).
EditorView RepOps Non-Guard Setters
(v0.164.0) In-place swaps of the geometry object bound to an existing rep id. 8.0.0p1 removed the standalone representation editor addressed by rep id, so these write into the bridge-side registry that backs the legacy rep-id ABI: a later set*RepId() call resolves the updated handle and hands it to the per-kind editor. They do not recreate the rep, and they make no structural graph change.
repSetSurface(_:surface:)
Swap the surface bound to an existing surface rep id.
public func repSetSurface(_ surfaceRepId: Int, surface: Surface)
- Parameters:
surfaceRepId, rep-store surface rep id;surface, the replacementSurface. - OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetSurface).
repSetCurve3D(_:curve:)
Swap the 3D curve bound to an existing curve-3D rep id.
public func repSetCurve3D(_ curve3DRepId: Int, curve: Curve3D)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetCurve3D).
repSetCurve2D(_:curve:)
Swap the 2D curve bound to an existing curve-2D rep id.
public func repSetCurve2D(_ curve2DRepId: Int, curve: Curve2D)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetCurve2D).
repSetTriangulation(_:triangulation:)
Swap the triangulation bound to an existing triangulation rep id.
public func repSetTriangulation(_ triRepId: Int, triangulation: Triangulation)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetTriangulation).
repSetPolygon3D(_:polygon:)
Swap the Polygon3D bound to an existing polygon-3D rep id.
public func repSetPolygon3D(_ polyRepId: Int, polygon: Polygon3D)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetPolygon3D).
repSetPolygon2D(_:polygon:)
Swap the Polygon2D bound to an existing polygon-2D rep id.
public func repSetPolygon2D(_ polyRepId: Int, polygon: Polygon2D)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetPolygon2D).
repSetPolygonOnTri(_:polygon:)
Swap the PolygonOnTriangulation bound to an existing polygon-on-triangulation rep id.
public func repSetPolygonOnTri(_ polyRepId: Int, polygon: PolygonOnTriangulation)
- OCCT: none; the write lands in the bridge-side rep-id registry, and the handle reaches the graph on the next
set*RepId()call (viaOCCTBRepGraphRepSetPolygonOnTri).
repSetPolygonOnTriTriangulationId(_:triRepId:) (removed in #1652)
BRepGraphInc::CoEdgePolygonOnTriRep is {ParentCoEdgeId, Polygon}: there is no triangulation id on the rep to rebind. A polygon-on-triangulation resolves its owning triangulation at attach time, through CoEdgeDef.FaceId to FaceDef.TriangulationRepId, so changing the face’s triangulation is what changes what the polygon resolves against:
let graph = BRepGraph(shape: box)!
let newTriRepId = graph.createTriangulationRep(updatedTri)!
graph.setFaceTriangulationRep(faceIndex, triRepId: newTriRepId)
Measured in Scripts/repro/1652-brepgraph-noop-setters/.
MeshView Cache Entry Inspection
(v0.164.0) Read-only accessors for the cached-mesh tier (algorithm-derived meshes). All return absent values (false, 0, or nil) when no cache entry exists for the entity.
cachedFaceMeshIsPresent(_:)
Whether a cached mesh entry exists for the given face.
public func cachedFaceMeshIsPresent(_ faceIndex: Int) -> Bool
- OCCT:
BRepGraph::Mesh().Cache().Faces().Has(BRepGraph_FaceId)(viaOCCTBRepGraphCachedFaceMeshIsPresent).
cachedFaceMeshTriRepCount(_:)
Number of triangulation reps in the cached mesh entry for a face.
public func cachedFaceMeshTriRepCount(_ faceIndex: Int) -> Int
- OCCT:
OCCTBRepGraphCachedFaceMeshTriRepCount.
cachedFaceMeshActiveIndex(_:)
The active triangulation rep index within the cached mesh entry for a face.
public func cachedFaceMeshActiveIndex(_ faceIndex: Int) -> Int
- OCCT:
OCCTBRepGraphCachedFaceMeshActiveIndex.
cachedFaceMeshStoredOwnGen(_:)
The stored generation counter from when the cached face mesh was last committed.
public func cachedFaceMeshStoredOwnGen(_ faceIndex: Int) -> UInt32
This is the entry’s own field (FaceMeshEntry::MeshGeneration), maintained by OCCT’s mesh cache. It is not comparable to the graph’s generation counter, and this page previously said to compare them, it never worked (#295).
- OCCT:
OCCTBRepGraphCachedFaceMeshStoredOwnGen.
cachedFaceMeshTriRepId(_:repIndex:)
The triangulation rep id at a given rep-slot in the cached face mesh.
public func cachedFaceMeshTriRepId(_ faceIndex: Int, repIndex: Int) -> Int?
- Returns: Non-negative rep id, or
nilifrepIndexis out of range. - OCCT:
OCCTBRepGraphCachedFaceMeshTriRepId. - Example:
if graph.cachedFaceMeshIsPresent(0) { let count = graph.cachedFaceMeshTriRepCount(0) for i in 0..<count { if let repId = graph.cachedFaceMeshTriRepId(0, repIndex: i) { print("TriRep:", repId) } } }
cachedEdgeMeshIsPresent(_:)
Whether a cached mesh entry exists for the given edge.
public func cachedEdgeMeshIsPresent(_ edgeIndex: Int) -> Bool
- OCCT:
OCCTBRepGraphCachedEdgeMeshIsPresent.
cachedEdgeMeshPolygon3DRepId(_:)
The polygon-3D rep id in the cached edge mesh entry.
public func cachedEdgeMeshPolygon3DRepId(_ edgeIndex: Int) -> Int?
- Returns: Non-negative rep id, or
nilif no entry exists. - OCCT:
OCCTBRepGraphCachedEdgeMeshPolygon3DRepId.
cachedEdgeMeshStoredOwnGen(_:)
The stored generation counter for the cached edge mesh.
public func cachedEdgeMeshStoredOwnGen(_ edgeIndex: Int) -> UInt32
- OCCT:
OCCTBRepGraphCachedEdgeMeshStoredOwnGen.
cachedCoEdgeMeshIsPresent(_:)
Whether a cached mesh entry exists for the given coedge.
public func cachedCoEdgeMeshIsPresent(_ coedgeIndex: Int) -> Bool
- OCCT:
OCCTBRepGraphCachedCoEdgeMeshIsPresent.
cachedCoEdgeMeshPolygon2DRepId(_:)
The polygon-2D rep id in the cached coedge mesh entry.
public func cachedCoEdgeMeshPolygon2DRepId(_ coedgeIndex: Int) -> Int?
- Returns: Non-negative rep id, or
nilif no entry exists. - OCCT:
OCCTBRepGraphCachedCoEdgeMeshPolygon2DRepId.
cachedCoEdgeMeshPolygonOnTriRepCount(_:)
Number of polygon-on-triangulation reps in the cached coedge mesh entry.
public func cachedCoEdgeMeshPolygonOnTriRepCount(_ coedgeIndex: Int) -> Int
- OCCT:
OCCTBRepGraphCachedCoEdgeMeshPolygonOnTriRepCount.
cachedCoEdgeMeshPolygonOnTriRepId(_:repIndex:)
The polygon-on-triangulation rep id at a given rep-slot in the cached coedge mesh.
public func cachedCoEdgeMeshPolygonOnTriRepId(_ coedgeIndex: Int, repIndex: Int) -> Int?
- Returns: Non-negative rep id, or
nilifrepIndexis out of range. - OCCT:
OCCTBRepGraphCachedCoEdgeMeshPolygonOnTriRepId.
cachedCoEdgeMeshStoredOwnGen(_:)
The stored generation counter for the cached coedge mesh.
public func cachedCoEdgeMeshStoredOwnGen(_ coedgeIndex: Int) -> UInt32
- OCCT:
OCCTBRepGraphCachedCoEdgeMeshStoredOwnGen.
UV-Grid Sampling
(v0.136.0)
FaceGridSample
Result of sampling a face surface on a regular UV grid.
public struct FaceGridSample: Sendable {
/// Surface positions at grid points, U-major.
public let positions: [SIMD3<Double>]
/// Surface normals at grid points, U-major.
public let normals: [SIMD3<Double>]
/// Gaussian curvature at each grid point, U-major.
public let gaussianCurvatures: [Double]
/// Mean curvature at each grid point, U-major.
public let meanCurvatures: [Double]
/// Number of samples in U direction.
public let uSamples: Int
/// Number of samples in V direction.
public let vSamples: Int
/// Position, normal and curvatures at the given U/V grid index.
public func at(u: Int, v: Int) -> (position: SIMD3<Double>, normal: SIMD3<Double>,
gaussianCurvature: Double, meanCurvature: Double)
}
| Field | Meaning |
|---|---|
positions | Surface positions at grid points, U-major |
normals | Surface normals at grid points, U-major |
gaussianCurvatures | Gaussian curvature at each grid point, U-major |
meanCurvatures | Mean curvature at each grid point, U-major |
uSamples | Number of samples in the U direction |
vSamples | Number of samples in the V direction |
Read through at(u:v:) rather than spelling the index out. Until #617 the bridge wrote these buffers transposed (v * uSamples + u) while this page already documented the U-major index above, so a caller who followed the docs read the wrong point of the face; getting the stride wrong instead (u * uSamples + v) is silently in range on a 3×10 grid and out of bounds on a 10×3 one. at(u:v:) removes both failure modes by owning the index.
guard let sample = graph.sampleFaceUVGrid(faceIndex: 0, uSamples: 10, vSamples: 3)
else { return }
for u in 0..<sample.uSamples {
for v in 0..<sample.vSamples {
let s = sample.at(u: u, v: v)
print("(\(u), \(v)) -> \(s.position) kG=\(s.gaussianCurvature) kH=\(s.meanCurvature)")
}
}
BRepGraph.FaceGridSample.vSamples
Number of samples in the V direction.
All four arrays share one layout: U-major, u varying slowest and v fastest, so grid position (u, v) sits at index = u * vSamples + v for u ∈ [0, uSamples) and v ∈ [0, vSamples). This is the layout SurfaceGrid and SurfaceGridD1 use, and the one #486 declared for the whole API.
BRepGraph.FaceGridSample.at(u:v:)
Position, normal and curvatures at the given U/V grid index. Read through this rather than spelling the flat index out yourself. Until #617 the bridge wrote these buffers transposed (v * uSamples + u) while this page already documented the U-major index above, so a caller who followed the docs read the wrong point of the face; getting the stride wrong instead (u * uSamples + v) is silently in range on a 3×10 grid and out of bounds on a 10×3 one. at(u:v:) removes both failure modes by owning the index.
public func at(u: Int, v: Int) -> (position: SIMD3<Double>, normal: SIMD3<Double>,
gaussianCurvature: Double, meanCurvature: Double)
- Parameters:
u: U grid index,0..<uSamples;v: V grid index,0..<vSamples. - Returns: The position, normal, Gaussian curvature and mean curvature at that grid point.
- Example:
if let sample = graph.sampleFaceUVGrid(faceIndex: 0, uSamples: 3, vSamples: 10) { let corner = sample.at(u: 2, v: 9) print(corner.position, corner.meanCurvature) }
Walk the whole grid row by row (U outer, V inner) to visit every point in storage order:
guard let sample = graph.sampleFaceUVGrid(faceIndex: 0, uSamples: 10, vSamples: 3)
else { return }
for u in 0..<sample.uSamples {
for v in 0..<sample.vSamples {
let s = sample.at(u: u, v: v)
print("(\(u), \(v)) -> \(s.position) kG=\(s.gaussianCurvature) kH=\(s.meanCurvature)")
}
}
sampleFaceUVGrid(faceIndex:uSamples:vSamples:)
Sample a face surface on a regular UV grid, evaluating positions, normals, and principal curvatures.
public func sampleFaceUVGrid(faceIndex: Int, uSamples: Int, vSamples: Int) -> FaceGridSample?
- Parameters:
faceIndex: face definition index.uSamples: number of samples in U direction (must be ≥ 1).vSamples: number of samples in V direction (must be ≥ 1).
The bound is on the product:
uSamples * vSamplesmust not exceedSampling.maximumSampleCount(10,000,000), and each factor is checked on its own, since two negatives multiply to a plausible positive total (#558). This sampler fills four buffers per point (position, normal, Gaussian and mean curvature), so it reaches the ceiling’s memory cost sooner than the point-only samplers do. - Returns:
FaceGridSamplewithuSamples × vSamplesentries laid out U-major (u * vSamples + v, seeFaceGridSampleabove), ornilif the face has no surface, sampling fails, or the grid cannot be served. - OCCT:
GeomLProp_SLProps(position, normal, curvature evaluation) viaOCCTBRepGraphSampleFaceUVGrid. - Example:
if let grid = graph.sampleFaceUVGrid(faceIndex: 0, uSamples: 5, vSamples: 5) { for (i, pos) in grid.positions.enumerated() { let kG = grid.gaussianCurvatures[i] let kH = grid.meanCurvatures[i] print("(\(pos.x), \(pos.y), \(pos.z)) kG=\(kG) kH=\(kH)") } } - Example (indexed by grid position, not flat order):
if let grid = graph.sampleFaceUVGrid(faceIndex: 0, uSamples: 10, vSamples: 3) { // The far corner of a non-square grid: `at` resolves the U-major index for you. let corner = grid.at(u: grid.uSamples - 1, v: grid.vSamples - 1) print(corner.position, corner.normal, corner.meanCurvature) }
Edge Curve Sampling
(v0.136.0)
sampleEdgeCurve(edgeIndex:count:)
Sample evenly-spaced points along an edge curve.
public func sampleEdgeCurve(edgeIndex: Int, count: Int) -> [SIMD3<Double>]
- Parameters:
edgeIndex: edge definition index.count: number of points to sample, a request honoured within1...Sampling.maximumSampleCount(10,000,000); outside that range the result is empty (#558).
- Returns: Array of 3D points along the edge curve, in parameter order; empty if the edge has no curve or sampling fails.
- OCCT:
BRepGraph_Tool::Edge::CurveandBRepGraph_Tool::Edge::Range, sampled withGeom_Curve::Value(viaOCCTBRepGraphSampleEdgeCurve). - Example:
let pts = graph.sampleEdgeCurve(edgeIndex: 0, count: 20) for p in pts { print(p.x, p.y, p.z) }
Durable Identity (UID / RefUID / ItemUID)
(OCCT 8.0.0p1) The UID system provides counter-based identifiers that remain stable across mutations of the one graph instance that minted them, compaction, node removal. Unlike (kind, index) pairs, counters never repeat within a kind and survive vector-index shifts. A counter of 0 is always the invalid sentinel.
Scope: one graph instance
A UID is meaningful only inside the graph that minted it. Every graph allocates counters from 1 independently, so the same (kind, counter) names some unrelated node in every other graph. Each UID therefore carries a graphID, the instanceID of the graph that minted it, and the resolvers reject a UID from anywhere else:
let boxGraph = BRepGraph(shape: box)!
let cylGraph = BRepGraph(shape: cylinder)!
let faceUID = boxGraph.uid(ofNodeKind: 2, index: 2)! // a face of the BOX
boxGraph.node(forUID: faceUID) // Optional((kind: 2, index: 2))
cylGraph.node(forUID: faceUID) // nil, not this graph's UID
cylGraph.contains(uid: faceUID) // false
The scope is the graph instance, not the shape: two graphs built from the same shape are two instances, and a UID from one does not resolve in the other. A rebuild is likewise a new instance.
Which operations carry identity across follows the kernel’s own rule, whether it transplants the UID counter space into the target:
| Operation | Identity | UIDs |
|---|---|---|
compact(), node removal, add(_:absorbing:…) | same instance, mutated in place | keep resolving |
copy(), translated() | inherited, BRepGraph_Copy/_Transform::Perform transplant the counter space, Generation and GraphGUID | keep resolving, naming the same nodes |
copyFace() | fresh, CopyNode lifts one face without the counter space | source UIDs return nil |
| a new graph over any shape (incl. a rebuild of the same one) | fresh | source UIDs return nil |
To carry a selection across a modelling operation rather than across mutations of one graph, absorb the operation’s history, see add(_:absorbing:inputRoots:operationName:). To carry one across a save/load, store (kind, index) with the shape and re-mint after rebuilding, see BRep Graph § UIDs and persistence.
Before v1.12.0 UIDs carried no provenance, so a UID from an unrelated graph resolved to a plausible but wrong node instead of returning
nil.copyFace()was affected the same way. (#295)
GraphUID
A durable node identifier: a (kind, counter) pair for a definition node, stamped with the graph that minted it.
public struct GraphUID: Sendable, Hashable, Codable {
public var kind: Int
public var counter: UInt32
public let graphID: UInt64
public var isValid: Bool { get }
}
graphID is the instanceID of the minting graph. 0 means unstamped, built by hand, or decoded from a payload written before v1.12.0, and resolves in no graph. Mint UIDs with uid(ofNodeKind:index:) rather than constructing them.
kind is the raw BRepGraph_NodeId::Kind ordinal:
| Value | Node type |
|---|---|
| 0 | Solid |
| 1 | Shell |
| 2 | Face |
| 3 | Wire |
| 4 | Edge |
| 5 | Vertex |
| 6 | Compound |
| 7 | CompSolid |
| 8 | CoEdge |
| 10 | Product |
| 11 | Occurrence |
isValid returns true when counter > 0; a valid UID may still fail to resolve if the node has been removed from the graph.
GraphUID.graphID
The instanceID of the graph instance that minted this UID. 0 means unstamped, built by hand or decoded from a payload written before v1.12.0, and resolves in no graph. BRepGraph.node(forUID:) rejects a UID minted by a different graph instance.
GraphUID.counter
The per-kind counter component of the (kind, counter) pair. Never repeats within a kind, and stays stable when the node’s (kind, index) shifts, e.g. after compact(). 0 is the invalid sentinel; see isValid.
kind is the raw BRepGraph_NodeId::Kind ordinal:
| Value | Node type |
|---|---|
| 0 | Solid |
| 1 | Shell |
| 2 | Face |
| 3 | Wire |
| 4 | Edge |
| 5 | Vertex |
| 6 | Compound |
| 7 | CompSolid |
| 8 | CoEdge |
| 10 | Product |
| 11 | Occurrence |
isValid returns true when counter > 0; a valid UID may still fail to resolve if the node has been removed from the graph.
GraphRefUID
A durable reference-entry identifier: a (kind, counter) pair for a reference (ref) node. Scoped to one graph instance and stamped with graphID, exactly as GraphUID is.
public struct GraphRefUID: Sendable, Hashable, Codable {
public var kind: Int
public var counter: UInt32
public let graphID: UInt64
public var isValid: Bool { get }
}
kind is the raw BRepGraph_RefId::Kind ordinal:
| Value | Ref type |
|---|---|
| 0 | Shell |
| 1 | Face |
| 2 | Wire |
| 3 | Vertex |
| 4 | Solid |
| 5 | Child |
| 6 | Occurrence |
| Field | Meaning |
|---|---|
kind | Raw BRepGraph_RefId::Kind ordinal, see the table above |
counter | Per-kind sequence number minted when the reference was created; 0 means invalid |
graphID | The BRepGraph instance ID that minted this UID; 0 means unstamped, resolving in no graph |
BRepGraph.GraphRefUID.graphID
The minting graph’s own instanceID; 0 means unstamped, which resolves in no graph.
GraphItemUID
A durable generic item identifier covering both definition nodes (domain == 1) and reference entries (domain == 2). Scoped to one graph instance and stamped with graphID, exactly as GraphUID is.
public struct GraphItemUID: Sendable, Hashable, Codable {
public var domain: Int
public var kind: Int
public var counter: UInt32
public let graphID: UInt64
public var isValid: Bool { get }
}
domain values: 1 = node, 2 = reference. kind is the raw kind ordinal in that domain’s enum space.
GraphItemUID.graphID
uid(ofNodeKind:index:)
Return the durable GraphUID for a node.
public func uid(ofNodeKind kind: Int, index: Int) -> GraphUID?
- Parameters:
kind, rawBRepGraph_NodeId::Kindordinal;index, per-kind node index. - Returns:
GraphUIDwith a non-zero counter, ornilif the node is invalid, removed, or out of bounds. - OCCT:
BRepGraph_UID, minted by the graph’s own UID registry (UIDs().Of(nodeId)), keyed by aBRepGraph_NodeIdbuilt fromkindandindex(viaOCCTBRepGraphNodeUID). - Example:
if let faceUID = graph.uid(ofNodeKind: 2, index: 0) { // Face at index 0 print("Face UID counter:", faceUID.counter) }
node(forUID:)
Resolve a GraphUID back to its (kind, index) in this graph.
public func node(forUID uid: GraphUID) -> (kind: Int, index: Int)?
- Parameters:
uid, aGraphUIDpreviously obtained from this graph’suid(ofNodeKind:index:). - Returns:
(kind, index)tuple if the UID resolves, ornilif this graph did not mint it or the node no longer exists. A UID minted by another graph returnsnileven when its counter is in range here, which it usually is, since counters restart per graph. - OCCT:
UIDs().NodeIdFrom(BRepGraph_UID), the registry’s reverse lookup, returning aBRepGraph_NodeId(viaOCCTBRepGraphNodeFromUID). - Example:
guard let faceUID = graph.uid(ofNodeKind: 2, index: 0) else { return } // ... graph mutations ... if let resolved = graph.node(forUID: faceUID) { print("Face now at index:", resolved.index) }
contains(uid:), GraphUID
Return true if this graph minted the GraphUID and the node it names still exists here.
public func contains(uid: GraphUID) -> Bool
- OCCT:
UIDs().Has(BRepGraph_UID)(viaOCCTBRepGraphHasNodeUID).
uid(ofRefKind:index:)
Return the durable GraphRefUID for a reference entry.
public func uid(ofRefKind kind: Int, index: Int) -> GraphRefUID?
- Parameters:
kind, rawBRepGraph_RefId::Kindordinal;index, per-kind reference index. - Returns:
GraphRefUID, ornilif invalid or removed. - OCCT:
BRepGraph_RefUID, minted byUIDs().Of(refId)and keyed by aBRepGraph_RefId(viaOCCTBRepGraphRefUID).
ref(forUID:)
Resolve a GraphRefUID back to its (kind, index).
public func ref(forUID uid: GraphRefUID) -> (kind: Int, index: Int)?
- Returns:
(kind, index)if the UID resolves, ornilif the reference no longer exists. - OCCT:
UIDs().RefIdFrom(BRepGraph_RefUID)(viaOCCTBRepGraphRefFromUID).
contains(uid:), GraphRefUID
Return true if this graph minted the GraphRefUID and the reference it names still exists here.
public func contains(uid: GraphRefUID) -> Bool
- OCCT:
UIDs().Has(BRepGraph_RefUID)(viaOCCTBRepGraphHasRefUID).
itemUID(ofNodeKind:index:)
Return the durable GraphItemUID for a node (domain 1).
public func itemUID(ofNodeKind kind: Int, index: Int) -> GraphItemUID?
- Returns:
GraphItemUIDwithdomain == 1, ornilif the node is invalid or removed. - OCCT:
BRepGraph_ItemUID, minted byUIDs().Of(BRepGraph_ItemId)(viaOCCTBRepGraphItemUIDOfNode). - Example:
if let itemUID = graph.itemUID(ofNodeKind: 2, index: 0) { assert(itemUID.domain == 1) }
item(forUID:)
Resolve a GraphItemUID back to its (domain, kind, index).
public func item(forUID uid: GraphItemUID) -> (domain: Int, kind: Int, index: Int)?
- Returns:
(domain, kind, index)if the UID resolves, ornilif the item no longer exists. - OCCT:
UIDs().ItemIdFrom(BRepGraph_ItemUID), returning aBRepGraph_ItemIdwhoseItemDomain()says whether it names a node or a reference (viaOCCTBRepGraphItemFromUID).
instanceID
Identifies this graph instance for as long as it lives.
public var instanceID: UInt64 { get }
Unique among every graph this process builds, and, because the sequence starts at a random point, distinct from any other process’s ids with overwhelming probability. Every UID this graph mints carries it as graphID, which is how node(forUID:) tells one of its own nodes from a node of some other graph.
Identity here is the graph object, not the geometry: two graphs built from the same shape have different ids.
- OCCT: none, assigned by the bridge per
OCCTBRepGraph(viaOCCTBRepGraphInstanceID). - Example:
let graph = BRepGraph(shape: box)! let uid = graph.uid(ofNodeKind: 2, index: 0)! print(uid.graphID == graph.instanceID) // true, this graph minted it let rebuilt = BRepGraph(shape: box)! // same shape, new instance print(rebuilt.instanceID == graph.instanceID) // false print(rebuilt.node(forUID: uid)) // nil
generation (removed in v2.0.0)
Removed by #784, along with the OCCTBRepGraphGeneration bridge function behind it. Deprecated in v1.12.0.
Use instanceID to compare graph identity. Nothing else is needed: node(forUID:) already rejects a UID minted by another graph on its own.
It was always 1. OCCT advances the counter only from BRepGraph::Clear(), and since v1.12.2 (#303) OCCTSwift calls Clear() exactly once, when it builds a graph, and never rebuilds an existing one. So it landed at 1, stayed there, and read the same for every graph, which is why it could tell neither two graphs apart nor a stale cache.
Earlier revisions of this page suggested comparing a cached storedOwnGen against generation to detect a stale mesh. That recipe never worked: storedOwnGen is a per-entity mesh field (FaceMeshEntry::MeshGeneration), not this counter, so the two were never comparable (#295).