| title | CADFileLoader |
|---|---|
| parent | API Reference |
The bridge between CAD files and renderable geometry. CADFileLoader wraps
OCCTSwiftIO's headless ShapeLoader and converts the resulting shapes into
ViewportBody + CADBodyMetadata for renderable, pickable consumers. It also
exposes the standalone Shape → body bridge (shapeToBodyAndMetadata) and two
mesh-quality presets. For headless, shape-only work with no Viewport dependency,
use OCCTSwiftIO.ShapeLoader directly instead.
- load · loadFromManifest · shapeToBodyAndMetadata
- Identity-table overloads: shapeToBodyMetadataAndIdentity · shapeToBodyMetadataAndIdentities
- Presets & defaults:
highQualityMeshParams·tessellationMeshParams·defaultEdgeDeflection·defaultMaxPointsPerEdge CADLoadResult
Loads a CAD file and returns viewport bodies with selection metadata. STL and
IGES loads that fail the primary bridge are transparently re-loaded via the
robust sewing/healing variants (Shape.loadSTLRobust / Shape.loadIGESRobust);
STEP / OBJ / BREP have no fallback.
When the robust reload returns a multibody result — since OCCTSwift v1.11.3 a
sewn multibody STL comes back as a compound of solids
(OCCTSwift#302) — it is
split into one ViewportBody per body rather than one lumped body, matching
the STEP path. Note the primary STL path meshes loose faces as a single body:
per-body identity for a mesh file only arises once sewing has recovered solids,
i.e. on the robust reload.
public static func load(
from url: URL,
format: CADFileFormat,
progress: ImportProgress? = nil,
includeIdentity: Bool = false
) async throws -> CADLoadResult- Parameters:
url: file URL to load.format: theCADFileFormat(.step,.iges,.stl,.obj,.brep, …).progress: optional progress + cancellation observer. Honoured by.stepand.igesonly (STL / OBJ / BREP are single-call upstream). Ifprogress.shouldCancel()returnstrue, the import throwsOCCTSwift.ImportError.cancelled.includeIdentity: populateCADLoadResult.identitywith aShapeIdentityper body: the shape it was tessellated from, aBRepGraphfor it, and the three ordinal-to-identity tablesSubShapePickResolverreads. Off by default: each body costs aBRepGraph, measured at roughly half the cost of meshing that body, and a consumer that loads geometry to render or reproject it never picks. Turn it on for anything that does. This is the only supported way to get identity out of a multi-body file load.
- Returns: a
CADLoadResultwith bridged bodies, per-body metadata, raw shapes, any PMI (dimensions / tolerances / datums), and, when asked, per-body identity. - Example:
let result = try await CADFileLoader.load( from: URL(fileURLWithPath: "/path/bracket.step"), format: .step ) // Picking as well as rendering: ask for identity, then read it by body id. let pickable = try await CADFileLoader.load( from: URL(fileURLWithPath: "/path/assembly.step"), format: .step, includeIdentity: true ) let uid = pickable.identity["step-0"]?.faces.uid(forOrdinal: 3)
Loads bodies from a script manifest (manifest.json plus its referenced BREP
files), applying each body's recorded colour. Synchronous.
public static func loadFromManifest(
at url: URL,
includeIdentity: Bool = false
) throws -> CADLoadResult- Parameters:
url: file URL of themanifest.json.includeIdentity: as onload(from:format:progress:includeIdentity:).
- Returns: a
CADLoadResultwhose bodies use ids of the form"script-<descriptor.id>"and fall back to grey(0.7, 0.7, 0.7, 1)where no colour was recorded. - Example:
let result = try CADFileLoader.loadFromManifest( at: URL(fileURLWithPath: "/path/output/manifest.json") )
Converts an OCCTSwift Shape to a ViewportBody and optional
CADBodyMetadata. Meshes the shape (using the high-quality preset by default),
applies crease-aware normal smoothing, extracts wireframe edge polylines, and
gathers source-shape vertices for picking. If meshing fails it still returns an
edge-only body when edge polylines could be extracted.
public static func shapeToBodyAndMetadata(
_ shape: Shape,
id bodyID: String,
color rgba: SIMD4<Float>,
stl: Bool = false,
deflection customDeflection: Double? = nil,
gpuTessellation: Bool = false,
edgeDeflection: Double = defaultEdgeDeflection,
maxPointsPerEdge: Int = defaultMaxPointsPerEdge,
includeMeasurements: Bool = false
) -> (ViewportBody?, CADBodyMetadata?)- Parameters:
shape— the OCCTSwift shape to bridge.id— body identifier.color— RGBA body colour.stl— iftrue, uses a coarser deflection (1.0) suitable for pre-tessellated STL data.deflection— custom linear deflection override; lower = smoother.gpuTessellation— iftrue, uses the coarsertessellationMeshParamspreset (GPU PN triangles refine it).edgeDeflection— linear deflection for the wireframe edge polylines (independent of the triangle deflection). Defaults todefaultEdgeDeflection(0.005).maxPointsPerEdge— hard cap on points per edge polyline. Defaults todefaultMaxPointsPerEdge(1000).includeMeasurements— iftrue, populatesmetadata.measurementswith per-face areas and per-edge lengths (viaShape.measure). Off by default — O(faces).
- Returns: a tuple
(ViewportBody?, CADBodyMetadata?). Both elements arenilwhen meshing fails and no edge polylines could be extracted. - Example:
let (body, meta) = CADFileLoader.shapeToBodyAndMetadata( Shape.box(width: 10, height: 5, depth: 3)!, id: "box", color: SIMD4<Float>(0.6, 0.6, 0.65, 1), includeMeasurements: true )
Overload of shapeToBodyAndMetadata that also emits a FaceIdentityTable
mapping every ordinal in the returned metadata's faceIndices back to the Shape it was
tessellated from, and, when graph is supplied, to the durable GraphUID minted from that graph.
See FaceIdentityTable for why this exists: a face ordinal resolved via
shape.subShapes(ofType: .face)[ordinal] silently misaligns once a face is shared between two
shells, and this captures the correspondence directly instead.
public static func shapeToBodyMetadataAndIdentity(
_ shape: Shape,
id bodyID: String,
color rgba: SIMD4<Float>,
stl: Bool = false,
deflection customDeflection: Double? = nil,
gpuTessellation: Bool = false,
edgeDeflection: Double = defaultEdgeDeflection,
maxPointsPerEdge: Int = defaultMaxPointsPerEdge,
includeMeasurements: Bool = false,
directMesh useDirectMesh: Bool = false,
graph: BRepGraph? = nil
) -> (ViewportBody?, CADBodyMetadata?, FaceIdentityTable?)- Parameters: same as
shapeToBodyAndMetadata, plusgraph, an optionalBRepGraphbuilt from this sameshape. When supplied, it populatesFaceIdentityTable.uids, minted viagraph.findNode(for:)on each ordinal's face soIsSamesemantics hold. Without a graph, onlyFaceIdentityTable.shapesis populated. - Returns: a tuple
(ViewportBody?, CADBodyMetadata?, FaceIdentityTable?).shapeToBodyAndMetadataitself is unchanged; this is a separate, additive overload. - Example:
let box = Shape.box(width: 10, height: 5, depth: 3)! let graph = BRepGraph(shape: box)! let (body, meta, faceTable) = CADFileLoader.shapeToBodyMetadataAndIdentity( box, id: "box", color: SIMD4<Float>(0.6, 0.6, 0.65, 1), graph: graph )
Overload of shapeToBodyAndMetadata that emits identity tables for all three pickable sub-shape
kinds: FaceIdentityTable, EdgeIdentityTable,
VertexIdentityTable. Each maps the render-path ordinal stored in the
corresponding ViewportBody array (faceIndices / edgeIndices / vertexIndices) back to the
Shape it was extracted from, and, when graph is supplied, to the durable GraphUID minted from
that graph.
public static func shapeToBodyMetadataAndIdentities(
_ shape: Shape,
id bodyID: String,
color rgba: SIMD4<Float>,
stl: Bool = false,
deflection customDeflection: Double? = nil,
gpuTessellation: Bool = false,
edgeDeflection: Double = defaultEdgeDeflection,
maxPointsPerEdge: Int = defaultMaxPointsPerEdge,
includeMeasurements: Bool = false,
directMesh useDirectMesh: Bool = false,
graph: BRepGraph? = nil
) -> (ViewportBody?, CADBodyMetadata?, FaceIdentityTable?, EdgeIdentityTable?, VertexIdentityTable?)- Parameters: same as
shapeToBodyMetadataAndIdentity. Pass aBRepGraphbuilt from this sameshapeto populate every table'suids; without one, onlyshapesis populated on each table. - Returns: a five-element tuple.
shapeToBodyAndMetadataandshapeToBodyMetadataAndIdentityare both unchanged; this is a separate, additive overload rather than an extension of either existing return shape. - Example:
let box = Shape.box(width: 10, height: 5, depth: 3)! let graph = BRepGraph(shape: box)! let (body, meta, faceTable, edgeTable, vertexTable) = CADFileLoader.shapeToBodyMetadataAndIdentities( box, id: "box", color: SIMD4<Float>(0.6, 0.6, 0.65, 1), graph: graph )
Two MeshParameters presets steer shapeToBodyAndMetadata. They are public so
you can inspect or reuse them.
public static let highQualityMeshParams: MeshParameters
public static let tessellationMeshParams: MeshParametershighQualityMeshParams— fine CPU mesh for smooth curved surfaces without GPU tessellation (deflection 0.03, ~11° angle, controlled surface deflection, parallel). The default used byshapeToBodyAndMetadata.tessellationMeshParams— moderate CPU mesh for GPU PN-triangle tessellation (deflection 0.1, ~20° angle). Selected bygpuTessellation: true.
public static let defaultEdgeDeflection: Double = 0.005
public static let defaultMaxPointsPerEdge: Int = 1000defaultEdgeDeflection— default linear deflection for wireframe edge polyline extraction.defaultMaxPointsPerEdge— default per-edge point cap for wireframe polyline extraction.
Note:
WireConverterdefines its own constants of the same names with a different cap (10000) — see WireConverter.
The value type returned by every CADFileLoader load. @unchecked Sendable.
public struct CADLoadResult: @unchecked Sendable {
public var bodies: [ViewportBody]
public var metadata: [String: CADBodyMetadata]
public var shapes: [Shape]
public var dimensions: [DimensionInfo]
public var geomTolerances: [GeomToleranceInfo]
public var datums: [DatumInfo]
public var identity: [String: ShapeIdentity]
public init(
bodies: [ViewportBody] = [],
metadata: [String: CADBodyMetadata] = [:],
shapes: [Shape] = [],
dimensions: [DimensionInfo] = [],
geomTolerances: [GeomToleranceInfo] = [],
datums: [DatumInfo] = [],
identity: [String: ShapeIdentity] = [:]
)
}bodies: bridged, renderable bodies.metadata: per-body selection metadata, keyed by body id.shapes: the raw OCCTSwift shapes that were loaded. Do not pair positionally withbodies, see below.dimensions/geomTolerances/datums: PMI (product manufacturing information) surfaced by formats that carry it. The types come from OCCTSwiftIO.identity: aShapeIdentityper loaded body, keyed byViewportBody.id. Empty unless the load was asked for it (includeIdentity: true).
shapes and bodies line up positionally on the primary bridge, where both arrays are appended
together only on tessellation success. They do not line up after the STL/IGES robust reload
(reloadRobustAndBridge), which appends a shape for every input even when that input produced no
body: every later pairing shifts by one, and a body silently gets another body's geometry.
From outside the loader the only symptom is a count mismatch (shapes.count > bodies.count), which
is why consumers used to guard on it and drop identity wholesale. identity is built inside the
loader, in the same branch that creates each body, so nothing pairs positionally and there is
nothing left to guard (OCCTSwiftInteraction#7).
let result = try await CADFileLoader.load(from: url, format: .step, includeIdentity: true)
for body in result.bodies {
guard let identity = result.identity[body.id],
let meta = result.metadata[body.id] else { continue }
let ref = SubShapePickResolver.resolveFace(
triangleIndex: pick.triangleIndex,
faceIndices: meta.faceIndices,
identity: identity.faces,
shape: identity.shape)
}