Drawing & Sheets
The drawing subsystem converts 3D solid geometry into 2D projected views using OCCT’s Hidden Line Removal (HLR) pipeline. Drawing produces exact or polygon-approximate orthographic/perspective views of a Shape and carries dimension and annotation objects. DrawingSheet provides ISO 5457/7200 sheet scaffolding (paper sizes, title blocks, projection symbols). DrawingStyle encodes ISO 128/3098/5455 line-width, text-height, arrow, and scale conventions. DisplayDrawer controls tessellation quality and wireframe rendering via OCCT’s Prs3d_Drawer.
Topics
- Drawing · DrawingError · PaperSize · Orientation · ProjectionAngle · TitleBlock · Sheet · ProjectionSymbol · DrawingLineWidth · DrawingLineStyle extension · DrawingTextHeight · DrawingArrowStyle · DrawingScale · DisplayDrawer
Drawing
A 2D projection of a 3D shape produced by OCCT’s Hidden Line Removal algorithm. Holds visible, hidden, and outline edge compounds plus an in-memory store of attached dimension and annotation objects.
public final class Drawing: @unchecked Sendable
Obtain a Drawing by calling one of the static factory methods (project(_:direction:type:), topView(of:), etc.). The object is memory-managed via OCCTDrawingRef; deinit calls OCCTDrawingRelease.
Enumerations
Drawing.ProjectionType
Projection algorithm applied during HLR computation. The perspective case carries the focal distance, because HLRAlgo_Projector’s perspective constructor requires one and there is no defensible default for it; an enum case with an associated value also means the focal distance cannot be supplied for, and silently ignored by, an orthographic projection.
public enum ProjectionType: Sendable, Hashable {
case orthographic
case perspective(focus: Double)
}
Hashable refines Equatable, so this keys a dictionary or joins a set and compares with ==.
One payload value breaks that contract, and Double is why. .perspective(focus: .nan) is not equal to itself, so it can be inserted into a Set twice, contains answers false for it immediately after insert, and a dictionary read returns nil immediately after the matching write. Drawing.project refuses any focal distance that is not strictly positive, so a .nan case never becomes a drawing, but nothing stops one being constructed and used as a key. The UInt32-backed form had no such value.
It carries no raw type, and cannot: an enum cannot declare one alongside an associated value, and the associated value is what the perspective constructor needs. That is narrower than “cannot be RawRepresentable”, which is not quite true, since a hand-written conformance with some other RawValue compiles. It is not offered, because it could not round-trip the old UInt32 and so would look like the old API while meaning something else.
Before #999 this was UInt32-backed with two payload-free cases, which is three conformances from two different sources: the raw type supplied RawRepresentable, and Equatable and Hashable came free because no case carried anything, in its own module and across a boundary alike. It was not Sendable, and that is a rule about publicness rather than about the payload: a public enum never gets Sendable implicitly, so the old form did not have it inside its own module either. That is the fourth delta and the only one that is a gain. Adding focus: removed the free pair as well as the raw type, so the new form had to declare what it wanted, and it declared Sendable, Equatable. Hashable came back in #1059; the raw type cannot. A caller who was round-tripping through .rawValue switches over the cases instead:
// Was: UInt32(type.rawValue)
let type: Drawing.ProjectionType = .perspective(focus: 50)
let code: UInt32
switch type {
case .orthographic: code = 0
case .perspective: code = 1
}
| Case | Meaning |
|---|---|
orthographic | Parallel-line projection (engineering drawings) |
perspective(focus:) | Converging-line projection from an eye point at focus * direction from the world origin |
Drawing.ProjectionType.perspective(focus:)
Converging-line projection.
The eye is anchored at the world origin, not at the shape. OCCTDrawingCreate builds its projection frame as gp_Ax2(gp_Pnt(0, 0, 0), viewDir) unconditionally, so the eye sits at focus * direction and the picture plane passes through the origin perpendicular to direction. There is no parameter that moves it. A point sitting at coordinate z along direction, again measured from the origin, is scaled by focus / (focus - z), so a longer focal distance converges on the orthographic projection.
Two consequences follow, and neither is what an eye anchored on the shape would give:
- The scale depends on where the shape sits in world space. Take a 10-unit cube and put the eye 40 units from its near face in both cases. Spanning
z0 to 10 withfocus50, it projects at half-width 6.25; spanningz1000 to 1010 withfocus1050, at 131.25. The foreshortening ratio is identical (1.25 in both cases), so no perspective information changes; only the uniform scale does, by the same factor of 21 as the two focal distances. Translate the shape to the origin first if you want the drawing’s scale to be independent of its modelled position. -
focusmust exceed the shape’s reach alongdirection, measured from the origin. A shape at or beyond the eye hasfocus - z <= 0and would be drawn mirrored through the origin, soDrawing.projectreturnsnilfor it instead. The check uses the shape’s axis-aligned bounding box, which bounds the true reach from above, so a rotated or curved shape can be refused slightly before it strictly needs to be. A focal distance that is not strictly positive returnsniltoo. - OCCT:
HLRAlgo_Projector(const gp_Ax2& CS, const double Focus), selected insideOCCTDrawingCreateon theOCCTProjectionTypevalue. The divide isHLRAlgo_Projector::Project’sR = 1 - Z/focus.
Drawing.EdgeType
Selects which class of projected edges to retrieve from a Drawing.
public enum EdgeType: UInt32 {
case visible = 0
case hidden = 1
case outline = 2
}
.visible: sharp and smooth edges not obscured by other geometry (VCompound+Rg1LineVCompound)..hidden: edges behind other geometry (HCompound+Rg1LineHCompound)..outline: silhouette / outline edges (OutLineVCompound/OutLineHCompound).- OCCT:
HLRBRep_HLRToShape/HLRBRep_PolyHLRToShapecompound accessors, selected viaOCCTEdgeTypeinOCCTDrawingGetEdges.
Drawing.EdgeType.visible
Sharp and smooth edges not obscured by other geometry (VCompound + Rg1LineVCompound).
Drawing.EdgeType.hidden
Edges behind other geometry (HCompound + Rg1LineHCompound).
Drawing.EdgeType.outline
Silhouette (outline) edges (OutLineVCompound / OutLineHCompound).
- OCCT:
HLRBRep_HLRToShape/HLRBRep_PolyHLRToShapecompound accessors, selected viaOCCTEdgeTypeinOCCTDrawingGetEdges.
Dimensions and annotations (v0.137, #64)
dimensions
All dimension annotations attached to this drawing.
public var dimensions: [DrawingDimension] { get }
Returns the live contents of the internal DrawingAnnotationStore. Mutations via addLinearDimension(...), addRadialDimension(...), etc. are reflected immediately.
- Example:
let drawing = Drawing.topView(of: myShape)! drawing.addLinearDimension(from: SIMD2(0, 0), to: SIMD2(100, 0)) print(drawing.dimensions.count) // 1
annotations
All non-dimensional annotations (centrelines, centremarks, text, hatches, balloons, cutting-plane lines) attached to this drawing.
public var annotations: [DrawingAnnotation] { get }
- Example:
drawing.addCentreLine(from: SIMD2(50, 0), to: SIMD2(50, 100)) print(drawing.annotations.count) // 1
addLinearDimension(from:to:offset:label:style:id:)
Attaches a linear (distance) dimension between two 2D points.
@discardableResult
public func addLinearDimension(from: SIMD2<Double>, to: SIMD2<Double>,
offset: Double = 10,
label: String? = nil,
style: DrawingLineStyle = .solid,
id: String? = nil) -> DrawingDimension
- Parameters:
from: start point in drawing coordinates (mm).to: end point in drawing coordinates (mm).offset: perpendicular distance of the dimension line from the measured line (default 10 mm).label: optional override text;nilauto-formats the measured distance.style: line style for extension and dimension lines (default.solid).id: optional stable identifier for downstream DXF/SVG export.
- Returns: The appended
DrawingDimension.linear(...)value. - Example:
let drawing = Drawing.topView(of: Shape.box(width: 100, height: 50, depth: 30)!)! drawing.addLinearDimension(from: SIMD2(0, 0), to: SIMD2(100, 0), offset: 15)
addRadialDimension(centre:radius:leaderAngle:label:style:id:)
Attaches a radial dimension (R notation) from the centre of an arc or circle.
@discardableResult
public func addRadialDimension(centre: SIMD2<Double>, radius: Double,
leaderAngle: Double = .pi / 4,
label: String? = nil,
style: DrawingLineStyle = .solid,
id: String? = nil) -> DrawingDimension
- Parameters:
centre: centre point of the arc/circle in drawing coordinates.radius: radius value in drawing units (mm).leaderAngle: angle of the leader line from the positive X axis (default π/4 = 45°).label: optional override;nilauto-formats as “R<value>”.style: line style (default.solid).id: optional stable identifier.
- Returns: The appended
DrawingDimension.radial(...)value. - Example:
drawing.addRadialDimension(centre: SIMD2(50, 50), radius: 25)
addDiameterDimension(centre:radius:leaderAngle:label:style:id:)
Attaches a diameter dimension (⌀ notation) across a circle.
@discardableResult
public func addDiameterDimension(centre: SIMD2<Double>, radius: Double,
leaderAngle: Double = .pi / 4,
label: String? = nil,
style: DrawingLineStyle = .solid,
id: String? = nil) -> DrawingDimension
- Parameters:
centre: centre of the circle in drawing coordinates.radius: radius value (the label shows the diameter, 2 × radius).leaderAngle: leader line angle (default π/4).label: optional override;nilauto-formats as “⌀<diameter>”.style: line style (default.solid).id: optional stable identifier.
- Returns: The appended
DrawingDimension.diameter(...)value. - Example:
drawing.addDiameterDimension(centre: SIMD2(50, 50), radius: 25, leaderAngle: .pi / 6)
addAngularDimension(vertex:ray1:ray2:arcRadius:label:style:id:)
Attaches an angular dimension measuring the angle between two rays from a common vertex.
@discardableResult
public func addAngularDimension(vertex: SIMD2<Double>,
ray1: SIMD2<Double>,
ray2: SIMD2<Double>,
arcRadius: Double = 20,
label: String? = nil,
style: DrawingLineStyle = .solid,
id: String? = nil) -> DrawingDimension
- Parameters:
vertex: vertex point of the angle.ray1: direction of the first ray (as a 2D point fromvertex).ray2: direction of the second ray.arcRadius: radius of the dimension arc drawn between the rays (default 20 mm).label: optional override;nilauto-formats the angle in degrees.style: line style (default.solid).id: optional stable identifier.
- Returns: The appended
DrawingDimension.angular(...)value. - Example:
drawing.addAngularDimension(vertex: SIMD2(50, 50), ray1: SIMD2(1, 0), ray2: SIMD2(0, 1), arcRadius: 15)
addOrdinateDimensions(origin:features:tolerance:id:)
Attaches ISO 129-1 §9.3 ordinate dimensions: X and Y offsets from a shared origin to a set of features.
@discardableResult
public func addOrdinateDimensions(origin: SIMD2<Double>,
features: [(position: SIMD2<Double>, label: String?)],
tolerance: DrawingTolerance = .none,
id: String? = nil) -> DrawingDimension
- Parameters:
origin: the datum origin from which all offsets are measured.features: array of(position, optional label)tuples;nillabels are auto-formatted as the offset value.tolerance: optional tolerance to apply to each offset value (default.none).id: optional stable identifier.
- Returns: The appended
DrawingDimension.ordinate(...)value. - Example:
drawing.addOrdinateDimensions( origin: .zero, features: [ (SIMD2(25, 0), nil), (SIMD2(60, 0), nil), (SIMD2(90, 0), "X3") ] )
addCentreLine(from:to:style:id:)
Attaches a centre-line annotation between two 2D points.
@discardableResult
public func addCentreLine(from: SIMD2<Double>, to: SIMD2<Double>,
style: DrawingLineStyle = .chain,
id: String? = nil) -> DrawingAnnotation
- Parameters:
from: start point.to: end point.style: line style (default.chain, per ISO 128-24 for axes and centre lines).id: optional stable identifier.
- Returns: The appended
DrawingAnnotation.centreline(...)value. - Example:
drawing.addCentreLine(from: SIMD2(50, 0), to: SIMD2(50, 100))
addCentermark(centre:extent:style:id:)
Attaches a centre-mark cross (two perpendicular centre lines) at a point.
@discardableResult
public func addCentermark(centre: SIMD2<Double>, extent: Double = 8,
style: DrawingLineStyle = .chain,
id: String? = nil) -> DrawingAnnotation
- Parameters:
centre: centre of the mark.extent: half-length of each arm of the cross (default 8 mm).style: line style (default.chain).id: optional stable identifier.
- Returns: The appended
DrawingAnnotation.centermark(...)value. - Example:
drawing.addCentermark(centre: SIMD2(50, 50), extent: 6)
addTextLabel(_:at:height:rotation:id:)
Attaches a free text label at a 2D position.
@discardableResult
public func addTextLabel(_ text: String, at position: SIMD2<Double>,
height: Double = 3.5, rotation: Double = 0,
id: String? = nil) -> DrawingAnnotation
- Parameters:
text: the string to display.position: insertion point in drawing coordinates.height: text height in mm (default 3.5 mm, the ISO 3098 standard body height).rotation: rotation angle in radians counter-clockwise (default 0).id: optional stable identifier.
- Returns: The appended
DrawingAnnotation.textLabel(...)value. - Example:
drawing.addTextLabel("SECTION A-A", at: SIMD2(10, 200), height: 5)
addBalloon(itemNumber:at:leaderTo:radius:id:)
Attaches an assembly-drawing balloon callout keyed to a bill-of-materials row.
@discardableResult
public func addBalloon(itemNumber: Int,
at position: SIMD2<Double>,
leaderTo target: SIMD2<Double>? = nil,
radius: Double = 5,
id: String? = nil) -> DrawingAnnotation
- Parameters:
itemNumber: BOM item number displayed inside the balloon circle.position: centre of the balloon circle in drawing coordinates.target: optional leader-line endpoint pointing at the referenced part;nildraws no leader.radius: balloon circle radius in mm (default 5 mm).id: optional stable identifier.
- Returns: The appended
DrawingAnnotation.balloon(...)value. - Example:
drawing.addBalloon(itemNumber: 3, at: SIMD2(120, 80), leaderTo: SIMD2(95, 60), radius: 6)
addCuttingPlaneLine(label:cuttingPlaneOrigin:cuttingPlaneNormal:sectionViewDirection:viewDirection:traceLength:)
Attaches an ISO 128-40 cutting-plane line, projecting the 3D plane into this drawing’s 2D frame.
@discardableResult
public func addCuttingPlaneLine(label: String,
cuttingPlaneOrigin: SIMD3<Double>,
cuttingPlaneNormal: SIMD3<Double>,
sectionViewDirection: SIMD3<Double>,
viewDirection: SIMD3<Double>,
traceLength: Double = 60) -> DrawingAnnotation?
- Parameters:
label: identifier letter(s) shown at the arrow tips (e.g."A").cuttingPlaneOrigin: a 3D point on the cutting plane.cuttingPlaneNormal: normal of the cutting plane in 3D.sectionViewDirection: the direction in which the section view is projected (controls arrow orientation).viewDirection: the projection direction of this parent drawing (used to project the trace into 2D).traceLength: length of the trace line in drawing units (default 60 mm).
- Returns: The appended annotation, or
nilif the cutting plane is parallel to the view plane (trace degenerates to a point). - Note: Pure-Swift: computes the 2D trace via
simd_cross(cuttingPlaneNormal, viewDirection)and projects both trace and arrow direction into the drawing plane. - Example:
if let cpl = drawing.addCuttingPlaneLine( label: "A", cuttingPlaneOrigin: SIMD3(50, 0, 0), cuttingPlaneNormal: SIMD3(1, 0, 0), sectionViewDirection: SIMD3(1, 0, 0), viewDirection: SIMD3(0, 0, 1)) { print("Cutting plane line added") }
addHatch(boundary:angle:spacing:islands:layer:id:)
Attaches ISO 128-50 section-view hatching over a closed boundary polygon.
@discardableResult
public func addHatch(boundary: [SIMD2<Double>],
angle: Double = .pi / 4,
spacing: Double = 3.0,
islands: [[SIMD2<Double>]] = [],
layer: String = "HATCH",
id: String? = nil) -> DrawingAnnotation
- Parameters:
boundary: ordered polygon vertices of the hatch region.angle: hatch line angle in radians (default π/4 = 45°, the ISO 128-50 convention for metals).spacing: distance between hatch lines in mm (default 3 mm per ISO).islands: optional inner boundary polygons excluded from the fill (holes).layer: DXF layer name for the hatch (default"HATCH").id: optional stable identifier.
- Returns: The appended
DrawingAnnotation.hatch(...)value. - Example:
drawing.addHatch(boundary: [SIMD2(10,10), SIMD2(90,10), SIMD2(90,60), SIMD2(10,60)], spacing: 4)
clearAnnotations()
Removes all dimensions and annotations from this drawing.
public func clearAnnotations()
- Example:
drawing.clearAnnotations() print(drawing.dimensions.count) // 0 print(drawing.annotations.count) // 0
Uniform append API (v0.148, #83, #84)
append(_:), DrawingAnnotation
Appends a pre-built annotation to this drawing.
public func append(_ annotation: DrawingAnnotation)
Use to install the result of a static factory (e.g. DrawingAnnotation.surfaceFinish(...), DrawingAnnotation.featureControlFrame(...)).
- Parameters:
annotation, anyDrawingAnnotationcase. - Example:
let sf = DrawingAnnotation.surfaceFinish(...) drawing.append(sf)
append(contentsOf:), [DrawingAnnotation]
Appends a batch of pre-built annotations.
public func append(contentsOf annotations: [DrawingAnnotation])
- Parameters:
annotations, array ofDrawingAnnotationvalues. - Example:
let cosmetics = DrawingAnnotation.cosmeticThreadSideView(...) drawing.append(contentsOf: cosmetics)
append(_:), DrawingDimension
Appends a pre-built dimension.
public func append(_ dimension: DrawingDimension)
- Parameters:
dimension, anyDrawingDimensioncase. - Example:
let dim = DrawingDimension.linear(.init(from: .zero, to: SIMD2(80, 0), offset: 12)) drawing.append(dim)
append(contentsOf:), [DrawingDimension]
Appends a batch of pre-built dimensions.
public func append(contentsOf dimensions: [DrawingDimension])
- Parameters:
dimensions, array ofDrawingDimensionvalues. - Example:
drawing.append(contentsOf: autoDimensions)
Creation
Drawing.project(_:direction:type:)
Creates a 2D projection of a 3D shape using exact HLR.
public static func project(
_ shape: Shape,
direction: SIMD3<Double>,
type: ProjectionType = .orthographic
) -> Drawing?
The underlying algorithm uses HLRBRep_Algo for exact edge-geometry HLR, slower but produces precise curves.
- Parameters:
shape: the 3D shape to project.direction: view direction vector (need not be normalised).type: projection type (default.orthographic), carrying the focal distance in the perspective case.
- Returns:
Drawingcontaining the projected edge compounds, ornilif the shape is null, HLR fails, or a perspective focal distance is not strictly positive. - OCCT:
HLRBRep_Algo+HLRAlgo_Projector+HLRBRep_HLRToShape. - Example:
let box = Shape.box(width: 100, height: 50, depth: 30)! if let view = Drawing.project(box, direction: SIMD3(0, 0, 1)) { let edges = view.visibleEdges } // The same box seen from 50 units away: the near face, 15 units towards the eye, // projects 50/(50-15) = 1.4286x larger than it does orthographically. let near = Drawing.project(box, direction: SIMD3(0, 0, 1), type: .perspective(focus: 50))
Standard Views
Drawing.topView(of:)
Creates a top (plan) view, looking down the −Z axis.
public static func topView(of shape: Shape) -> Drawing?
Shorthand for project(shape, direction: SIMD3(0, 0, 1)).
- Returns:
Drawing?,nilon failure. - Example:
let top = Drawing.topView(of: myShape)
Drawing.frontView(of:)
Creates a front view, looking down the −Y axis.
public static func frontView(of shape: Shape) -> Drawing?
Shorthand for project(shape, direction: SIMD3(0, 1, 0)).
- Returns:
Drawing?,nilon failure. - Example:
let front = Drawing.frontView(of: myShape)
Drawing.sideView(of:)
Creates a right side view, looking down the −X axis.
public static func sideView(of shape: Shape) -> Drawing?
Shorthand for project(shape, direction: SIMD3(1, 0, 0)).
- Returns:
Drawing?,nilon failure. - Example:
let side = Drawing.sideView(of: myShape)
Drawing.isometricView(of:)
Creates an isometric view, looking from direction (1, 1, 1) / √3.
public static func isometricView(of shape: Shape) -> Drawing?
- Returns:
Drawing?,nilon failure. - Example:
let iso = Drawing.isometricView(of: myShape)
Fast Polygon-Based Projection (v0.39.0)
Drawing.projectFast(_:direction:deflection:)
Creates a fast polygon-based 2D projection using triangulation HLR.
public static func projectFast(
_ shape: Shape,
direction: SIMD3<Double>,
deflection: Double = 0.01
) -> Drawing?
Uses BRepMesh_IncrementalMesh + HLRBRep_PolyAlgo, significantly faster than exact HLR but the edges follow the tessellation facets rather than the true curves. Suitable for interactive previews.
Orthographic only, and deliberately without a type: parameter: HLRBRep_PolyAlgo stores a projector’s perspective flag but never acts on it, so its output is identical for HLRAlgo_Projector(cs) and HLRAlgo_Projector(cs, focus) at every focal distance. Use Drawing.project(_:direction:type:) for a perspective view.
- Parameters:
shape: the 3D shape to project.direction: view direction vector.deflection: triangulation chord deflection (default 0.01); smaller = more accurate, slower.
- Returns:
Drawing?,nilon failure. - OCCT:
BRepMesh_IncrementalMesh+HLRBRep_PolyAlgo+HLRBRep_PolyHLRToShape. - Example:
let preview = Drawing.projectFast(myShape, direction: SIMD3(0, 0, 1), deflection: 0.05)
Drawing.fastTopView(of:deflection:)
Creates a fast polygon-based top view.
public static func fastTopView(of shape: Shape, deflection: Double = 0.01) -> Drawing?
Shorthand for projectFast(shape, direction: SIMD3(0, 0, 1), deflection: deflection).
- Returns:
Drawing?,nilon failure. - Example:
let top = Drawing.fastTopView(of: myShape, deflection: 0.02)
Drawing.fastIsometricView(of:deflection:)
Creates a fast polygon-based isometric view.
public static func fastIsometricView(of shape: Shape, deflection: Double = 0.01) -> Drawing?
Shorthand for projectFast(shape, direction: SIMD3(1,1,1)/√3, deflection: deflection).
- Returns:
Drawing?,nilon failure. - Example:
let iso = Drawing.fastIsometricView(of: myShape)
Edge Access
edges(ofType:)
Returns the projected edges of the given type as a compound Shape.
public func edges(ofType type: EdgeType) -> Shape?
- Parameters:
type,.visible,.hidden, or.outline. - Returns: A
Shapewrapping the compound of matching edges, ornilif none are present. - OCCT:
OCCTDrawingGetEdges, selects the appropriateHLRBRep_HLRToShapecompounds. - Example:
if let visible = drawing.edges(ofType: .visible) { // visible is a compound Shape of all non-hidden edges }
visibleEdges
Shorthand for edges(ofType: .visible).
public var visibleEdges: Shape? { get }
- Returns: Compound
Shapeof visible edges, ornil. - Example:
let vis = drawing.visibleEdges
hiddenEdges
Shorthand for edges(ofType: .hidden).
public var hiddenEdges: Shape? { get }
- Returns: Compound
Shapeof hidden edges, ornil. - Example:
let hid = drawing.hiddenEdges
outlineEdges
Shorthand for edges(ofType: .outline).
public var outlineEdges: Shape? { get }
- Returns: Compound
Shapeof outline/silhouette edges, ornil. - Example:
let outline = drawing.outlineEdges
DrawingError
Error type for 2D drawing operations.
public enum DrawingError: Error, LocalizedError {
case projectionFailed
public var errorDescription: String? { get }
}
DrawingError.projectionFailed
Thrown (or surfaced as a nil return) when OCCTDrawingCreate cannot construct the HLR projection.
case projectionFailed
errorDescriptionreturns"Failed to create 2D projection".
DrawingError.errorDescription
LocalizedError conformance: a human-readable message for the current case.
public var errorDescription: String? { get }
- Returns:
"Failed to create 2D projection"for.projectionFailed(the only case).
PaperSize
ISO 5457 paper size enumeration.
public enum PaperSize: String, Sendable, Hashable, CaseIterable {
case a0, a1, a2, a3, a4
}
PaperSize.dimensions
ISO 5457 trimmed-sheet dimensions in mm for landscape orientation.
public var dimensions: SIMD2<Double> { get }
- A0 → (1189, 841), A1 → (841, 594), A2 → (594, 420), A3 → (420, 297), A4 → (297, 210).
- Example:
let w = PaperSize.a3.dimensions.x // 420.0
PaperSize.size(in:)
Returns the sheet dimensions for the given orientation.
public func size(in orientation: Orientation) -> SIMD2<Double>
- Parameters:
orientation,.landscapeor.portrait; portrait swaps X and Y. - Returns: Width × Height in mm.
- Example:
let sz = PaperSize.a4.size(in: .portrait) // (210, 297)
Orientation
Drawing sheet orientation.
public enum Orientation: String, Sendable, Hashable {
case landscape
case portrait
}
Not to be confused with Shape.Orientation (forward/reversed B-Rep topology orientation, declared in Shape+Topology.swift): this is a distinct, top-level enum for sheet layout only.
| Case | Meaning |
|---|---|
landscape | Sheet wider than tall (the default; see PaperSize.dimensions) |
portrait | Sheet taller than wide (PaperSize.size(in:) swaps X and Y) |
Orientation.portrait
Sheet taller than wide; PaperSize.size(in:) swaps X and Y to produce it.
ProjectionAngle
ISO 5456-2 first-angle vs. third-angle projection convention.
public enum ProjectionAngle: String, Sendable, Hashable {
case first // ISO / Europe: top view below front view
case third // ANSI / USA: top view above front view
}
Used by Sheet and rendered by ProjectionSymbol.render(_:at:into:).
| Case | Meaning |
|---|---|
first | First-angle projection (ISO / Europe): the top view is placed below the front view. |
third | Third-angle projection (ANSI / USA): the top view is placed above the front view. |
(Per-case anchors below, for cross-reference; the table above has the actual meaning of each.)
third
TitleBlock
ISO 7200 title block data. Rendered into the bottom-right of the drawable frame by Sheet.render(into:).
public struct TitleBlock: Sendable, Hashable
TitleBlock.init(title:drawingNumber:owner:creator:approver:documentType:dateOfIssue:revision:sheetNumber:language:material:weight:scale:)
public init(title: String,
drawingNumber: String? = nil,
owner: String? = nil,
creator: String? = nil,
approver: String? = nil,
documentType: String? = nil,
dateOfIssue: String? = nil,
revision: String? = nil,
sheetNumber: String? = nil,
language: String? = nil,
material: String? = nil,
weight: String? = nil,
scale: String? = nil)
All fields except title are optional. dateOfIssue should follow ISO 8601. scale overrides the sheet-level scale in the title-block cell.
- Parameters: ISO 7200 mandatory and optional fields, see inline comments in source.
- Example:
let tb = TitleBlock( title: "Mounting Bracket", drawingNumber: "MB-042", creator: "J. Smith", dateOfIssue: "2026-06-21", revision: "B", scale: "1:2" )
Stored properties of TitleBlock
All stored as var so they can be mutated after construction.
| Property | Type | ISO 7200 status |
|---|---|---|
title | String | mandatory |
drawingNumber | String? | optional |
owner | String? | optional |
creator | String? | optional |
approver | String? | optional |
documentType | String? | optional |
dateOfIssue | String? | optional |
revision | String? | optional |
sheetNumber | String? | optional |
language | String? | optional |
material | String? | optional |
weight | String? | optional |
scale | String? | optional |
TitleBlock.language
The language the drawing is authored in, an ISO 7200 optional field.
Sheet
ISO 5457/7200 drawing sheet scaffolding. Renders a trimmed outer edge, inner drawable frame, centring marks, title block, and projection-angle symbol.
public struct Sheet: Sendable, Hashable
Sheet.init(size:orientation:projection:title:scale:)
public init(size: PaperSize,
orientation: Orientation = .landscape,
projection: ProjectionAngle = .first,
title: TitleBlock? = nil,
scale: String = "1:1")
- Parameters:
size: ISO 5457 paper size.orientation: landscape (default) or portrait.projection: first-angle (ISO/Europe) or third-angle (ANSI/USA) convention.title: optional ISO 7200 title block data; passnilto omit the title block.scale: drawing scale string displayed in the title block (default"1:1").
- Example:
let sheet = Sheet(size: .a3, orientation: .landscape, projection: .first, title: TitleBlock(title: "Bracket", drawingNumber: "B-001"), scale: "1:2")
Sheet.size
The paper size.
public var size: PaperSize
Sheet.orientation
Landscape or portrait orientation.
public var orientation: Orientation
Sheet.projection
ISO 5456-2 projection-angle convention.
public var projection: ProjectionAngle
Sheet.title
Optional ISO 7200 title block.
public var title: TitleBlock?
Sheet.scale
Scale string shown in the title block (e.g. "1:2", "2:1").
public var scale: String
Sheet.dimensions
Overall sheet dimensions in mm for the current size and orientation.
public var dimensions: SIMD2<Double> { get }
Delegates to size.size(in: orientation).
- Example:
let sheet = Sheet(size: .a3) let w = sheet.dimensions.x // 420.0
Sheet.inset
ISO 5457 border insets in mm: binding margin on the left, 10 mm on other sides for A0–A4.
public var inset: (left: Double, right: Double, top: Double, bottom: Double) { get }
- Returns: Tuple
(left: 20, right: 10, top: 10, bottom: 10)for all sizes (A0–A4). - Example:
let ins = Sheet(size: .a3).inset // (left: 20, right: 10, top: 10, bottom: 10)
Sheet.innerFrame
The inner drawable rectangle corners in sheet coordinates.
public var innerFrame: (min: SIMD2<Double>, max: SIMD2<Double>) { get }
- Returns:
min=(inset.left, inset.bottom),max=(width − inset.right, height − inset.top). - Example:
let frame = Sheet(size: .a3).innerFrame // frame.min = (20, 10), frame.max = (410, 287)
Sheet.render(into:)
Renders the sheet border, centring marks, title block, and projection symbol into a DXFWriter, PDFWriter, or SVGWriter.
public func render(into writer: DXFWriter)
public func render(into writer: PDFWriter)
public func render(into writer: SVGWriter)
public func render(into writer: DrawingWriter)
Writes to layers "BORDER", "CENTER", "TITLE", and "TEXT". Outer sheet edge and inner frame are polylines; centring marks are short lines at midpoints of each frame edge; the title block is a 170 × 55 mm rectangle in the bottom-right with ISO 7200 fields; the projection symbol is rendered by ProjectionSymbol.render(_:at:into:) above the title block.
Three overloads, one per writer type, sharing one internal implementation (#1180) – before this the scaffolding only rendered onto a DXFWriter, so a PDFWriter/SVGWriter sheet got no border, title block, or projection symbol at all, even though the body only calls the five primitives every writer implements identically. A fourth overload (#1267) accepts the public DrawingWriter protocol directly, for a caller that only knows which writer to use at runtime – it dispatches to whichever of the three concrete overloads matches the value’s actual type, rather than a second copy of the render body (DrawingWriter and the shared internal implementation’s own protocol are declared independently, so a DrawingWriter value has no static path into that internal-typed body).
- Parameters:
writer, aDXFWriter,PDFWriter,SVGWriter, or anyDrawingWriterthat accumulates geometry for eventual file output. - Note: Pure-Swift; no OCCT bridge call.
- Example:
let sheet = Sheet(size: .a3, title: TitleBlock(title: "Part A")) let writer = DXFWriter() sheet.render(into: writer) // Now add view geometry into writer, then call writer.write(to:) // Or, composing a PDF the same way: try Exporter.writePDF(sheet: sheet, body: { pdf in sheet.render(into: pdf) }, to: URL(fileURLWithPath: "/tmp/part-a.pdf"))
ProjectionSymbol
Namespace for rendering ISO 5456-2 first/third-angle projection symbols into a DXFWriter, PDFWriter, or SVGWriter.
public enum ProjectionSymbol
ProjectionSymbol.render(_:at:into:)
Renders a projection-angle symbol at a 2D origin.
public static func render(_ angle: ProjectionAngle,
at origin: SIMD2<Double>,
into writer: DXFWriter)
public static func render(_ angle: ProjectionAngle,
at origin: SIMD2<Double>,
into writer: PDFWriter)
public static func render(_ angle: ProjectionAngle,
at origin: SIMD2<Double>,
into writer: SVGWriter)
public static func render(_ angle: ProjectionAngle,
at origin: SIMD2<Double>,
into writer: DrawingWriter)
The symbol is approximately 30 × 15 mm. First-angle: truncated-cone view on the left, circle on the right. Third-angle: circle on the left, truncated-cone on the right.
Three overloads, one per writer type, sharing one internal implementation (#1180) – previously DXFWriter-only, so Sheet.render(into:) could emit the symbol onto a PDF/SVG sheet’s DXF writer but not onto the PDF/SVG writer it was actually building. A fourth overload (#1267) accepts the public DrawingWriter protocol directly and dispatches to whichever concrete overload matches, the same pattern and for the same reason as Sheet.render(into: DrawingWriter) above.
- Parameters:
angle:.first(ISO) or.third(ANSI).origin: bottom-left origin of the symbol bounding box in drawing coordinates.writer: theDXFWriter,PDFWriter,SVGWriter, orDrawingWriterto emit lines and circles into (layer"TEXT").
- Note: Pure-Swift; no OCCT bridge call. Called automatically by
Sheet.render(into:). - Example:
let writer = DXFWriter() ProjectionSymbol.render(.first, at: SIMD2(370, 20), into: writer)
DrawingLineWidth
ISO 128-20 standard line-width values in mm.
public enum DrawingLineWidth: Double, Sendable, Hashable, CaseIterable {
case w013 = 0.13
case w018 = 0.18
case w025 = 0.25
case w035 = 0.35
case w050 = 0.50
case w070 = 0.70
case w100 = 1.00
case w140 = 1.40
case w200 = 2.00
}
Only these values are recognised by ISO-compliant DXF/SVG readers. The geometric series increments by ≈ 1.4× per tier.
DrawingLineWidth.w013
0.13 mm.
DrawingLineWidth.w018
0.18 mm.
DrawingLineWidth.w025
0.25 mm; the ISO-standard thin weight (see thin below).
DrawingLineWidth.w035
0.35 mm.
DrawingLineWidth.w050
0.50 mm; the ISO-standard thick weight (see thick below).
DrawingLineWidth.w070
0.70 mm.
DrawingLineWidth.w100
1.00 mm.
DrawingLineWidth.w140
1.40 mm.
DrawingLineWidth.w200
2.00 mm.
DrawingLineWidth.thin
ISO-standard thin weight for general drawing features (0.25 mm).
public static let thin: DrawingLineWidth = .w025
DrawingLineWidth.thick
ISO-standard thick weight, 2× thin (0.50 mm).
public static let thick: DrawingLineWidth = .w050
DrawingLineStyle.defaultWidth / .boldWidth (removed, #1170): a second, disagreeing ISO 128-20 width table. The line width a rendered drawing actually uses is the internal strokeWidthMM(for:) table in DrawingDispatch.swift; these two computed properties were dead code nothing called.
DrawingTextHeight
ISO 3098 standard text-height series in mm.
public enum DrawingTextHeight: Double, Sendable, Hashable, CaseIterable {
case h25 = 2.5
case h35 = 3.5
case h50 = 5.0
case h70 = 7.0
case h100 = 10.0
case h140 = 14.0
case h200 = 20.0
}
Each tier is ≈ 1.4× the previous, matching the ISO geometric series.
| Case | Text height (mm) |
|---|---|
h25 | 2.5 |
h35 | 3.5 |
h50 | 5.0 |
h70 | 7.0 |
h100 | 10.0 |
h140 | 14.0 |
h200 | 20.0 |
(Per-case anchors below, for cross-reference; the table above has the actual meaning of each.)
h200
DrawingTextHeight.recommended(forPaper:)
Returns the recommended dimension-text height for a given ISO 5457 paper size.
public static func recommended(forPaper paper: String) -> DrawingTextHeight
- Parameters:
paper, paper size string, e.g."A3"(case-insensitive). - Returns:
.h50for A0/A1;.h35for A2, A3, A4, and any unrecognised size. - Example:
let h = DrawingTextHeight.recommended(forPaper: "A0") // .h50
DrawingTextHeight.snap(_:)
Snaps an arbitrary height in mm to the nearest ISO 3098 tier.
public static func snap(_ mm: Double) -> DrawingTextHeight
- Parameters:
mm, desired text height in mm. - Returns: The
DrawingTextHeightcase whoserawValueis closest tomm; falls back to.h35if the cases are empty. - Example:
let h = DrawingTextHeight.snap(4.0) // .h35 (3.5 is closer than 5.0)
DrawingArrowStyle
ISO 128-21 arrow-head conventions.
public enum DrawingArrowStyle: String, Sendable, Hashable, Codable {
case filledClosed // solid triangle. ISO default
case openClosed90 // stroked triangle, 90° included angle
case openClosed30 // stroked triangle, 30° included angle (narrow)
case tick // 45° tick (architectural; not ISO default)
}
DrawingArrowStyle.filledClosed
A solid, filled triangular arrowhead; the ISO 128-21 default.
DrawingArrowStyle.openClosed90
A stroked (unfilled) triangular arrowhead with a 90 degree included angle.
DrawingArrowStyle.openClosed30
A stroked (unfilled) triangular arrowhead with a narrow 30 degree included angle.
DrawingArrowStyle.tick
A 45 degree tick mark; architectural convention, not an ISO 128-21 default.
DrawingArrowStyle.length(forLineWidth:)
Recommended arrow length in mm for a given dimension line width.
public func length(forLineWidth width: DrawingLineWidth) -> Double
Returns width.rawValue * 6, approximately 1.5 mm at the standard thin width of 0.25 mm, within the ISO 129 recommendation of 3–5× thin width.
- Parameters:
width, the dimension line’sDrawingLineWidth. - Returns: Arrow length in mm.
- Example:
let len = DrawingArrowStyle.filledClosed.length(forLineWidth: .thin) // 0.25 * 6 = 1.5
DrawingScale
ISO 5455 preferred drawing scales.
public enum DrawingScale: Sendable, Hashable {
case one // 1:1
case reduction(Int) // 1:N (N > 1)
case enlargement(Int) // N:1 (N > 1)
case custom(Double) // any ratio
}
| Case | Ratio | Meaning |
|---|---|---|
one | 1:1 | Full scale |
reduction(N) | 1:N, N > 1 | Drawing smaller than the model |
enlargement(N) | N:1, N > 1 | Drawing larger than the model |
custom(f) | f:1 | Any other ratio, e.g. a non-ISO-preferred value |
DrawingScale.custom(_:)
Any other scale ratio not covered by .one, .reduction, or .enlargement.
DrawingScale.factor
Drawing-to-model scale factor.
public var factor: Double { get }
.one→ 1.0;.reduction(2)→ 0.5;.enlargement(5)→ 5.0;.custom(f)→f.- Example:
let f = DrawingScale.reduction(2).factor // 0.5
DrawingScale.label
Human-readable scale label.
public var label: String { get }
.one→"1:1",.reduction(5)→"1:5",.enlargement(2)→"2:1",.custom(0.333)→"0.333:1".- Example:
let s = DrawingScale.enlargement(10).label // "10:1"
DrawingScale.preferred
ISO 5455 preferred scale values.
public static var preferred: [DrawingScale] { get }
Returns the 15 ISO-standard scales from 50:1 down to 1:1000, plus 1:1.
- Example:
for scale in DrawingScale.preferred { print(scale.label) }
DisplayDrawer
Display attribute controller wrapping OCCT’s Prs3d_Drawer. Controls tessellation quality and which edge types are emitted by a Metal renderer.
public final class DisplayDrawer: @unchecked Sendable
Obtain by calling DisplayDrawer(). The underlying Prs3d_Drawer handle is created in OCCTDrawerCreate and destroyed in deinit via OCCTDrawerDestroy.
DisplayDrawer.DeflectionType
public enum DeflectionType: Int32, Sendable {
case relative = 0
case absolute = 1
}
.relative: chordal deviation is derived from the shape’s own size, seedeviationCoefficientfor the exact formula..absolute: chordal deviation is a fixed distance in model units.
DisplayDrawer.DeflectionType.relative
Chordal deviation is derived from the shape’s own bounding box rather than given as a distance.
.absolute: chordal deviation is a fixed distance in model units.
DisplayDrawer.init()
Creates a DisplayDrawer with OCCT default settings.
public init()
- OCCT:
Prs3d_Drawerdefault constructor viaOCCTDrawerCreate. - Example:
let drawer = DisplayDrawer() drawer.deviationCoefficient = 0.0005 // finer tessellation
Tessellation Quality
deviationCoefficient
Chordal deviation coefficient, scaled by the shape’s own size when deflectionType is .relative.
public var deviationCoefficient: Double { get set }
Lower values produce finer tessellation. Default ≈ 0.001. Only applies when deflectionType is .relative.
The absolute deflection Shape.shadedMesh(drawer:) and Shape.edgeMesh(drawer:) hand to BRepMesh_IncrementalMesh is Prs3d::GetDeflection’s, which is
max(longest bounding-box side * deviationCoefficient * 4, Precision::Confusion())
So a 10-unit cube at the default 0.001 tessellates to 0.04, not to 0.001 and not to the bounding-box diagonal times the coefficient. This page and DisplayDrawer/deviationCoefficient’s own doc comment both said “diagonal” until #1399 measured Prs3d::GetDeflection against the pinned kernel (Scripts/repro/1399-refman-coverage-unlaned/probe_foundation.mm): 0.004 for a 1-unit cube, 0.04 for 10, 0.4 for 100, all at coefficient 0.001. Prs3d_Drawer.hxx’s own comment, “SizeOfObject * DeviationCoefficient”, is where the diagonal reading came from, and it does not say diagonal either.
- OCCT:
Prs3d_Drawer::DeviationCoefficient/SetDeviationCoefficient, consumed throughPrs3d::GetDeflection. - Example:
drawer.deviationCoefficient = 0.0002 // high-quality render
deviationAngle
Angular deviation in radians controlling curved surface approximation.
public var deviationAngle: Double { get set }
Default ≈ 0.35 radians (20°). Smaller angles produce smoother curved surfaces but more triangles.
- OCCT:
Prs3d_Drawer::DeviationAngle/SetDeviationAngle. - Example:
drawer.deviationAngle = 0.1 // smoother curves (~5.7°)
maximalChordialDeviation
Maximum chordal deviation as an absolute distance in model units.
public var maximalChordialDeviation: Double { get set }
The maximum allowed distance between a tessellation facet and the true surface. Only applies when deflectionType is .absolute.
- OCCT:
Prs3d_Drawer::MaximalChordialDeviation/SetMaximalChordialDeviation. - Example:
drawer.deflectionType = .absolute drawer.maximalChordialDeviation = 0.05 // 0.05 mm max chord error
deflectionType
Whether deflection is measured relative to the bounding box or as an absolute distance.
public var deflectionType: DeflectionType { get set }
- OCCT:
Prs3d_Drawer::TypeOfDeflection/SetTypeOfDeflection(mapped toAspect_TypeOfDeflection). - Example:
drawer.deflectionType = .absolute
autoTriangulation
Whether automatic triangulation is performed before display. Default true.
public var autoTriangulation: Bool { get set }
When true, OCCT will automatically triangulate shapes that lack a mesh before rendering. Set to false if you manage triangulation explicitly.
- OCCT:
Prs3d_Drawer::IsAutoTriangulation/SetAutoTriangulation. - Example:
drawer.autoTriangulation = false
Isolines and Discretisation
isoOnTriangulation
Whether iso-parameter lines are drawn on triangulated surfaces.
public var isoOnTriangulation: Bool { get set }
- OCCT:
Prs3d_Drawer::IsoOnTriangulation/SetIsoOnTriangulation. - Example:
drawer.isoOnTriangulation = true
discretisation
Number of discretisation points for curve approximation. Default 30.
public var discretisation: Int32 { get set }
Higher values produce smoother curves in wireframe rendering at the cost of more geometry.
- OCCT:
Prs3d_Drawer::Discretisation/SetDiscretisation. - Example:
drawer.discretisation = 60 // smoother wireframe curves
Edge Display
faceBoundaryDraw
Whether face boundary edges are included in wireframe rendering. Default false.
public var faceBoundaryDraw: Bool { get set }
When true, edges where two faces meet are drawn in addition to true wire edges.
- OCCT:
Prs3d_Drawer::FaceBoundaryDraw/SetFaceBoundaryDraw. - Example:
drawer.faceBoundaryDraw = true // show all face-boundary seams
wireDraw
Whether wireframe edges are drawn. Default true.
public var wireDraw: Bool { get set }
Set to false for shaded-only rendering without any edge lines.
- OCCT:
Prs3d_Drawer::WireDraw/SetWireDraw. - Example:
drawer.wireDraw = false // shaded only, no wireframe overlay