Drawing Automation & Helpers
This page covers the helper types and extension methods that automate annotation production on a Drawing: multi-view sheet composition (TransformedDrawing), auto-centreline and auto-centermark placement, ISO thread callouts, ISO surface-finish and GD&T symbols, heuristic auto-dimensioning, and 2D hatch-pattern generation. See Drawing.md (forthcoming) for the core Drawing type and its base annotation primitives.
Topics
- TransformedDrawing · Drawing (sheet composition) · Drawing (auto centrelines) · Drawing (auto centermarks) · DrawingAnnotation (thread) · Drawing (thread convenience) · DXFWriter (thread) · SurfaceFinishSymbol · GDTSymbol · DrawingAnnotation (symbols) · Drawing (detail view) · DrawingAnnotation (break line) · Drawing (auto dimensions) · HatchSegment · HatchPattern
TransformedDrawing
TransformedDrawing wraps a Drawing together with a uniform scale and a 2D translation offset, enabling multiple views to be composed onto the same DXF sheet. Introduced in v0.144 (issue #75).
TransformedDrawing.source
The underlying Drawing being transformed.
public let source: Drawing
- Example:
let td = TransformedDrawing(source: frontView, translate: SIMD2(100, 0), scale: 2.0) print(td.source === frontView) // true
TransformedDrawing.translate
The 2D translation applied after scaling.
public let translate: SIMD2<Double>
- Example:
let td = TransformedDrawing(source: frontView, translate: SIMD2(50, 0), scale: 1.0) print(td.translate) // SIMD2<Double>(50.0, 0.0)
TransformedDrawing.scale
The uniform scale factor applied to all geometry and annotation coordinates.
public let scale: Double
- Example:
let td = frontView.transformed(translate: .zero, scale: 0.5) print(td.scale) // 0.5
TransformedDrawing.init(source:translate:scale:)
Initialises a TransformedDrawing directly.
public init(source: Drawing, translate: SIMD2<Double> = .zero, scale: Double = 1.0)
- Parameters:
source: theDrawingto wrap.translate: 2D offset applied after scaling (default.zero).scale: uniform scale factor (default1.0).
- Example:
let placed = TransformedDrawing(source: sideView, translate: SIMD2(200, 0), scale: 1.0)
TransformedDrawing.apply(_:)
Applies the transform to a single 2D point.
public func apply(_ p: SIMD2<Double>) -> SIMD2<Double>
Computes scale * p + translate. Pure-Swift; used internally by DXFWriter.collectFromDrawing.
- Parameters:
p, input 2D point in drawing-local coordinates. - Returns: Transformed point in sheet coordinates.
- Example:
let td = TransformedDrawing(source: view, translate: SIMD2(10, 5), scale: 2.0) let sheet = td.apply(SIMD2(3, 4)) // SIMD2(16.0, 13.0)
Drawing (sheet composition)
These methods are declared as extensions on Drawing in DrawingComposition.swift.
Drawing.transformed(translate:scale:)
Returns a TransformedDrawing wrapping this drawing with a uniform scale and 2D translation.
public func transformed(translate: SIMD2<Double>, scale: Double = 1.0) -> TransformedDrawing
Sugar for TransformedDrawing(source: self, translate: translate, scale: scale). Pass the result to DXFWriter.collectFromDrawing to emit all edges and annotations with the transform applied.
- Parameters:
translate: 2D offset in sheet coordinates.scale: uniform scale factor (default1.0).
- Returns: A
TransformedDrawingready to pass toDXFWriter.collectFromDrawing. - Example:
for (view, placement) in layout { writer.collectFromDrawing(view.transformed(translate: placement.offset, scale: placement.scale)) }
Drawing.bounds(deflection:includeAnnotations:)
Computes the 2D axis-aligned bounding box of all visible, hidden, and outline edges in the drawing.
public func bounds(deflection: Double = 0.1,
includeAnnotations: Bool = true) -> (min: SIMD2<Double>, max: SIMD2<Double>)?
Iterates all edge polylines (tessellated at deflection) and, when includeAnnotations is true, also expands the box to include the key points of all dimensions and annotations.
- Parameters:
deflection: tessellation deflection for edge polyline sampling (default0.1).includeAnnotations: whentrue, annotation extents are included in the bounding box (defaulttrue).
- Returns: Tuple of min and max 2D corners, or
nilif the drawing contains no geometry. - Example:
if let bb = frontView.bounds() { let width = bb.max.x - bb.min.x let height = bb.max.y - bb.min.y print("view extents: \(width) × \(height)") }
Drawing (auto centrelines)
Extensions on Drawing in DrawingAutoCenterlines.swift that project a shape’s axes of revolution into the view plane as centreline annotations.
Drawing.AutoCentrelineResult
Result returned by addAutoCentrelines(from:viewDirection:overshoot:tolerance:bounds:).
public struct AutoCentrelineResult: Sendable {
public let added: [DrawingAnnotation]
public let skipped: [ShapeAxis]
}
added: the.centrelineannotations appended to the drawing.skipped: axes that projected to a point in the view (i.e. the axis is parallel to the view direction) and were therefore omitted.
(Per-field anchor below, for cross-reference; the list above has the actual meaning of each.)
Drawing.AutoCentrelineResult.skipped
Drawing.addAutoCentrelines(from:viewDirection:overshoot:tolerance:bounds:)
Projects the shape’s revolution axes into this drawing’s view plane and adds them as .chain centreline annotations.
@discardableResult
public func addAutoCentrelines(from shape: Shape,
viewDirection: SIMD3<Double>,
overshoot: Double = 5,
tolerance: Double = 1e-6,
bounds: (min: SIMD2<Double>, max: SIMD2<Double>)? = nil) -> AutoCentrelineResult
Calls Shape.revolutionAxes(tolerance:) on shape, then projects and clips each axis to the drawing’s 2D bounding box, extending each end by overshoot. Axes whose direction is parallel to the view direction are recorded in AutoCentrelineResult.skipped.
- Parameters:
shape: the source 3D shape (typically the one this drawing was projected from).viewDirection: the projection direction used when creating the drawing; assumed unit-length.overshoot: extra length (drawing units) added past the bounding box at both ends of each projected axis (default5).tolerance: axis-deduplication tolerance passed toShape.revolutionAxes(default1e-6).bounds: optional 2D clipping rectangle; whennilfalls back to±1000centred at the origin. PassDrawing.bounds()for best accuracy.
- Returns:
AutoCentrelineResultlisting added annotations and skipped axes. - Example:
let cyl = Shape.cylinder(radius: 10, height: 50)! let drawing = Drawing.project(cyl, direction: SIMD3(0, 1, 0))! let bb = drawing.bounds() let result = drawing.addAutoCentrelines(from: cyl, viewDirection: SIMD3(0, 1, 0), overshoot: 5, bounds: bb) print("\(result.added.count) centrelines added")
Drawing (auto centermarks)
Extensions on Drawing in DrawingAutoCenterlines.swift that add centermark crosses at the projected centres of visible circular edges.
Drawing.AutoCentermarkResult
Result returned by addAutoCentermarks(from:viewDirection:extent:minRadius:bounds:).
public struct AutoCentermarkResult: Sendable {
public let added: [DrawingAnnotation]
public let skipped: [Edge]
}
added: the.centermarkannotations appended to the drawing.skipped: circular edges that project edge-on (circle plane parallel to view direction) and were therefore omitted.
AutoCentermarkResult.skipped
Circular edges that project edge-on (circle plane parallel to view direction) and were therefore omitted.
Drawing.addAutoCentermarks(from:viewDirection:extent:minRadius:bounds:)
Walks the shape’s circular edges, projects each circle’s centre into the view plane, and adds a .centermark annotation for each circle visible face-on.
@discardableResult
public func addAutoCentermarks(from shape: Shape,
viewDirection: SIMD3<Double>,
extent: Double = 8,
minRadius: Double = 0,
bounds: (min: SIMD2<Double>, max: SIMD2<Double>)? = nil) -> AutoCentermarkResult
A circle is considered edge-on (and is skipped) when abs(dot(circleNormal, viewDirection)) < 0.1. This complements addAutoCentrelines, which handles revolution axes.
- Parameters:
shape: the 3D shape whose circular edges are inspected.viewDirection: the projection direction; assumed unit-length.extent: full arm length of the centermark cross in drawing units (default8).minRadius: circles with radius smaller than this value are skipped (default0).bounds: optional 2D clipping rectangle; circles whose projected centre falls outside are skipped.
- Returns:
AutoCentermarkResultlisting added annotations and skipped edges. - Example:
let part = Shape.cylinder(radius: 5, height: 20)! let drw = Drawing.project(part, direction: SIMD3(0, 0, 1))! let result = drw.addAutoCentermarks(from: part, viewDirection: SIMD3(0, 0, 1), extent: 6, minRadius: 1.0) print("\(result.added.count) centermarks added")
DrawingAnnotation (thread)
Static factory methods on DrawingAnnotation in DrawingThreadAnnotation.swift for ISO 6410 cosmetic thread representations. These produce annotation primitives; add them to a Drawing via Drawing.addCosmeticThreadSide/Drawing.addCosmeticThreadEndView or insert them directly into annotationStore.
DrawingAnnotation.Arc
A single 2D arc annotation: centre, radius, start angle, end angle (all in radians), plus the DXF/PDF/SVG layer it renders to. Dispatches through the shared emitAnnotation/transformed/keyPoints machinery like every other DrawingAnnotation case, so it renders identically to DXF, PDF and SVG and participates in Drawing.bounds() (#1179 — previously cosmeticThreadEndView returned a bespoke, non-DrawingAnnotation ArcSegment type reachable only via a DXFWriter-only extension).
public struct Arc: Sendable, Hashable {
public var centre: SIMD2<Double>
public var radius: Double
public var startAngle: Double // radians
public var endAngle: Double // radians
public var layer: String
public var id: String?
}
| Field | Meaning |
|---|---|
centre | 2D arc centre |
radius | Arc radius |
startAngle | Start angle in radians |
endAngle | End angle in radians |
layer | DXF/PDF/SVG layer (default "VISIBLE") |
id | Optional identifier |
DrawingAnnotation.cosmeticThreadSideView(axisStart:axisEnd:majorDiameter:pitch:callout:)
Produces an ISO 6410 cosmetic thread side-view pattern as an array of DrawingAnnotation values.
public static func cosmeticThreadSideView(
axisStart: SIMD2<Double>,
axisEnd: SIMD2<Double>,
majorDiameter: Double,
pitch: Double,
callout: String? = nil
) -> [DrawingAnnotation]
Generates two parallel solid centrelines at the minor diameter (computed as max(majorDiameter - 1.0825 × pitch, majorDiameter × 0.8)) on each side of the thread axis, plus an optional text label positioned at the thread midline. Returns an empty array if axisStart == axisEnd.
- Parameters:
axisStart: projected 2D start of the thread axis.axisEnd: projected 2D end of the thread axis.majorDiameter: nominal (major) thread diameter.pitch: thread pitch; used to compute minor diameter per ISO 68.callout: optional thread callout string (e.g."M10×1.5"); placed with a leader 10 units past the minor radius.
- Returns: Array of
DrawingAnnotationvalues (.centrelinelines, and optionally.textLabel). - Example:
let anns = DrawingAnnotation.cosmeticThreadSideView( axisStart: SIMD2(0, 0), axisEnd: SIMD2(20, 0), majorDiameter: 10, pitch: 1.5, callout: "M10×1.5") // anns contains 2 centreline lines + 1 textLabel
DrawingAnnotation.cosmeticThreadEndView(centre:majorDiameter:pitch:)
Produces an ISO 6410 cosmetic thread end-view pattern as an array of DrawingAnnotation values (three .arc cases) — a full DrawingAnnotation factory, matching cosmeticThreadSideView above.
public static func cosmeticThreadEndView(
centre: SIMD2<Double>,
majorDiameter: Double,
pitch: Double
) -> [DrawingAnnotation]
Returns a 3/4 broken arc at the minor diameter: three .arc annotations on the "CENTER" layer, covering 0–90°, 90–180°, and 180–315°, leaving a 45° gap in the last quadrant per the ISO convention.
- Parameters:
centre: projected 2D centre of the threaded hole or shaft.majorDiameter: nominal (major) thread diameter.pitch: thread pitch; used to compute minor diameter.
- Returns: Array of three
.arc(DrawingAnnotation.Arc)values, ready toappend(contentsOf:)onto aDrawing(or pass toDrawing.addCosmeticThreadEndViewbelow). - Example:
let anns = DrawingAnnotation.cosmeticThreadEndView( centre: SIMD2(30, 30), majorDiameter: 10, pitch: 1.5) drawing.append(contentsOf: anns)
Drawing (thread convenience)
Drawing.addCosmeticThreadSide(axisStart:axisEnd:majorDiameter:pitch:callout:)
Adds an ISO 6410 cosmetic thread side-view pattern to this drawing and returns the appended annotations.
@discardableResult
public func addCosmeticThreadSide(
axisStart: SIMD2<Double>,
axisEnd: SIMD2<Double>,
majorDiameter: Double,
pitch: Double,
callout: String? = nil
) -> [DrawingAnnotation]
Delegates to DrawingAnnotation.cosmeticThreadSideView and appends each annotation to annotationStore.
- Parameters: Same as
DrawingAnnotation.cosmeticThreadSideView(axisStart:axisEnd:majorDiameter:pitch:callout:). - Returns: The annotations that were appended.
- Example:
drawing.addCosmeticThreadSide( axisStart: SIMD2(0, 0), axisEnd: SIMD2(30, 0), majorDiameter: 12, pitch: 1.75, callout: "M12×1.75")
Drawing.addCosmeticThreadEndView(centre:majorDiameter:pitch:)
Adds an ISO 6410 cosmetic thread end-view 3/4-broken-arc pattern to this drawing and returns the appended annotations. Unlike DXFWriter.addCosmeticThreadEndView below, the arcs land on the Drawing’s own annotationStore, so they render identically whether the drawing is later exported to DXF, PDF or SVG, and participate in Drawing.bounds()/detailView() like every other annotation (#1179).
@discardableResult
public func addCosmeticThreadEndView(
centre: SIMD2<Double>,
majorDiameter: Double,
pitch: Double
) -> [DrawingAnnotation]
Delegates to DrawingAnnotation.cosmeticThreadEndView and appends each .arc annotation to annotationStore.
- Parameters: Same as
DrawingAnnotation.cosmeticThreadEndView(centre:majorDiameter:pitch:). - Returns: The annotations that were appended.
- Example:
drawing.addCosmeticThreadEndView( centre: SIMD2(30, 30), majorDiameter: 10, pitch: 1.5)
DXFWriter (thread)
DXFWriter.addCosmeticThreadEndView(centre:majorDiameter:pitch:)
Writes an ISO 6410 cosmetic thread end-view 3/4-arc set directly onto the DXF writer, on the CENTER layer, with no Drawing in the loop.
public func addCosmeticThreadEndView(centre: SIMD2<Double>,
majorDiameter: Double,
pitch: Double)
Delegates to DrawingAnnotation.cosmeticThreadEndView and stages each .arc annotation through the same shared emitAnnotation dispatch every other annotation renders through — behaviourally identical to before (three arcs on the CENTER layer), just no longer hand-rolling its own radians→degrees addArc calls (#1179). For a drawing that should also be exportable to PDF/SVG, or that should participate in bounds(), prefer Drawing.addCosmeticThreadEndView above.
- Parameters:
centre: 2D centre of the threaded hole or shaft in drawing coordinates.majorDiameter: nominal (major) thread diameter.pitch: thread pitch.
- Example:
writer.addCosmeticThreadEndView(centre: SIMD2(50, 50), majorDiameter: 10, pitch: 1.5)
SurfaceFinishSymbol
ISO 1302 surface-texture symbol type.
public enum SurfaceFinishSymbol: String, Sendable, Hashable, Codable {
case any
case machiningRequired
case machiningProhibited
}
any: any manufacturing method permitted; renders as a basic check-mark V.machiningRequired: machining required; V with a horizontal bar across the top.machiningProhibited: machining prohibited; V with a circle in the apex.
SurfaceFinishSymbol.any
Any manufacturing method permitted; renders as a basic check-mark V.
SurfaceFinishSymbol.machiningRequired
Machining required; V with a horizontal bar across the top.
SurfaceFinishSymbol.machiningProhibited
Machining prohibited; V with a circle in the apex.
GDTSymbol
ISO 1101 geometric characteristic symbol, matching Document.GeomToleranceType raw values for round-tripping XDE data into drawings.
public enum GDTSymbol: String, Sendable, Hashable, Codable {
case straightness, flatness, circularity, cylindricity
case profileOfLine, profileOfSurface
case perpendicularity, parallelism, angularity
case position, concentricity, symmetry, coaxiality
case circularRunout, totalRunout
}
Case meanings, per ISO 1101 / ASME Y14.5 (also matching Document.GeomToleranceType’s raw values, which mirror OCCT’s XCAFDimTolObjects_GeomToleranceType):
GDTSymbol.straightness
Form tolerance: a line element must lie within a specified straightness zone.
GDTSymbol.flatness
Form tolerance: a surface must lie within a specified flatness zone.
GDTSymbol.circularity
Form tolerance (also called roundness): a circular cross-section must lie within a specified annular zone.
GDTSymbol.cylindricity
Form tolerance: a cylindrical surface’s roundness, straightness and taper together must lie within a specified zone.
GDTSymbol.profileOfLine
Profile tolerance: a 2D line profile must lie within a zone around the true (nominal) profile.
GDTSymbol.profileOfSurface
Profile tolerance: a surface must lie within a zone around the true (nominal) profile.
GDTSymbol.perpendicularity
Orientation tolerance: a feature must lie within a specified zone at 90 degrees to a datum.
GDTSymbol.parallelism
Orientation tolerance: a feature must lie within a specified zone parallel to a datum.
GDTSymbol.angularity
Orientation tolerance: a feature must lie within a specified zone at a stated angle (other than 90 degrees) to a datum.
GDTSymbol.position
Location tolerance: a feature’s true position must lie within a specified zone relative to one or more datums.
GDTSymbol.concentricity
Location tolerance: the median points of a feature of revolution must lie within a specified spherical zone centered on a datum axis or point.
GDTSymbol.symmetry
Location tolerance: a feature’s own median points must lie within a specified zone symmetric about a datum plane or axis.
GDTSymbol.coaxiality
Location tolerance: a feature’s derived axis must lie within a specified cylindrical zone coaxial with a datum axis. Distinct from concentricity above, which constrains median points rather than a derived axis.
GDTSymbol.circularRunout
Runout tolerance: a single circular cross-section’s surface variation, measured as the part rotates about a datum axis, must stay within a specified zone.
GDTSymbol.totalRunout
Runout tolerance: the entire surface’s variation, measured as the part rotates about a datum axis, must stay within a specified zone.
GDTSymbol.glyph
A Unicode glyph or short textual representation for use in DXF plain-text entities.
public var glyph: String { get }
Full Unicode glyphs (e.g. "⊥", "⌖") render correctly in AutoCAD with a TrueType font. The textual fallbacks ("STR", "FLT", "O") are always safe.
- Example:
let sym = GDTSymbol.perpendicularity print(sym.glyph) // "⊥"
DrawingAnnotation (symbols)
Static factory methods on DrawingAnnotation in DrawingSymbols.swift for ISO standard engineering-drawing symbols.
DrawingAnnotation.surfaceFinish(at:leaderTo:ra:symbol:method:)
Produces an ISO 1302 surface finish annotation as an array of DrawingAnnotation values.
public static func surfaceFinish(
at position: SIMD2<Double>,
leaderTo target: SIMD2<Double>,
ra: Double,
symbol: SurfaceFinishSymbol = .machiningRequired,
method: String? = nil
) -> [DrawingAnnotation]
Generates the check-mark geometry (two lines at position), the appropriate symbol modifier (horizontal bar or apex circle), an Ra text label, an optional production-method text, and a leader line to target. The symbol is 8×10 drawing-units with the apex at position.
- Parameters:
position: apex position of the check-mark symbol.target: feature point the leader line points to.ra: roughness value (Ra); formatted as"Ra X.XX".symbol: ISO 1302 symbol type (default.machiningRequired).method: optional production method text placed below the Ra label.
- Returns: Array of
DrawingAnnotationvalues (.centrelinelines and.textLabelentries). - Example:
let anns = DrawingAnnotation.surfaceFinish( at: SIMD2(40, 10), leaderTo: SIMD2(40, 30), ra: 1.6, symbol: .machiningRequired, method: "Mill") for a in anns { drawing.annotationStore.appendAnnotation(a) }
DrawingAnnotation.featureControlFrame(at:symbol:tolerance:datums:leaderTo:)
Produces an ISO 1101 feature control frame, the classic rectangular box divided into symbol, tolerance, and datum-reference cells.
public static func featureControlFrame(
at position: SIMD2<Double>,
symbol: GDTSymbol,
tolerance: String,
datums: [String] = [],
leaderTo target: SIMD2<Double>? = nil
) -> [DrawingAnnotation]
Generates the outer rectangle, vertical cell dividers, the symbol glyph, tolerance text, one cell per datum, and an optional leader from the left edge of the frame to target.
- Parameters:
position: bottom-left corner of the frame.symbol: GD&T characteristic symbol.tolerance: tolerance string, e.g."0.1"or"0.1 M"for MMC modifier.datums: ordered datum reference letters, e.g.["A", "B", "C"](default empty).leaderTo: optional feature point for the leader line (defaultnil).
- Returns: Array of
DrawingAnnotationvalues. - Example:
let frame = DrawingAnnotation.featureControlFrame( at: SIMD2(10, 60), symbol: .position, tolerance: "0.1 M", datums: ["A", "B", "C"], leaderTo: SIMD2(10, 50)) for a in frame { drawing.annotationStore.appendAnnotation(a) }
DrawingAnnotation.datumFeature(label:at:pointingTo:)
Produces an ISO 1101 datum feature symbol, a letter in a square box with a filled-triangle pointer.
public static func datumFeature(
label: String,
at position: SIMD2<Double>,
pointingTo target: SIMD2<Double>
) -> [DrawingAnnotation]
Generates a 8×8 box with the label text centred, then a filled triangle (rendered as three lines) pointing from the box edge toward target, connected by a leader line.
- Parameters:
label: single-letter datum identifier, e.g."A".position: bottom-left corner of the datum box.target: the feature surface point the triangle points to.
- Returns: Array of
DrawingAnnotationvalues. - Example:
let datum = DrawingAnnotation.datumFeature( label: "A", at: SIMD2(5, 70), pointingTo: SIMD2(5, 55)) for a in datum { drawing.annotationStore.appendAnnotation(a) }
Drawing (detail view)
Drawing.detailView(at:scale:)
Composes a magnified detail view of this drawing, placed at placement on the sheet.
public func detailView(at placement: SIMD2<Double>, scale: Double) -> TransformedDrawing
Returns a TransformedDrawing with the given placement and scale. Pass the result to DXFWriter.collectFromDrawing. The caller is responsible for adding a bubble label on the parent view and a scale label on the detail placement.
- Parameters:
placement: 2D position of the detail view’s origin on the sheet.scale: magnification factor (e.g.2.0for 2:1).
- Returns: A
TransformedDrawingready forDXFWriter.collectFromDrawing. - Example:
let detail = mainView.detailView(at: SIMD2(300, 0), scale: 4.0) writer.collectFromDrawing(detail) // Add reference bubble and scale callout manually: mainView.annotationStore.appendAnnotation( .textLabel(.init(position: SIMD2(80, 60), text: "DETAIL A", height: 3.5)))
DrawingAnnotation (break line)
DrawingAnnotation.breakLine(from:to:amplitude:)
Produces an ISO 128-30 break line marking a compressed (foreshortened) length.
public static func breakLine(from: SIMD2<Double>, to: SIMD2<Double>,
amplitude: Double = 2.0) -> [DrawingAnnotation]
Renders as five line segments forming a zigzag at the midpoint of from–to: straight run to the midpoint, then a Z-shaped kink of the given amplitude, then a straight run to the end.
- Parameters:
from: start point of the break line.to: end point of the break line.amplitude: lateral displacement of the zigzag peak (default2.0).
- Returns: Five
.centrelineannotations forming the break-line geometry. - Example:
let bl = DrawingAnnotation.breakLine(from: SIMD2(0, 50), to: SIMD2(100, 50), amplitude: 3.0) for a in bl { drawing.annotationStore.appendAnnotation(a) }
Drawing (auto dimensions)
Extension on Drawing in DrawingAutoDimensions.swift for heuristic dimension placement.
Drawing.AutoDimensionResult
Result returned by addAutoDimensions(from:viewDirection:minRadius:dimensionOffset:bounds:).
public struct AutoDimensionResult: Sendable {
public let added: [DrawingDimension]
public let skipped: [String]
}
added: theDrawingDimensionvalues appended to the drawing.skipped: human-readable reasons for features that were not dimensioned; useful for debugging missing hole dimensions.
Drawing.AutoDimensionResult.skipped
Human-readable reasons for edges/features that were not dimensioned, e.g. a circle below minRadius or outside bounds.
Drawing.addAutoDimensions(from:viewDirection:minRadius:dimensionOffset:bounds:)
Heuristically adds overall width and height dimensions plus a diameter dimension on every visible circular edge.
@discardableResult
public func addAutoDimensions(from shape: Shape,
viewDirection: SIMD3<Double>,
minRadius: Double = 0.1,
dimensionOffset: Double = 10,
bounds: (min: SIMD2<Double>, max: SIMD2<Double>)? = nil) -> AutoDimensionResult
Projects all eight corners of the shape’s 3D bounding box into the view plane to derive overall X and Y extents, then places addLinearDimension calls for each non-zero extent. Then iterates circular edges: skips edge-on circles (abs(dot(circleNormal, viewDirection)) < 0.1), skips circles with radius below minRadius, and places addDiameterDimension for each remaining circle.
- Parameters:
shape: the 3D source shape.viewDirection: the projection direction; assumed unit-length.minRadius: minimum circle radius to dimension (default0.1).dimensionOffset: distance in drawing units between the view boundary and the dimension line for overall extents (default10).bounds: optional 2D clipping rectangle; circles outside are skipped.
- Returns:
AutoDimensionResultwith all added dimensions and skip reasons. - Example:
let part = Shape.cylinder(radius: 15, height: 40)! let drw = Drawing.project(part, direction: SIMD3(0, 1, 0))! let result = drw.addAutoDimensions(from: part, viewDirection: SIMD3(0, 1, 0), minRadius: 1.0, dimensionOffset: 12) print("\(result.added.count) dimensions added, skipped: \(result.skipped)")
HatchSegment
A single hatch line segment in a 2D fill pattern.
public struct HatchSegment: Sendable {
public let start: SIMD2<Double>
public let end: SIMD2<Double>
}
start: start point of the hatch line segment.end: end point of the hatch line segment.
HatchPattern
Caseless enum with a single static factory that generates 2D hatch fill segments within a polygon boundary.
HatchPattern.generate(boundary:direction:spacing:offset:islands:maxSegments:)
Generates hatch line segments within a 2D polygon boundary.
public static func generate(
boundary: [SIMD2<Double>],
direction: SIMD2<Double>,
spacing: Double,
offset: Double = 0,
islands: [[SIMD2<Double>]] = [],
maxSegments: Int = 10000
) -> [HatchSegment]
Clips an infinite family of parallel lines at the given spacing against the boundary polygon using Hatch_Hatcher. Returns an empty array if boundary.count < 3, spacing <= 0, or maxSegments <= 0.
- Parameters:
boundary: ordered polygon vertices defining the closed fill region.direction: direction of the hatch lines (need not be unit-length).spacing: perpendicular distance between consecutive hatch lines.offset: offset of the first hatch line from the origin along the perpendicular axis (default0).islands: closed inner-hole polygons excluded from the fill, via the sameHatch_Hatchereven/odd trim rule asboundary(default: none). An island with fewer than 3 vertices is ignored (#1172).maxSegments: output capacity (default10000), clamped into0...Sampling.maximumSampleCount(10,000,000); 0 or less returns empty (#622).
- Returns: Array of
HatchSegmentvalues clipped insideboundaryand outside everyislandspolygon. - OCCT:
Hatch_Hatcher::AddLine+Hatch_Hatcher::Trim, adds one directed line per spacing interval, then trims against each boundary edge and each island edge. - Example:
// Cross-hatch a rectangle at 45° with 2mm spacing let boundary: [SIMD2<Double>] = [ SIMD2(0, 0), SIMD2(10, 0), SIMD2(10, 5), SIMD2(0, 5) ] let segments = HatchPattern.generate( boundary: boundary, direction: SIMD2(1, 1), spacing: 2.0) for seg in segments { writer.addLine(from: seg.start, to: seg.end, layer: "HATCH") } - Note: The
maxSegmentscap silently truncates dense fills. Increase it for large areas or fine spacing; decrease it to bound output size.