Annotation & GD&T
OCCTSwift provides 3D annotation types for attaching measurement dimensions and positioned text to geometry (Annotation.swift), plus a typed GD&T authoring layer that creates STEP AP242-compatible dimensions, geometric tolerances, and datums on a Document (GDTWrite.swift).
Topics
- DimensionGeometry · LengthDimension · RadiusDimension · AngleDimension · DiameterDimension · TextLabel · PointCloud · Document Extensions — GD&T Enums & Structs · Document Extensions — Typed Read Path · Document Extensions — Write Path
DimensionGeometry
Geometry extracted from a dimension measurement, ready for Metal rendering or downstream layout.
public struct DimensionGeometry: Sendable {
public let firstPoint: SIMD3<Double>
public let secondPoint: SIMD3<Double>
public let centerPoint: SIMD3<Double>
public let textPosition: SIMD3<Double>
public let circleNormal: SIMD3<Double>
public let circleRadius: Double
public let value: Double
public let isValid: Bool
}
Returned by the geometry property on all four dimension types. Fields:
firstPoint— first attachment point on the measured geometry.secondPoint— second attachment point on the measured geometry.centerPoint— angle vertex, or circle center for radius / diameter dimensions.textPosition— suggested 3D location for placing the dimension label.circleNormal— axis direction of the measured circle (radius / diameter only).circleRadius— radius of the measured circle (radius / diameter only;0for linear / angle dims).value— measured value: distance in model units for length/radius/diameter, radians for angle.isValid— whether the extraction succeeded; check before using the other fields.
LengthDimension
Measures distance between two points, along a linear edge, or between two parallel faces.
LengthDimension.init?(from:to:)
Creates a length dimension between two 3D points.
public init?(from p1: SIMD3<Double>, to p2: SIMD3<Double>)
- Parameters:
p1,p2— endpoints of the measured span. - Returns:
nilifPrsDim_LengthDimensionconstruction fails (e.g. coincident points). - OCCT:
PrsDim_LengthDimension(gp_Pnt, gp_Pnt, gp_Pln)— plane is chosen automatically perpendicular to the connecting vector. - Example:
if let dim = LengthDimension(from: SIMD3(0, 0, 0), to: SIMD3(10, 0, 0)) { print(dim.value) // 10.0 }
LengthDimension.init?(edge:)
Creates a length dimension measuring a single linear edge.
public init?(edge: Shape)
- Parameters:
edge— aShapewrapping aTopoDS_Edgethat is straight (line segment). - Returns:
nilifedgeis not a valid linear edge or dimension construction fails. - OCCT:
PrsDim_LengthDimension(TopoDS_Edge, gp_Pln). - Example:
let box = Shape.box(width: 10, height: 5, depth: 2)! for e in box.edges() { if let dim = LengthDimension(edge: e), dim.isValid { print(dim.value) break } }
LengthDimension.init?(face1:face2:)
Creates a length dimension between two parallel planar faces.
public init?(face1: Shape, face2: Shape)
- Parameters:
face1,face2—Shapevalues each wrapping aTopoDS_Face; the faces must be parallel planes. - Returns:
nilif the faces are not parallel or dimension construction fails. - OCCT:
PrsDim_LengthDimension(TopoDS_Face, TopoDS_Face). - Example:
let box = Shape.box(width: 10, height: 5, depth: 2)! let faces = box.faces() if let dim = LengthDimension(face1: faces[0].shape, face2: faces[1].shape) { print(dim.value) }
value
The measured distance.
public var value: Double { get }
- Returns: Distance in model units between the attached geometry points.
- OCCT:
PrsDim_Dimension::GetValue(). - Example:
let dim = LengthDimension(from: .zero, to: SIMD3(3, 4, 0))! print(dim.value) // 5.0
isValid
Whether the dimension geometry is valid and the measured value is meaningful.
public var isValid: Bool { get }
- Returns:
trueif the underlyingPrsDim_Dimensionconsiders its geometry valid. - OCCT:
PrsDim_Dimension::IsValid().
setCustomValue(_:)
Overrides the measured value with a display value for annotation purposes.
public func setCustomValue(_ value: Double)
- Parameters:
value— custom display value in model units. - OCCT:
PrsDim_Dimension::SetCustomValue(Standard_Real). - Note: Affects rendered label text only;
valuecontinues to return the originally measured distance.
geometry
Geometry data for Metal rendering — attachment points, text position, and the measured value.
public var geometry: DimensionGeometry? { get }
- Returns:
DimensionGeometrypopulated from the dimension’s internal geometry, ornilif extraction fails. - OCCT:
PrsDim_LengthDimension::FirstPoint(),SecondPoint(),GetValue(). - Example:
if let dim = LengthDimension(from: SIMD3(0, 0, 0), to: SIMD3(5, 0, 0)), let geo = dim.geometry { print(geo.firstPoint, geo.secondPoint, geo.textPosition) }
RadiusDimension
Measures the radius of circular geometry such as a circle edge, arc, or cylindrical face.
RadiusDimension.init?(shape:)
Creates a radius dimension from a shape with circular geometry.
public init?(shape: Shape)
- Parameters:
shape— aShapewrapping circular geometry (edge or face). - Returns:
nilif the shape does not contain circular geometry or construction fails. - OCCT:
PrsDim_RadiusDimension(TopoDS_Shape). - Example:
let cyl = Shape.cylinder(radius: 5, height: 10)! if let dim = RadiusDimension(shape: cyl) { print(dim.value) // ≈ 5.0 }
value
The measured radius.
public var value: Double { get }
- OCCT:
PrsDim_Dimension::GetValue().
isValid
Whether the dimension is valid.
public var isValid: Bool { get }
- OCCT:
PrsDim_Dimension::IsValid().
setCustomValue(_:)
Overrides the displayed radius with a custom value.
public func setCustomValue(_ value: Double)
- OCCT:
PrsDim_Dimension::SetCustomValue(Standard_Real).
geometry
Geometry data for Metal rendering.
public var geometry: DimensionGeometry? { get }
- Returns:
DimensionGeometrywithcenterPointset to the circle center andcircleNormalset to the axis direction;nilon failure. - OCCT:
PrsDim_RadiusDimension::Circle(),GetValue().
AngleDimension
Measures angles between edges, faces, or a vertex-defined triple of points.
AngleDimension.init?(edge1:edge2:)
Creates an angle dimension between two edges.
public init?(edge1: Shape, edge2: Shape)
- Parameters:
edge1,edge2—Shapevalues wrapping linear or planar edges. - Returns:
nilif the edges are parallel or dimension construction fails. - OCCT:
PrsDim_AngleDimension(TopoDS_Edge, TopoDS_Edge). - Example:
let box = Shape.box(width: 10, height: 10, depth: 5)! let edges = box.edges() if edges.count >= 2, let dim = AngleDimension(edge1: edges[0].shape, edge2: edges[1].shape) { print(dim.degrees) }
AngleDimension.init?(first:vertex:second:)
Creates an angle dimension from three points: first, vertex, second.
public init?(first: SIMD3<Double>, vertex: SIMD3<Double>, second: SIMD3<Double>)
- Parameters:
first— first arm point;vertex— the angle’s apex;second— second arm point. - Returns:
nilif the points are collinear or construction fails. - OCCT:
PrsDim_AngleDimension(gp_Pnt p1, gp_Pnt center, gp_Pnt p2). - Example:
let dim = AngleDimension( first: SIMD3(1, 0, 0), vertex: SIMD3(0, 0, 0), second: SIMD3(0, 1, 0)) print(dim?.degrees) // Optional(90.0)
AngleDimension.init?(face1:face2:)
Creates an angle dimension between two planar faces.
public init?(face1: Shape, face2: Shape)
- Parameters:
face1,face2—Shapevalues wrapping planarTopoDS_Facesub-shapes. - Returns:
nilif either face is non-planar or construction fails. - OCCT:
PrsDim_AngleDimension(TopoDS_Face, TopoDS_Face).
value
The measured angle in radians.
public var value: Double { get }
- OCCT:
PrsDim_Dimension::GetValue().
degrees
The measured angle converted to degrees.
public var degrees: Double { get }
Pure-Swift: value * 180.0 / .pi.
- Example:
let dim = AngleDimension(first: SIMD3(1,0,0), vertex: .zero, second: SIMD3(0,1,0))! print(dim.degrees) // 90.0
isValid
Whether the dimension is valid.
public var isValid: Bool { get }
- OCCT:
PrsDim_Dimension::IsValid().
setCustomValue(_:)
Overrides the displayed angle (in radians).
public func setCustomValue(_ value: Double)
- Parameters:
value— custom angle in radians. - OCCT:
PrsDim_Dimension::SetCustomValue(Standard_Real).
geometry
Geometry data for Metal rendering.
public var geometry: DimensionGeometry? { get }
- Returns:
DimensionGeometrywithcenterPointat the angle vertex andvaluein radians;nilon failure. - OCCT:
PrsDim_AngleDimension::FirstPoint(),SecondPoint(),CenterPoint(),GetValue().
DiameterDimension
Measures the diameter of circular geometry (edge, arc, or cylindrical face).
DiameterDimension.init?(shape:)
Creates a diameter dimension from a shape with circular geometry.
public init?(shape: Shape)
- Parameters:
shape— aShapewrapping circular geometry. - Returns:
nilif the shape does not contain circular geometry or construction fails. - OCCT:
PrsDim_DiameterDimension(TopoDS_Shape). - Example:
let cyl = Shape.cylinder(radius: 5, height: 10)! if let dim = DiameterDimension(shape: cyl) { print(dim.value) // ≈ 10.0 }
value
The measured diameter.
public var value: Double { get }
- OCCT:
PrsDim_Dimension::GetValue().
isValid
Whether the dimension is valid.
public var isValid: Bool { get }
- OCCT:
PrsDim_Dimension::IsValid().
setCustomValue(_:)
Overrides the displayed diameter with a custom value.
public func setCustomValue(_ value: Double)
- OCCT:
PrsDim_Dimension::SetCustomValue(Standard_Real).
geometry
Geometry data for Metal rendering.
public var geometry: DimensionGeometry? { get }
- Returns:
DimensionGeometrywithcenterPointat the circle center,circleRadiusset, andvalueequal to the diameter;nilon failure. - OCCT:
PrsDim_DiameterDimension::Circle(),GetValue().
TextLabel
A positioned 3D text annotation — a label with a location, string content, and optional character height.
TextLabel.init?(text:position:)
Creates a text label at a 3D position.
public init?(text: String, position: SIMD3<Double>)
- Parameters:
text— label string;position— 3D anchor point in model space. - Returns:
nilif construction fails (e.g. empty string or bridge allocation error). - OCCT: Internal
OCCTTextLabelstruct — stores string +gp_Pntposition. - Example:
if let label = TextLabel(text: "Datum A", position: SIMD3(0, 0, 10)) { label.setHeight(2.0) print(label.text) }
text
The label string. Readable and writable.
public var text: String { get set }
- OCCT (get):
OCCTTextLabelGetInfo→ copies the stored C string. - OCCT (set):
OCCTTextLabelSetText→ replaces the stored string. - Note: Returns
""if the info retrieval fails.
position
The 3D anchor point of the label. Readable and writable.
public var position: SIMD3<Double> { get set }
- OCCT (get):
OCCTTextLabelGetInfo→ reads the storedgp_Pnt. - OCCT (set):
OCCTTextLabelSetPosition(x:y:z:). - Returns:
.zeroif info retrieval fails.
setHeight(_:)
Sets the character height for rendering the label text.
public func setHeight(_ height: Double)
- Parameters:
height— character height in model units. - OCCT:
OCCTTextLabelSetHeight. - Example:
label.setHeight(3.5)
PointCloud
A colored point set for 3D visualization, backed by packed coordinate / color buffers.
PointCloud.init?(points:) (uncolored)
Creates a point cloud from an array of 3D positions without per-point colors.
public init?(points: [SIMD3<Double>])
- Parameters:
points— array of 3D positions. - Returns:
nilifpointsis empty or allocation fails. - OCCT:
OCCTPointCloudCreate(const double*, int32_t)— packs XYZ into a flatDoublebuffer. - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(1,0,0), SIMD3(0,1,0)] if let cloud = PointCloud(points: pts) { print(cloud.count) // 3 }
PointCloud.init?(points:colors:) (colored)
Creates a colored point cloud with per-point RGB values.
public init?(points: [SIMD3<Double>], colors: [SIMD3<Float>])
- Parameters:
points— 3D positions;colors— per-point RGB values with components in [0, 1]. Must have the same count aspoints. - Returns:
nilifpoints.count != colors.countor allocation fails. - OCCT:
OCCTPointCloudCreateColored(const double*, const float*, int32_t). - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(1,0,0)] let cols: [SIMD3<Float>] = [SIMD3(1,0,0), SIMD3(0,1,0)] if let cloud = PointCloud(points: pts, colors: cols) { print(cloud.colors.count) // 2 }
count
Number of points in the cloud.
public var count: Int { get }
- OCCT:
OCCTPointCloudGetCount.
bounds
Axis-aligned bounding box of the point cloud.
public var bounds: (min: SIMD3<Double>, max: SIMD3<Double>)? { get }
- Returns: Tuple of min/max corners, or
nilif the cloud is empty or bounds computation fails. - OCCT:
OCCTPointCloudGetBounds— iterates stored points to compute AABB. - Example:
if let cloud = PointCloud(points: [SIMD3(0,0,0), SIMD3(5,5,5)]), let bb = cloud.bounds { print(bb.min, bb.max) // (0,0,0) (5,5,5) }
points
All point positions as an array.
public var points: [SIMD3<Double>] { get }
- Returns: Array of 3D positions in insertion order. Returns
[]if the cloud is empty. - OCCT:
OCCTPointCloudGetPoints(cloud, buffer, count)— copies the packed buffer into the returned array.
colors
All per-point colors as an array.
public var colors: [SIMD3<Float>] { get }
- Returns: Array of RGB colors matching each point;
[]if the cloud was created without colors. - OCCT:
OCCTPointCloudGetColors(cloud, buffer, count)— copies the packed Float buffer; returns empty if no color data is stored.
Document Extensions — GD&T Enums & Structs
These types are declared as extensions on Document in GDTWrite.swift and encode the STEP AP242 GD&T vocabulary.
Document.DimensionType
Maps OCCT’s XCAFDimTolObjects_DimensionType — the 32 dimension sub-types that STEP AP242 dimensions can carry.
public enum DimensionType: Int32, Sendable, CaseIterable {
case locationNone = 0
case locationCurvedDistance = 1
case locationLinearDistance = 2
case locationLinearDistanceFromCenterToOuter = 3
case locationLinearDistanceFromCenterToInner = 4
case locationLinearDistanceFromOuterToCenter = 5
case locationLinearDistanceFromOuterToOuter = 6
case locationLinearDistanceFromOuterToInner = 7
case locationLinearDistanceFromInnerToCenter = 8
case locationLinearDistanceFromInnerToOuter = 9
case locationLinearDistanceFromInnerToInner = 10
case locationAngular = 11
case locationOriented = 12
case locationWithPath = 13
case sizeCurveLength = 14
case sizeDiameter = 15
case sizeSphericalDiameter = 16
case sizeRadius = 17
case sizeSphericalRadius = 18
case sizeToroidalMinorDiameter = 19
case sizeToroidalMajorDiameter = 20
case sizeToroidalMinorRadius = 21
case sizeToroidalMajorRadius = 22
case sizeToroidalHighMajorDiameter = 23
case sizeToroidalLowMajorDiameter = 24
case sizeToroidalHighMajorRadius = 25
case sizeToroidalLowMajorRadius = 26
case sizeThickness = 27
case sizeAngular = 28
case sizeWithPath = 29
case commonLabel = 30
case dimensionPresentation = 31
}
Raw values match XCAFDimTolObjects_DimensionType integer codes directly.
Document.GeomToleranceType
Maps OCCT’s XCAFDimTolObjects_GeomToleranceType — the 16 ASME / ISO geometric tolerance classes.
public enum GeomToleranceType: Int32, Sendable, CaseIterable {
case none = 0
case angularity = 1
case circularRunout = 2
case circularityOrRoundness = 3
case coaxiality = 4
case concentricity = 5
case cylindricity = 6
case flatness = 7
case parallelism = 8
case perpendicularity = 9
case position = 10
case profileOfLine = 11
case profileOfSurface = 12
case straightness = 13
case symmetry = 14
case totalRunout = 15
}
Raw values match XCAFDimTolObjects_GeomToleranceType integer codes.
Document.Dimension
Typed snapshot of a dimension read from or created on a Document.
public struct Dimension: Sendable, Hashable {
public let type: DimensionType
public let value: Double
public let lowerTolerance: Double
public let upperTolerance: Double
public let index: Int
}
type— the STEP AP242 dimension sub-type.value— nominal value (model units).lowerTolerance— lower tolerance bound (may be 0 if not set).upperTolerance— upper tolerance bound (may be 0 if not set).index— position in the document’s dimension sequence (for use withsetDimensionTolerance(at:lower:upper:)).
Document.GeomTolerance
Typed snapshot of a geometric tolerance entry.
public struct GeomTolerance: Sendable, Hashable {
public let type: GeomToleranceType
public let value: Double
public let index: Int
}
type— ASME / ISO tolerance class.value— tolerance zone value in model units.index— position in the document’s geom-tolerance sequence.
Document.Datum
Typed snapshot of a datum reference.
public struct Datum: Sendable, Hashable {
public let name: String
public let index: Int
}
name— datum label string (e.g."A","B").index— position in the document’s datum sequence.
Document Extensions — Typed Read Path
These methods provide type-safe access to GD&T objects stored in a Document, complementing the raw Int32-returning read path in Document.swift.
typedDimension(at:)
Returns the typed dimension at a given index.
public func typedDimension(at index: Int) -> Dimension?
- Parameters:
index— zero-based index within the document’s dimension label sequence. - Returns:
Dimensionif the label exists and itsXCAFDimTolObjects_DimensionTypemaps to a knownDimensionTypecase;nilotherwise. - OCCT:
XCAFDoc_DimTolTool::GetDimensionLabels→XCAFDoc_Dimension::GetObject()→XCAFDimTolObjects_DimensionObject::GetType()/GetValues()/GetLowerTolValue()/GetUpperTolValue(). - Example:
for i in 0..<doc.dimensionCount { if let dim = doc.typedDimension(at: i) { print(dim.type, dim.value) } }
typedGeomTolerance(at:)
Returns the typed geometric tolerance at a given index.
public func typedGeomTolerance(at index: Int) -> GeomTolerance?
- Parameters:
index— zero-based index within the document’s geom-tolerance label sequence. - Returns:
GeomToleranceif the label exists and its type maps to a knownGeomToleranceTypecase;nilotherwise. - OCCT:
XCAFDoc_DimTolTool::GetGeomToleranceLabels→XCAFDoc_GeomTolerance::GetObject()→XCAFDimTolObjects_GeomToleranceObject::GetType()/GetValue(). - Example:
for i in 0..<doc.geomToleranceCount { if let tol = doc.typedGeomTolerance(at: i) { print(tol.type, tol.value) } }
typedDatum(at:)
Returns the typed datum at a given index.
public func typedDatum(at index: Int) -> Datum?
- Parameters:
index— zero-based index within the document’s datum label sequence. - Returns:
Datumwrapping the datum name and index;nilif the label does not exist. - OCCT: Delegates to
Document.datum(at:)→XCAFDoc_DimTolTool::GetDatumLabels→XCAFDoc_Datum::GetObject()→XCAFDimTolObjects_DatumObject::GetName().
typedDimensions
All typed dimensions in the document.
public var typedDimensions: [Dimension] { get }
- Returns: Array of all
Dimensionvalues for whichtypedDimension(at:)succeeds. - Example:
let dims = doc.typedDimensions let diameters = dims.filter { $0.type == .sizeDiameter }
typedGeomTolerances
All typed geometric tolerances in the document.
public var typedGeomTolerances: [GeomTolerance] { get }
- Returns: Array of all
GeomTolerancevalues for whichtypedGeomTolerance(at:)succeeds.
typedDatums
All typed datums in the document.
public var typedDatums: [Datum] { get }
- Returns: Array of all
Datumvalues for whichtypedDatum(at:)succeeds.
Document Extensions — Write Path
Methods that author new GD&T objects on the document for round-trip through STEP AP242.
createDimension(on:type:value:lowerTolerance:upperTolerance:)
Creates a new STEP AP242 dimension on the document, attached to a shape label.
@discardableResult
public func createDimension(on shapeLabel: Int64,
type: DimensionType,
value: Double,
lowerTolerance: Double = 0,
upperTolerance: Double = 0) -> Int?
- Parameters:
shapeLabel— label ID of the shape to annotate (fromDocument.labelForShape(_:)or equivalent).type— the dimension sub-type (e.g..sizeDiameter,.locationLinearDistance).value— nominal measured value in model units.lowerTolerance— lower tolerance; omit or pass0to leave unset.upperTolerance— upper tolerance; omit or pass0to leave unset.
- Returns: Zero-based index of the new dimension in the document’s sequence, or
nilon failure. - OCCT:
XCAFDoc_DimTolTool::AddDimension→XCAFDoc_DimTolTool::SetDimension→XCAFDimTolObjects_DimensionObject::SetType/SetValues+ optionallySetLowerTolValue/SetUpperTolValueviasetDimensionTolerance(at:lower:upper:). - Example:
if let shapeLabel = doc.labelForShape(shaft), let idx = doc.createDimension(on: shapeLabel, type: .sizeDiameter, value: 20.0, lowerTolerance: -0.1, upperTolerance: 0.0) { print("Created dimension at index \(idx)") }
createGeomTolerance(on:type:value:)
Creates a new geometric tolerance on the document, attached to a shape label.
@discardableResult
public func createGeomTolerance(on shapeLabel: Int64,
type: GeomToleranceType,
value: Double) -> Int?
- Parameters:
shapeLabel— label ID of the shape to annotate.type— the ASME / ISO tolerance class (e.g..flatness,.perpendicularity).value— tolerance zone size in model units.
- Returns: Zero-based index of the new tolerance in the document’s geom-tolerance sequence, or
nilon failure. - OCCT:
XCAFDoc_DimTolTool::AddGeomTolerance→XCAFDoc_DimTolTool::SetGeomTolerance→XCAFDimTolObjects_GeomToleranceObject::SetType/SetValue. - Example:
if let shapeLabel = doc.labelForShape(face), let idx = doc.createGeomTolerance(on: shapeLabel, type: .flatness, value: 0.05) { print("Flatness tolerance at index \(idx)") }
createDatum(name:)
Creates a new datum reference on the document.
@discardableResult
public func createDatum(name: String) -> Int?
- Parameters:
name— datum identifier string (typically a single letter, e.g."A"). - Returns: Zero-based index of the new datum in the document’s datum sequence, or
nilon failure. - OCCT:
XCAFDoc_DimTolTool::AddDatum→XCAFDimTolObjects_DatumObject::SetName(TCollection_HAsciiString). - Example:
if let idxA = doc.createDatum(name: "A") { print("Datum A at index \(idxA)") }
setDimensionTolerance(at:lower:upper:)
Updates the tolerance bounds on an existing dimension.
@discardableResult
public func setDimensionTolerance(at index: Int,
lower: Double,
upper: Double) -> Bool
- Parameters:
index— zero-based dimension index (as returned bycreateDimensionor used intypedDimension(at:)).lower— lower tolerance value in model units.upper— upper tolerance value in model units.
- Returns:
trueif the update succeeded;falseif the index is out of range or the attribute is missing. - OCCT:
XCAFDoc_DimTolTool::GetDimensionLabels→XCAFDoc_Dimension::GetObject()→XCAFDimTolObjects_DimensionObject::SetLowerTolValue/SetUpperTolValue→XCAFDoc_Dimension::SetObject. - Example:
let idx = doc.createDimension(on: shapeLabel, type: .locationLinearDistance, value: 25.0)! let ok = doc.setDimensionTolerance(at: idx, lower: -0.05, upper: 0.05) #expect(ok)