Link Search Menu Expand Document

Shape

Shape is OCCTSwift’s central B-Rep type, wrapping OCCT’s TopoDS_Shape hierarchy. It represents any B-Rep entity, solid, shell, face, wire, edge, vertex, or compound, and provides construction, boolean, modification, transformation, meshing, and format-import operations. Obtain a Shape via a static factory (e.g. Shape.box(), Shape.loft()), a sweep or boolean operation on an existing Shape, or by importing a STEP/IGES/BREP/STL/OBJ file.

Note: Shape is large, documented across several pages. This is the core (construction, booleans, modifications, meshing, import); see also the other Shape, … pages for features, healing, measurement, local operations, and the low-level OCCT builder wrappers.

Topics


Lifecycle

init(handle:)

Internal designated initialiser; takes ownership of a bridge handle.

internal init(handle: OCCTShapeRef)

Not part of the public API, all public entry points return Shape? optionals and manage memory internally.


deinit

Releases the underlying OCCT shape handle.

deinit
  • OCCT: OCCTShapeRelease → TopoDS_Shape reference count decremented.

Primitive Creation

Shape.box(width:height:depth:)

Create a box centered at the origin.

public static func box(width: Double, height: Double, depth: Double) -> Shape?
  • Parameters: width, height, depth, extents along X, Y, Z respectively.
  • Returns: A solid box, or nil if any dimension is non-positive.
  • OCCT: BRepPrimAPI_MakeBox.
  • Example:
    if let box = Shape.box(width: 10, height: 5, depth: 3) {
        // box.isValid == true
    }
    

Shape.box(origin:width:height:depth:)

Create a box at a specific position.

public static func box(
    origin: SIMD3<Double>,
    width: Double,
    height: Double,
    depth: Double
) -> Shape?
  • Parameters: origin, corner point of the box; width, height, depth, extents along X, Y, Z.
  • Returns: A solid box at the given position, or nil on failure.
  • OCCT: BRepPrimAPI_MakeBox.
  • Example:
    if let box = Shape.box(origin: SIMD3(5, 0, 0), width: 10, height: 10, depth: 10) {
        // volume == 1000
    }
    

Shape.box(at:direction:width:height:depth:)

Create a box at an arbitrary origin along an arbitrary direction.

public static func box(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    width: Double,
    height: Double,
    depth: Double
) -> Shape?
  • Parameters:
    • origin: corner point of the box.
    • direction: axis direction for the box height (will be normalised).
    • width: X extent in local frame.
    • height: Y extent in local frame.
    • depth: extent along direction.
  • Returns: Oriented solid box, or nil on failure.
  • OCCT: BRepPrimAPI_MakeBox (via OCCTShapeCreateBoxOriented).
  • Example:
    if let box = Shape.box(at: .zero, direction: SIMD3(0, 0, 1), width: 4, height: 4, depth: 10) {
        // box tilted along Z
    }
    

Shape.cylinder(radius:height:)

Create a cylinder along the Z axis with base at the origin.

public static func cylinder(radius: Double, height: Double) -> Shape?
  • Parameters: radius, base circle radius; height, height along Z.
  • Returns: A solid cylinder, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCylinder.
  • Example:
    if let cyl = Shape.cylinder(radius: 2, height: 10) {
        // volume ≈ 125.66
    }
    

Shape.cylinder(at:bottomZ:radius:height:)

Create a cylinder at a specific XY position with a specified Z base.

public static func cylinder(
    at position: SIMD2<Double>,
    bottomZ: Double,
    radius: Double,
    height: Double
) -> Shape?
  • Parameters: position, XY centre of base circle; bottomZ, Z coordinate of base; radius, height.
  • Returns: Positioned cylinder, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCylinder (via OCCTShapeCreateCylinderAt).
  • Example:
    if let cyl = Shape.cylinder(at: SIMD2(3, 4), bottomZ: 0, radius: 1.5, height: 8) { }
    

Shape.cylinder(at:direction:radius:height:)

Create a cylinder at an arbitrary origin along an arbitrary direction.

public static func cylinder(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    radius: Double,
    height: Double
) -> Shape?
  • Parameters:
    • origin: centre of the base circle.
    • direction: axis direction (will be normalised).
    • radius: cylinder radius.
    • height: height along direction.
  • Returns: Oriented cylinder, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCylinder (via OCCTShapeCreateCylinderOriented).
  • Example:
    guard let cyl = Shape.cylinder(at: SIMD3(0, 0, -8),
                                   direction: SIMD3(0, 0, 1),
                                   radius: 3, height: 16) else { return }
    

Shape.cylinder(radius:height:angle:)

Create a partial cylinder (angular segment) along the Z axis.

public static func cylinder(radius: Double, height: Double, angle: Double) -> Shape?
  • Parameters: radius, height; angle, angular extent in radians (0 < angle <= 2*pi).
  • Returns: Partial cylinder sector, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCylinder (via OCCTShapeCreateCylinderPartial).
  • Example:
    if let half = Shape.cylinder(radius: 5, height: 10, angle: .pi) { }
    

Shape.cylinder(at:direction:radius:height:angle:)

Create a partial cylinder (angular segment) at an arbitrary origin along an arbitrary direction.

public static func cylinder(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    radius: Double,
    height: Double,
    angle: Double
) -> Shape?
  • Parameters: origin, direction, radius, height, angle, angular extent in radians (0 < angle <= 2*pi).
  • Returns: Oriented partial cylinder, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCylinder (via OCCTShapeCreateCylinderOrientedPartial).

Shape.toolSweep(radius:height:from:to:)

Create a tool-sweep solid, the volume swept by a cylindrical tool moving between two points.

public static func toolSweep(
    radius: Double,
    height: Double,
    from start: SIMD3<Double>,
    to end: SIMD3<Double>
) -> Shape?

Used for CAM simulation to compute material removal volumes.

  • Parameters: radius, height, tool dimensions; start, end, tool centre path.
  • Returns: Swept solid volume, or nil on failure.
  • OCCT: OCCTShapeCreateToolSweep.
  • Example:
    if let sweep = Shape.toolSweep(radius: 2, height: 5,
                                   from: SIMD3(0, 0, 0), to: SIMD3(10, 0, 0)) { }
    

Shape.sphere(radius:)

Create a sphere centred at the origin.

public static func sphere(radius: Double) -> Shape?
  • Parameters: radius, sphere radius.
  • Returns: Solid sphere, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere.
  • Example:
    if let ball = Shape.sphere(radius: 5) {
        // volume ≈ 523.6
    }
    

Shape.sphere(center:radius:)

Create a sphere at a specific centre point.

public static func sphere(center: SIMD3<Double>, radius: Double) -> Shape?
  • Parameters: center, centre of the sphere; radius.
  • Returns: Positioned sphere, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere (via OCCTShapeCreateSphereAtCenter).
  • Example:
    if let ball = Shape.sphere(center: SIMD3(0, 0, 10), radius: 3) { }
    

Shape.sphere(at:direction:radius:)

Create an oriented sphere at an arbitrary origin along an arbitrary direction.

public static func sphere(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    radius: Double
) -> Shape?
  • Parameters: origin, centre; direction, axis direction (affects parameterisation); radius.
  • Returns: Oriented sphere, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere (via OCCTShapeCreateSphereOriented).

Shape.sphere(radius:angle:)

Create a partial sphere (angular segment).

public static func sphere(radius: Double, angle: Double) -> Shape?
  • Parameters: radius; angle, angular extent in radians (0 < angle <= 2*pi).
  • Returns: Sphere sector, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere (via OCCTShapeCreateSpherePartial).

Shape.sphere(at:direction:radius:angle:)

Create a partial sphere (angular segment) at an arbitrary origin along an arbitrary direction.

public static func sphere(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    radius: Double,
    angle: Double
) -> Shape?
  • Parameters: origin, direction, radius, angle, angular extent in radians.
  • Returns: Oriented sphere sector, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere (via OCCTShapeCreateSphereOrientedPartial).

Shape.sphere(at:direction:radius:angle1:angle2:)

Create a sphere latitude segment at an arbitrary origin along an arbitrary direction.

public static func sphere(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    radius: Double,
    angle1: Double,
    angle2: Double
) -> Shape?
  • Parameters:
    • origin: centre of the sphere.
    • direction: axis direction (affects parameterisation).
    • radius: sphere radius.
    • angle1: lower latitude bound in radians (−π/2 to π/2).
    • angle2: upper latitude bound in radians (−π/2 to π/2).
  • Returns: Latitude-band sphere segment, or nil on failure.
  • OCCT: BRepPrimAPI_MakeSphere (via OCCTShapeCreateSphereOrientedSegment).

Shape.cone(bottomRadius:topRadius:height:)

Create a cone along the Z axis.

public static func cone(bottomRadius: Double, topRadius: Double, height: Double) -> Shape?
  • Parameters: bottomRadius, radius at the base; topRadius, radius at the top (0 = true cone); height.
  • Returns: Solid cone or frustum, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCone.
  • Example:
    if let cone = Shape.cone(bottomRadius: 5, topRadius: 0, height: 10) { }
    if let frustum = Shape.cone(bottomRadius: 5, topRadius: 3, height: 8) { }
    

Shape.cone(at:direction:bottomRadius:topRadius:height:)

Create a cone at an arbitrary origin along an arbitrary direction.

public static func cone(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    bottomRadius: Double,
    topRadius: Double,
    height: Double
) -> Shape?
  • Parameters: origin, centre of the base circle; direction, axis (will be normalised); bottomRadius, topRadius, height.
  • Returns: Oriented cone/frustum, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCone (via OCCTShapeCreateConeOriented).

Shape.cone(at:direction:bottomRadius:topRadius:height:angle:)

Create a partial cone (angular segment) at an arbitrary origin along an arbitrary direction.

public static func cone(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    bottomRadius: Double,
    topRadius: Double,
    height: Double,
    angle: Double
) -> Shape?
  • Parameters: origin, direction, bottomRadius, topRadius, height; angle, angular extent in radians (0 < angle <= 2*pi).
  • Returns: Oriented cone sector, or nil on failure.
  • OCCT: BRepPrimAPI_MakeCone (via OCCTShapeCreateConeOrientedPartial).

Shape.torus(majorRadius:minorRadius:)

Create a torus in the XY plane.

public static func torus(majorRadius: Double, minorRadius: Double) -> Shape?
  • Parameters: majorRadius, distance from torus centre to tube centre; minorRadius, tube radius.
  • Returns: Solid torus, or nil on failure.
  • OCCT: BRepPrimAPI_MakeTorus.
  • Example:
    if let ring = Shape.torus(majorRadius: 10, minorRadius: 2) { }
    

Shape.torus(at:direction:majorRadius:minorRadius:)

Create a torus at an arbitrary origin along an arbitrary direction.

public static func torus(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    majorRadius: Double,
    minorRadius: Double
) -> Shape?
  • Parameters: origin, centre of the torus; direction, normal to the torus plane; majorRadius, minorRadius.
  • Returns: Oriented torus, or nil on failure.
  • OCCT: BRepPrimAPI_MakeTorus (via OCCTShapeCreateTorusOriented).

Shape.torus(at:direction:majorRadius:minorRadius:angle:)

Create a partial torus (angular segment) at an arbitrary origin along an arbitrary direction.

public static func torus(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    majorRadius: Double,
    minorRadius: Double,
    angle: Double
) -> Shape?
  • Parameters: origin, direction, majorRadius, minorRadius; angle, angular extent in radians (0 < angle <= 2*pi).
  • Returns: Oriented torus sector, or nil on failure.
  • OCCT: BRepPrimAPI_MakeTorus (via OCCTShapeCreateTorusOrientedPartial).

Shape.torus(at:direction:majorRadius:minorRadius:angle1:angle2:)

Create a torus tube segment at an arbitrary origin along an arbitrary direction.

public static func torus(
    at origin: SIMD3<Double>,
    direction: SIMD3<Double>,
    majorRadius: Double,
    minorRadius: Double,
    angle1: Double,
    angle2: Double
) -> Shape?
  • Parameters:
    • origin: centre of the torus.
    • direction: axis direction (normal to torus plane).
    • majorRadius, minorRadius.
    • angle1: start angle of the tube section in radians.
    • angle2: end angle of the tube section in radians.
  • Returns: Tube-segment torus, or nil on failure.
  • OCCT: BRepPrimAPI_MakeTorus (via OCCTShapeCreateTorusOrientedSegment).

Sweep Operations

Shape.sweep(profile:along:)

Sweep a 2D profile wire along a path wire to create a solid.

public static func sweep(profile: Wire, along path: Wire) -> Shape?

The result is orientation-normalised so its faces always point outward (positive volume), regardless of how the profile’s normal relates to the path tangent. You do not need to manually orient the section against the tangent.

  • Parameters: profile, closed cross-section wire placed at the path’s start; path, spine wire.
  • Returns: Swept solid, or nil on failure.
  • OCCT: BRepOffsetAPI_MakePipe.
  • Example:
    guard let section = Wire.circle(origin: SIMD3(16, 0, 0),
                                    normal: SIMD3(0, 1, 0), radius: 5),
          let path = Wire.arc(center: .zero, radius: 16,
                              startAngle: 0, endAngle: .pi / 2),
          let elbow = Shape.sweep(profile: section, along: path) else { return }
    // elbow.isValid == true
    

Shape.extrude(profile:direction:length:)

Extrude a 2D profile wire in a direction to create a prism.

public static func extrude(profile: Wire, direction: SIMD3<Double>, length: Double) -> Shape?
  • Parameters: profile, closed profile wire; direction, extrusion direction (need not be unit length); length, extrusion distance.
  • Returns: Solid prism, or nil on failure. A zero, NaN or infinite length and a zero, NaN, infinite or overflowing direction answer nil: they built a solid whose first validity check never returned (#3100). A finite nonzero length such as 1e-6 or 1e-12 is passed to OCCT as before.
  • OCCT: BRepPrimAPI_MakePrism.
  • Example:
    guard let profile = Wire.rectangle(width: 20, height: 12),
          let prism = Shape.extrude(profile: profile,
                                    direction: SIMD3(0, 0, 1), length: 8) else { return }
    // prism.volume == 20 * 12 * 8 == 1920
    // Shape.extrude(profile: profile, direction: SIMD3(0, 0, 1), length: 0) == nil
    

Shape.revolve(profile:axisOrigin:axisDirection:angle:)

Revolve a 2D profile wire around an axis.

public static func revolve(
    profile: Wire,
    axisOrigin: SIMD3<Double>,
    axisDirection: SIMD3<Double>,
    angle: Double = .pi * 2
) -> Shape?
  • Parameters:
    • profile: meridian wire in a plane containing the axis.
    • axisOrigin: a point on the revolution axis.
    • axisDirection: axis direction vector.
    • angle: sweep angle in radians (default full 2π).
  • Returns: Revolution solid, or nil on failure. A NaN, infinite or absurd angle (past 1e4 radians, about 1591 turns; the builders ran for a time proportional to the angle, #3109), a NaN or infinite component of the axis origin, or an axis direction that is NaN, infinite, zero or overflowing, answers nil: an infinite angle never returned from BRepSweep_Revol, a NaN one built an invalid shell (#3100), and an unusable axis answered a shape (#3113). Any angle within that bound, 0 and 2 * .pi included, and any axis of length 1e-6 to 1e6, is passed to OCCT as before.
  • OCCT: BRepPrimAPI_MakeRevol.
  • Example:
    guard let meridian = Wire.polygon([
        SIMD2(5, 0), SIMD2(7, 0), SIMD2(7, 10), SIMD2(5, 10)
    ], closed: true),
          let drum = Shape.revolve(profile: meridian,
                                   axisOrigin: .zero,
                                   axisDirection: SIMD3(0, 1, 0),
                                   angle: 2 * .pi) else { return }
    

extruded(by:)

Extrude an existing shape (any topology) along a vector.

public func extruded(by vector: SIMD3<Double>) -> Shape?
  • Parameters: vector, translation vector; its magnitude determines the extrusion distance.
  • Returns: Extruded shape, or nil on failure. A zero vector, a NaN or infinite component, and a vector whose squared magnitude underflows to zero (below about 1.5e-162) or overflows (above about 1.3e154) answer nil: they built a shape whose first validity check never returned (#3100). Every vector between, 1e-6 and 1e-12 included, is passed to OCCT as before.
  • OCCT: BRepPrimAPI_MakePrism (via OCCTShapeCreateExtrusionShape).
  • Example:
    if let solid = face.extruded(by: SIMD3(0, 0, 5)) { }
    

BRepPrimAPI_MakePrism dereferences the profile shape, so a null shape (from Shape.nullified) used to crash the process here. It is refused now and answers nil (#1035, measured in Scripts/repro/1035-unwrap-guard/).


extrudedInfinite(direction:infinite:)

Extrude an existing shape to infinity (or semi-infinity) along a direction.

public func extrudedInfinite(direction: SIMD3<Double>, infinite: Bool = true) -> Shape?

Useful for half-space cutters in booleans (e.g. slice a solid).

  • Parameters: direction, extrusion direction; infinite, if true, both directions; if false, semi-infinite.
  • Returns: Infinite-extent shape (shell), or nil on failure. A zero direction, a NaN or infinite component, and a direction whose squared magnitude underflows to zero or overflows (so SIMD3(0, 0, 1e300)) answer nil: they built a shape whose first validity check never returned (#3100). The direction’s length is otherwise irrelevant, so 1e-6 and 1e150 still extrude.
  • OCCT: BRepPrimAPI_MakePrism (via OCCTShapeCreateExtrusionInfinite).
  • Example:
    // Cut anything above z = 0 from a solid
    guard let plane = Shape.face(from: Wire.rectangle(width: 200, height: 200)!),
          let halfSpace = plane.extrudedInfinite(direction: SIMD3(0, 0, 1)),
          let trimmed = solid.subtracting(halfSpace) else { return }
    

A null shape (from Shape.nullified) used to crash the process inside BRepPrimAPI_MakePrism. It is refused now and answers nil (#1035).


revolved(axisOrigin:axisDirection:)

Revolve an existing shape around an axis by a full 360 degrees.

public func revolved(
    axisOrigin: SIMD3<Double>,
    axisDirection: SIMD3<Double>
) -> Shape?
  • Parameters: axisOrigin, point on revolution axis; axisDirection, axis direction.
  • Returns: Revolution solid, or nil on failure. A NaN or infinite component of the axis origin, or an axis direction that is NaN, infinite, zero or overflowing, answers nil (such an axis answered a shape, #3113).
  • OCCT: BRepPrimAPI_MakeRevol (via OCCTShapeCreateRevolutionFull).

revolved(axisOrigin:axisDirection:angle:)

Revolve an existing shape around an axis by a partial angle.

public func revolved(
    axisOrigin: SIMD3<Double>,
    axisDirection: SIMD3<Double>,
    angle: Double
) -> Shape?
  • Parameters: axisOrigin, axisDirection; angle, sweep angle in radians.
  • Returns: Partial revolution solid, or nil on failure. A NaN, infinite or absurd angle (past 1e4 radians, about 1591 turns; the builders ran for a time proportional to the angle, #3109), a NaN or infinite component of the axis origin, or an axis direction that is NaN, infinite, zero or overflowing, answer nil (an infinite angle or a NaN axis never returned, #3100, #3113); any angle within that bound is passed to OCCT as before.
  • OCCT: BRepPrimAPI_MakeRevol (via OCCTShapeCreateRevolutionPartial).

Shape.loft(profiles:solid:)

Loft through multiple profile wires.

public static func loft(profiles: [Wire], solid: Bool = true) -> Shape?
  • Parameters: profiles, ordered array of profile wires; solid, true (default) creates a closed solid, false creates an open shell.
  • Returns: Lofted shape, or nil on failure.
  • OCCT: BRepOffsetAPI_ThruSections.
  • Note: Mismatched closed/open profiles can trigger a SIGSEGV in BRepFill_CompatibleWires when using smooth (non-ruled) lofts, see the CLAUDE.md note on this known upstream bug and the patch in Scripts/patches/.
  • Example:
    guard let base = Wire.circle(radius: 5),
          let top = Wire.circle(origin: SIMD3(0, 0, 20), normal: SIMD3(0, 0, 1), radius: 2),
          let transition = Shape.loft(profiles: [base, top], solid: true) else { return }
    

Shape.loft(profiles:solid:ruled:firstVertex:lastVertex:)

Loft through profile wires with advanced options.

public static func loft(profiles: [Wire], solid: Bool = true, ruled: Bool,
                        firstVertex: SIMD3<Double>? = nil,
                        lastVertex: SIMD3<Double>? = nil) -> Shape?
  • Parameters:
    • profiles: ordered profile wires.
    • solid: solid vs shell.
    • ruled: true = ruled (flat/faceted) surfaces between profiles; false = smooth B-spline blend.
    • firstVertex: optional starting tip point for cone/taper shapes.
    • lastVertex: optional ending tip point for cone/taper shapes.
  • Returns: Lofted shape, or nil on failure.
  • Refuses: fewer than two sections return nil, where firstVertex and lastVertex each count as a section. One profile with no vertex used to end the process with SIGSEGV when ruled was false and to return an invalid shape when it was true (#3099); one profile plus a vertex is a cone and still works.
  • OCCT: BRepOffsetAPI_ThruSections (via OCCTShapeCreateLoftAdvanced).
  • Example:
    // Ruled loft (flat sides)
    let ruled = Shape.loft(profiles: [bottom, upper], solid: true, ruled: true)
    // Smooth loft
    let smooth = Shape.loft(profiles: [bottom, upper], solid: true, ruled: false)
    
    // Taper to a point (cone tip)
    guard let circle = Wire.circle(radius: 5) else { return }
    let cone = Shape.loft(profiles: [circle], solid: true, ruled: true,
                          lastVertex: SIMD3(0, 0, 10))
    
    // One section is not a loft
    let none = Shape.loft(profiles: [circle], solid: true, ruled: false)  // nil
    

Boolean Operations

BooleanGlue

Glue mode enum for boolean operations (BOPAlgo_GlueEnum).

public enum BooleanGlue: Int32, Sendable {
    case off   = 0  // No gluing, full intersection (default)
    case shift = 1  // BOPAlgo_GlueShift, coincident but otherwise disjoint faces
    case full  = 2  // BOPAlgo_GlueFull, all faces coincident (fastest, strictest)
}

Gluing speeds up booleans when arguments share coincident faces. Only use when faces truly coincide, gluing genuinely interpenetrating solids produces a wrong result.


Case OCCT Meaning
.off OCCT default No gluing; the full intersection is computed.
.shift BOPAlgo_GlueShift Arguments may share coincident faces but are otherwise disjoint.
.full BOPAlgo_GlueFull Every argument is known to share coincident faces. Fastest and strictest.

defaultBooleanTimeout

Default wall-clock timeout for boolean operations, in seconds.

public static let defaultBooleanTimeout: Double = 120

A self-intersecting or inside-out operand (e.g. from loft(ruled: false)) can make BRepAlgoAPI_Cut spin indefinitely; boolean operations abort and return nil once this elapses. Pass 0 or negative to disable. Override per call via the timeout: parameter.

That nil is the same nil a failed boolean returns, so union / subtracting / intersection cannot tell “this geometry is bad” from “this machine was busy”. The *Outcome siblings below separate the two (#1067).


BooleanOutcome

What a boolean did, separating a genuine failure from the watchdog firing.

public enum BooleanOutcome: Sendable {
    case success(Shape)
    case failed
    case timedOut

    public var shape: Shape? { get }
}
Case Meaning Does a larger timeout: help?
.success(Shape) The operation completed and produced this shape. n/a
.failed The operation ran to a conclusion and declined, for example on an invalid operand. No.
.timedOut The wall-clock watchdog interrupted the build. A property of the deadline and the machine, not of the geometry: indeterminate, not “cannot be built”. Possibly.

shape is the result on .success and nil for both other cases, which is exactly what union / subtracting / intersection return, and why they cannot tell the two apart.

Returned by unionOutcome, subtractionOutcome and intersectionOutcome. Deliberately not Equatable: it carries a Shape, which is a reference type with no value equality.


unionOutcome(_:fuzzyValue:glue:timeout:) / subtractionOutcome(_:fuzzyValue:glue:timeout:) / intersectionOutcome(_:fuzzyValue:glue:timeout:)

The three booleans below, reporting why they produced no shape.

public func unionOutcome(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                         timeout: Double = Shape.defaultBooleanTimeout) -> BooleanOutcome
public func subtractionOutcome(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                               timeout: Double = Shape.defaultBooleanTimeout) -> BooleanOutcome
public func intersectionOutcome(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                                timeout: Double = Shape.defaultBooleanTimeout) -> BooleanOutcome

Same operation, same options and the same watchdog as union / subtracting / intersection, which are now thin wrappers returning BooleanOutcome.shape. Only the return type differs.

  • Parameters: identical to the matching named method.
  • Returns: .success(Shape), .failed, or .timedOut.
  • OCCT: BRepAlgoAPI_Fuse / _Cut / _Common, each via its existing OCCTShape*Ex bridge function, which now also reports whether its Message_ProgressIndicator watchdog was the thing that stopped the build.
  • ⚠️ timeout is cooperative, not a hard deadline (#293), exactly as on the named methods. OCCT polls it at its own internal checkpoints, so the call returns at the first checkpoint after timeout, not at timeout itself. .timedOut means “the watchdog fired”, which is indeterminate rather than negative, the reading isSelfIntersecting(timeout:) gives its nil. The two are not the same signal: .timedOut is the watchdog and only the watchdog, while that nil also covers a refused argument and an errored analysis (#1054).
  • timeout: 0 (or negative) makes .timedOut unreachable, since no watchdog is constructed at all.
  • Example:
    // Retry the deadline, not the geometry.
    var outcome = blank.subtractionOutcome(tools)
    if case .timedOut = outcome {
        outcome = blank.subtractionOutcome(tools, timeout: 600)
    }
    switch outcome {
    case .success(let gear): print(gear.volume as Any)
    case .timedOut:          print("still no answer, the bound is the limit, not the shape")
    case .failed:            print("the boolean declined these operands")
    }
    

union(_:fuzzyValue:glue:timeout:)

Union (fuse) two shapes together.

public func union(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                  timeout: Double = Shape.defaultBooleanTimeout) -> Shape?

Also available as the + operator.

  • Parameters:
    • other: the shape to fuse with self.
    • fuzzyValue: tolerance (SetFuzzyValue). 0 = OCCT default; a small positive value (e.g. 1e-4) helps near-tangent/coincident faces fuse cleanly. Negative = ignored.
    • glue: glue mode for coincident-face arguments.
    • timeout: wall-clock bound in seconds; nil result if elapsed.
  • Returns: Fused solid, or nil on failure or timeout. Call unionOutcome to tell those two apart.
  • OCCT: BRepAlgoAPI_Fuse (via OCCTShapeUnionEx).
  • Example:
    guard let box = Shape.box(width: 10, height: 10, depth: 10),
          let cyl = Shape.cylinder(at: SIMD3(0, 0, -8), direction: SIMD3(0, 0, 1),
                                   radius: 3, height: 16) else { return }
    let fused = box.union(cyl)           // or: box + cyl
    // Fuzzy: near-tangent walls fuse cleanly
    let clean = outer.union(inner, fuzzyValue: 2.1e-5)
    // Glue: stacked blocks sharing a face
    let stacked = lower.union(upper, glue: .shift)
    
  • Note: union(with:), the deprecated with:-labelled overload, was removed at v2.0.0 (#784); use union(_:).

subtracting(_:fuzzyValue:glue:timeout:)

Subtract another shape from this one.

public func subtracting(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                        timeout: Double = Shape.defaultBooleanTimeout) -> Shape?

Also available as the - operator.

  • Parameters:
    • other: the tool shape to remove from self.
    • fuzzyValue: tolerance. Raise slightly when a thin-wall cut under-subtracts.
    • glue: glue mode.
    • timeout: wall-clock bound in seconds.
  • Returns: Result of self − other, or nil on failure or timeout. Call subtractionOutcome to tell those two apart.
  • OCCT: BRepAlgoAPI_Cut (via OCCTShapeSubtractEx).
  • Example:
    guard let box = Shape.box(width: 10, height: 10, depth: 10),
          let cyl = Shape.cylinder(at: SIMD3(0, 0, -8), direction: SIMD3(0, 0, 1),
                                   radius: 3, height: 16) else { return }
    let drilled = box.subtracting(cyl)   // or: box - cyl
    

intersection(_:fuzzyValue:glue:timeout:)

Intersection of two shapes.

public func intersection(_ other: Shape, fuzzyValue: Double = 0, glue: BooleanGlue = .off,
                         timeout: Double = Shape.defaultBooleanTimeout) -> Shape?

Also available as the & operator.

  • Parameters:
    • other: the shape to intersect with self.
    • fuzzyValue: tolerance.
    • glue: glue mode.
    • timeout: wall-clock bound in seconds.
  • Returns: The volume common to both shapes, or nil on failure or timeout. Call intersectionOutcome to tell those two apart.
  • OCCT: BRepAlgoAPI_Common (via OCCTShapeIntersectEx).
  • Example:
    let common = box.intersection(cyl)  // or: box & cyl
    
  • Note: intersection(with:), the deprecated with:-labelled overload, was removed at v2.0.0 (#784); use intersection(_:).

Modifications

filleted(radius:)

Fillet (round) all edges with a given radius.

public func filleted(radius: Double) -> Shape?
  • Parameters: radius, fillet radius.
  • Returns: Shape with all edges rounded, or nil on failure, including a result BRepCheck_Analyzer reports invalid, such as a radius above half the width of a face (#3200). See filleted(edges:radius:) for the contract.
  • OCCT: BRepFilletAPI_MakeFillet.
  • Example:
    if let rounded = Shape.box(width: 10, height: 10, depth: 10)?.filleted(radius: 1) { }
    

chamfered(distance:)

Chamfer all edges with a given distance.

public func chamfered(distance: Double) -> Shape?
  • Parameters: distance, chamfer distance (equal on both sides).
  • Returns: Shape with all edges chamfered, or nil on failure, including a result BRepCheck_Analyzer reports invalid (#3200).
  • OCCT: BRepFilletAPI_MakeChamfer.
  • Example:
    if let bevelled = Shape.box(width: 10, height: 10, depth: 10)?.chamfered(distance: 0.5) { }
    

chamferedTwoDistances(_:)

Chamfer specific edges with two different distances (asymmetric).

public func chamferedTwoDistances(_ edges: [(edgeIndex: Int, faceIndex: Int, dist1: Double, dist2: Double)]) -> Shape?

Each entry specifies an edge, a reference face adjacent to that edge, and two distances. dist1 is measured on the reference face side, dist2 on the opposite side.

  • Parameters: edges, array of (edgeIndex, faceIndex, dist1, dist2) tuples.
  • Returns: Chamfered shape, or nil on failure.
  • OCCT: BRepFilletAPI_MakeChamfer (via OCCTShapeChamferTwoDistances).
  • Example:
    if let c = box.chamferedTwoDistances([(edgeIndex: 0, faceIndex: 0, dist1: 1.0, dist2: 2.0)]) { }
    

chamferedDistAngle(_:)

Chamfer specific edges with a distance and angle.

public func chamferedDistAngle(_ edges: [(edgeIndex: Int, faceIndex: Int, distance: Double, angleDegrees: Double)]) -> Shape?

Each entry specifies an edge, a reference face adjacent to that edge, a distance measured on the reference face, and a chamfer angle in degrees (must be between 0 and 90, exclusive).

  • Parameters: edges, array of (edgeIndex, faceIndex, distance, angleDegrees) tuples.
  • Returns: Chamfered shape, or nil on failure.
  • OCCT: BRepFilletAPI_MakeChamfer (via OCCTShapeChamferDistAngle).

shelled(thickness:)

Thicken a non-closed shell or face into a solid, via BRepOffsetAPI_MakeThickSolid::MakeThickSolidBySimple.

public func shelled(thickness: Double) -> Shape?
  • Parameters: thickness, offset distance passed straight through to the underlying OCCT call.
  • Returns: The thickened solid, or nil on failure, including whenever the receiver is closed.
  • OCCT: BRepOffsetAPI_MakeThickSolid (via OCCTShapeShell).
  • Warning: MakeThickSolidBySimple takes a non-closed shell or face as input, not a closed solid, per OCCT’s own header comment, and its only two callers in OCCT’s own tree each hand it a single extracted face. Measured on a closed box, cylinder and sphere at several thicknesses and both signs, this always returns nil on a closed solid (#2739). Use shelled(thickness:openFaces:) with at least one open face to hollow a closed solid.
  • The routing is deliberate, and #2739 settled it. The alternative, MakeThickSolidByJoin with an empty closing-face list, does not hollow a closed solid either: measured, it returns the plain offset solid, a 24-box from a 20-box at +2.0, one shell and six faces, the same geometry BRepOffsetAPI_MakeOffsetShape::PerformByJoin returns and therefore what offset(by:) already gives. Hollowing needs at least one closing face. An explicit closedness guard was measured and declined as well: BRep_Tool::IsClosed answers only for TopAbs_SHELL and TopAbs_WIRE, so it returns false for the closed TopoDS_Solid the kernel refuses, and IsDone() is the kernel’s own answer. Evidence: Scripts/repro/2739-shelled-single-argument-routing/.
  • The precondition is a free boundary, and that is the mechanism. #2830 read the error code off the algorithm itself, which BRepOffsetAPI_MakeThickSolid does not expose: a closed box solid, a closed cylinder, a closed sphere and even the box’s own closed TopoDS_Shell all stop with BRepOffsetSimple_ErrorInvalidNbShells, “Result contains two or more shells”. The offset succeeds; the solid-building step is what refuses. BRepOffset_MakeSimpleOffset::BuildMissingWalls takes the result solid’s side walls from ShapeAnalysis_FreeBounds(input).GetClosedWires(), a closed input has zero free wires, so no walls are built and BRepTools_Quilt sews the input skin and the offset skin into two disjoint closed shells. An open shell of five box faces has one free closed wire, gets its walls, and is accepted. This is also why BRep_Tool::IsClosed cannot be used as the guard: the refused box solid answers false and so does the accepted open shell. Evidence: Scripts/repro/2830-openshell-fixture/.
  • A positive thickness returns a reversed solid. It offsets along the face normals and the kernel does not normalise the result’s orientation, so volume returns nil and signedVolume a negative number; a negative thickness gives a forward solid. OCCT’s own ThickSolidLargerVolume test reads the figure through std::abs. Call orientedForward() for the forward solid.
  • Warning: The offset surfaces this produces (Geom_OffsetSurface) can hang the mesher unboundedly on pathological input, see the warning on mesh(linearDeflection:angularDeflection:) (#286).
  • Example:
    let box = Shape.box(width: 10, height: 10, depth: 10)!
    
    // A single face thickened into a slab: the accepted domain.
    let slab = Shape.fromFace(box.faces()[0])!.shelled(thickness: 2.0)
    slab?.signedVolume          // -200, the face's 100 area times 2, reversed
    
    // Returns nil at either sign: `box` is a closed solid (#2739).
    box.shelled(thickness: 1.0)
    box.shelled(thickness: -1.0)
    

hollowed(removingFaces:thickness:tolerance:joinType:)

Remove the specified faces and shell the rest to a uniform wall thickness, the removed faces become the openings.

public func hollowed(removingFaces faceIndices: [Int],
                     thickness: Double,
                     tolerance: Double = 1e-3,
                     joinType: OffsetJoinType = .arc) -> Shape?

Pass the 0-based indices of the faces to remove; they become the openings. Returns nil if faceIndices is empty.

  • Parameters:
    • faceIndices: 0-based indices of the faces to remove (they become openings).
    • thickness: wall thickness (positive = outward, negative = inward), matching offset(by:) and shelled(thickness:openFaces:) (#2736).
    • tolerance: tolerance for the operation (default 1e-3).
    • joinType: how to join offset edges (default .arc).
  • Returns: Hollowed solid, or nil on failure.
  • OCCT: BRepOffsetAPI_MakeThickSolid (via OCCTShapeMakeThickSolid).
  • Warning: As with shelled(thickness:), the resulting offset surfaces can hang the mesher on pathological input (#286).
  • Example:
    // An open-topped box: remove face 5, keep 1 mm walls.
    if let tray = Shape.box(width: 20, height: 20, depth: 10)?
                       .hollowed(removingFaces: [5], thickness: 1) { }
    

offset(by:) (simple)

Offset all faces by a distance.

public func offset(by distance: Double) -> Shape?
  • Parameters: distance, offset distance (positive = outward, negative = inward).
  • Returns: Offset shape, or nil on failure. A null result that OCCT reports as done is nil too (#3061); it used to be a non-nil Shape with isNull == true. A NaN or infinite distance is nil too (#3455); it used to return a shape built on NaN geometry.
  • OCCT: BRepOffsetAPI_MakeOffsetShape (via OCCTShapeOffset).
  • Example:
    if let grown = box.offset(by: 1.0) { }
    

offset(by:tolerance:joinType:removeInternalEdges:)

Offset all faces using the proper join algorithm (PerformByJoin).

public func offset(by distance: Double, tolerance: Double = 1e-7,
                   joinType: OffsetJoinType = .arc,
                   removeInternalEdges: Bool = false) -> Shape?

More robust than the simple offset(by:) overload; handles gap-filling between parallel faces.

  • Parameters:
    • distance: offset distance (positive = outward, negative = inward).
    • tolerance: coincidence tolerance (default 1e-7).
    • joinType: how to fill gaps between offset faces: .arc (smooth), .tangent, or .intersection (sharp).
    • removeInternalEdges: whether to clean up internal edges.
  • Returns: Offset shape, or nil on failure. A null result that OCCT reports as done is nil too (#3061); it used to be a non-nil Shape with isNull == true. An inward offset deeper than the shape is thick is the deterministic case: a 10 mm cube offset by -6 with .arc collapses. A NaN or infinite distance or tolerance is nil as well (#3455): the .arc join never returned on it for a box and faulted for a cylinder.
  • OCCT: BRepOffsetAPI_MakeOffsetShape::PerformByJoin (via OCCTShapeOffsetByJoin).
  • Example:
    if let grown = box.offset(by: 1.0, joinType: .intersection) { }
    let cube = Shape.box(width: 10, height: 10, depth: 10)!
    print(cube.offset(by: -6.0, joinType: .arc) == nil)  // true: the cube collapses
    

Transformations

translated(by:)

Translate the shape.

public func translated(by offset: SIMD3<Double>) -> Shape?
  • Parameters: offset, translation vector.
  • Returns: Translated shape, or nil on failure.
  • OCCT: BRepBuilderAPI_Transform (via OCCTShapeTranslate).
  • Example:
    if let moved = box.translated(by: SIMD3(5, 0, 0)) { }
    

rotated(axis:angle:)

Rotate the shape around an axis through the origin.

public func rotated(axis: SIMD3<Double>, angle: Double) -> Shape?
  • Parameters: axis, rotation axis vector (need not be unit length); angle, angle in radians.
  • Returns: Rotated shape, or nil on failure.
  • OCCT: BRepBuilderAPI_Transform / gp_Trsf (via OCCTShapeRotate).
  • Example:
    if let tilted = box.rotated(axis: SIMD3(0, 0, 1), angle: .pi / 4) { }
    

scaled(by:)

Scale the shape uniformly from the origin.

public func scaled(by factor: Double) -> Shape?
  • Parameters: factor, uniform scale factor.
  • Returns: Scaled shape, or nil on failure.
  • OCCT: BRepBuilderAPI_Transform / gp_Trsf (via OCCTShapeScale).
  • Example:
    if let big = box.scaled(by: 2.0) { }
    

mirrored(planeNormal:planeOrigin:)

Mirror the shape across a plane.

public func mirrored(planeNormal: SIMD3<Double>, planeOrigin: SIMD3<Double> = .zero) -> Shape?
  • Parameters: planeNormal, normal of the mirror plane; planeOrigin, point on the mirror plane (default origin).
  • Returns: Mirrored shape, or nil on failure.
  • OCCT: BRepBuilderAPI_Transform / gp_Trsf (via OCCTShapeMirror).
  • Example:
    // Mirror a shape across the XZ plane (normal = +Y)
    if let reflected = part.mirrored(planeNormal: SIMD3(0, 1, 0)) { }
    

Compound Operations

Shape.compound(_:)

Combine multiple shapes into a compound (grouping only, no boolean merge).

public static func compound(_ shapes: [Shape]) -> Shape?
  • Parameters: shapes, array of shapes to group.
  • Returns: A TopoDS_Compound holding all shapes, or nil if the array is empty, if any element is null, or if creation fails.
  • OCCT: BRep_Builder::MakeCompound / BRep_Builder::Add (via OCCTShapeCreateCompound). TopoDS_Builder::Add dereferences the component’s TShape in its first statement, so a null element (from Shape.nullified) used to crash the process here. It is refused now, and the whole call is refused rather than the element skipped: a silently dropped member is one the caller asked for and did not get (#1026).
  • Example:
    guard let box = Shape.box(width: 10, height: 5, depth: 3),
          let cyl = Shape.cylinder(radius: 2, height: 8) else { return }
    if let group = Shape.compound([box, cyl]) {
        // group.isValid == true; shapes remain separate
    }
    

Conversion

Shape.fromWire(_:)

Wrap a Wire as a Shape to access edge extraction and other Shape methods.

public static func fromWire(_ wire: Wire) -> Shape?

Since TopoDS_Wire inherits from TopoDS_Shape in OCCT, this is a lightweight conversion that enables using Shape methods (e.g. allEdgePolylines()) on wire geometry without creating solid geometry.

  • Parameters: wire, the wire to wrap.
  • Returns: A Shape wrapping the wire, or nil on failure.
  • OCCT: no OCCT call; a TopoDS_Wire is a TopoDS_Shape, so the bridge rewraps it (via OCCTShapeFromWire).
  • Example:
    if let wireAsShape = Shape.fromWire(myWire) {
        let polylines = wireAsShape.allEdgePolylines()
    }
    

Shape.fromEdge(_:)

Wrap an Edge as a Shape to use it with Shape-based APIs.

public static func fromEdge(_ edge: Edge) -> Shape?

Since TopoDS_Edge inherits from TopoDS_Shape in OCCT, this is a lightweight conversion.

  • Parameters: edge, the edge to wrap.
  • Returns: A Shape wrapping the edge, or nil on failure.
  • OCCT: no OCCT call; a TopoDS_Edge is a TopoDS_Shape, so the bridge rewraps it (via OCCTShapeFromEdge).

Shape.fromFace(_:)

Wrap a Face as a Shape to use it with Shape-based APIs.

public static func fromFace(_ face: Face) -> Shape?

Since TopoDS_Face inherits from TopoDS_Shape in OCCT, this is a lightweight conversion.

  • Parameters: face, the face to wrap.
  • Returns: A Shape wrapping the face, or nil on failure.
  • OCCT: no OCCT call; a TopoDS_Face is a TopoDS_Shape, so the bridge rewraps it (via OCCTShapeFromFace).

Validation

isValid

Check if the shape is topologically and geometrically valid.

public var isValid: Bool { get }
  • Returns: true if the shape passes OCCT’s checker; false otherwise.
  • OCCT: BRepCheck_Analyzer (via OCCTShapeIsValid).
  • Note: false is also the answer for two shapes BRepCheck_Analyzer itself cannot survive, both reachable through a .brep file and neither constructible through this API: a face edge with no valid 3D curve and at least one pcurve (#2746), and a face with no surface (#2777, and #2789 for the analyzer’s own fault). OCCT’s checker calls both invalid where it manages to answer at all, so the refusal agrees with the kernel rather than standing in for it. Every Exporter write method reaches this property first, so an export of either shape throws ExportError.invalidShape.
  • Example:
    guard let box = Shape.box(width: 10, height: 10, depth: 10),
          box.isValid else { return }
    

healed()

Attempt to repair and heal the shape.

public func healed() -> Shape?
  • Returns: A healed shape, or nil if healing fails entirely.
  • OCCT: ShapeFix_Shape (via OCCTShapeHeal).
  • Note: Healing fixes common issues (gaps, bad tolerances, degenerate edges) but cannot repair fundamentally broken topology. Check isValid after healing.
  • Example:
    guard let repaired = importedShape.healed(), repaired.isValid else { return }
    

Meshing

mesh(linearDeflection:angularDeflection:)

Generate a triangulated mesh for visualisation.

public func mesh(
    linearDeflection: Double = 0.1,
    angularDeflection: Double = 0.5
) -> Mesh?
  • Parameters:
    • linearDeflection: maximum chord deviation from curved faces (smaller = finer mesh, default 0.1).
    • angularDeflection: maximum angular deviation in radians (default 0.5).
  • Returns: A Mesh containing triangles and normals, or nil on failure.
  • OCCT: BRepMesh_IncrementalMesh.
  • Example:
    if let mesh = box.mesh(linearDeflection: 0.05, angularDeflection: 0.3) {
        // use mesh.triangles, mesh.normals, etc.
    }
    
  • ⚠️ Can take minutes on a degenerate offset surface. The geometry shelled(thickness:) / offset(by:) produce (Geom_OffsetSurface) can be enormously expensive to mesh. It is not a hang: measured on the fitted-then-offset B-spline panel from #286, one face took 249 s and produced 1.4 M triangles at a coarse deflection (1/638 of the bbox diagonal). It terminates, earlier reports of an unbounded hang were timeouts set below 249 s.

    Cause: the offset surface is self-intersecting, and the mesher’s angular criterion cannot converge on it. Offsetting by more than the local radius of curvature produces cusps: on the #286 panel the basis fit’s minimum principal curvature radius is 2.6e-05 against an offset of 1.27, so 23.8 % of the domain is cusped and the normal swings by up to π across one. BRepMesh splits any triangle link whose end normals differ by more than AngleInterior (= 2 × angularDeflection), but at a normal discontinuity splitting never helps, halving a link that straddles a cusp just moves the cusp into one half. BRepMesh_DelaunayDeflectionControlMeshAlgo::optimizeMesh therefore runs all 11 passes demanding ~80 k splits each, long after linear deflection is met (1.82 against a 2.48 target by pass 6); MinSize (= linearDeflection / 10) is the only backstop.

    This is invalid input, not an OCCT defect, a well-formed offset surface meshes normally. Note isValid will not catch it (the offending solid reports isValid == true): a self-intersecting offset surface is still a topologically valid face.

    Mitigations, best first:

    1. Sanity-check before meshing. bounds is a cheap Bnd_Box query (no tessellation), comparing a thickened solid’s bbox against its source’s rejects the ballooned offsets that trigger this.
    2. Bound it with a deadline. meshWithProgress interrupts this reliably, a 10 s deadline returns in 10.1 s on the #286 face.
    3. Raise angularDeflection (it sets the AngleInterior threshold doing all the splitting), or raise MinSize via mesh(parameters:) so the backstop bites sooner.
    4. withSurfacesAsBSpline(offset: true) approximates the offset surface with a plain B-spline, smoothing the cusps the angular check chokes on: 125 s / 526 k verts on the #286 solid. It resamples the geometry, so it is a rescue path, not a default.
  • Winding always reflects true topological orientation, not a naive transform read (#375). This reads each face’s TopoDS_Face::Orientation() faithfully, but for a valid, closed solid, that orientation is always consistently outward, even after a mirror (negative-determinant transform): mirrored(planeNormal:planeOrigin:) doesn’t naively re-tessellate flipped geometry, OCCT’s BRepBuilderAPI_Transform compensates by flipping each face’s orientation flag. Confirmed empirically: a box and its mirror both mesh 12/12 triangles outward, with the identical FORWARD/REVERSED face split before and after. There is no way, via a valid Shape, to get caller-controlled (“wrong-way”) winding, use Mesh(vertices:normals:indices:) directly for that.

meshWithProgress(linearDeflection:angularDeflection:progress:)

Generate a triangulated mesh with progress and cancellation support.

@discardableResult
public func meshWithProgress(
    linearDeflection: Double = 0.1,
    angularDeflection: Double = 0.5,
    progress: ImportProgress? = nil
) throws -> Shape

Wraps BRepMesh_IncrementalMesh(shape, IMeshTools_Parameters, Message_ProgressRange) so callers can observe meshing progress on large assemblies and cooperatively cancel.

This is the supported way to bound meshing of untrusted geometry. OCCT polls the range densely, before each face (BRepMesh_FaceDiscret), at each deflection-control pass, and per vertex in BRepMesh_Delaun, so a wall-clock deadline in shouldCancel() interrupts even the pathological offset surfaces of #286. Measured on the #286 face, which needs 249 s to mesh in full: a 10 s deadline throws ImportError.cancelled after 10.1 s, having polled 154,898 times.

  • Parameters:
    • linearDeflection, angularDeflection, same as mesh().
    • progress: optional ImportProgress object; call shouldCancel() to abort cooperatively.
  • Returns: self (with triangulations attached) on success.
  • Throws: ImportError.cancelled if the meshing was cancelled cooperatively.
  • OCCT: BRepMesh_IncrementalMesh with Message_ProgressRange (via OCCTShapeIncrementalMeshProgress).
  • Example: bound an untrusted mesh to 10 seconds:
    final class Deadline: ImportProgress, @unchecked Sendable {
        private let start = Date()
        func progress(fraction: Double, step: String) {}
        func shouldCancel() -> Bool { Date().timeIntervalSince(start) > 10 }
    }
    
    do {
        try shape.meshWithProgress(linearDeflection: 0.02, progress: Deadline())
        let mesh = shape.mesh()  // triangulations are now attached
    } catch ImportError.cancelled {
        // Gave up after 10s, the geometry is probably degenerate.
    }
    
  • Fixed in v1.11.1: cancellation previously did not work at all. The bridge used the BRepMesh_IncrementalMesh(shape, linDefl, isRelative, angDefl) constructor, which calls Perform() internally with a null progress range: the whole mesh was built uninterruptibly before the range was polled, and the shape was then meshed a second time. Cancellation still threw (UserBreak() was checked afterwards), which is why v1.10.2 documented this as an OCCT limitation.

mesh(parameters:)

Generate a triangulated mesh with enhanced parameters.

public func mesh(parameters: MeshParameters) -> Mesh?

Provides fine-grained control over tessellation quality, useful for CAM toolpath generation or high-quality visualisation.

  • Parameters: parameters, a MeshParameters struct with deflection, parallelism, and other options.
  • Returns: A Mesh with the specified quality settings, or nil on failure.
  • OCCT: BRepMesh_IncrementalMesh (via OCCTShapeCreateMeshWithParams).
  • Example:
    var params = MeshParameters.default
    params.deflection = 0.02   // very fine mesh
    params.inParallel = true   // multi-threaded
    if let mesh = shape.mesh(parameters: params) { }
    
  • Same winding guarantee as mesh(linearDeflection:angularDeflection:) above (#375).

Edge Discretization

edgePolyline(at:deflection:maxPoints:)

Get a discretised edge as a polyline.

public func edgePolyline(
    at index: Int,
    deflection: Double = 0.1,
    maxPoints: Int = 1000
) -> [SIMD3<Double>]?

Adaptively samples points along a B-Rep edge using curvature-based deflection control. Useful for contour toolpath generation, edge visualisation, and G-code generation.

  • Parameters:
    • index: edge index (0-based).
    • deflection: maximum chord deviation. Must be at least Precision::Confusion() (1e-7); below that the result is nil (#2872). GCPnts_TangentialDeflection’s own check on it is compiled out of the pinned Release kernel, and without it the sampler subdivides to an internal million-point cap, of which maxPoints returns the leading fraction of a percent of the edge with nothing marking it as partial.
    • maxPoints: output capacity, clamped into 0...Sampling.maximumSampleCount (10,000,000), so an unservable capacity returns the same points rather than a coarser sampling; 0 or less returns nil (#558). The deflection decides the actual point count.
  • Returns: Array of 3D points along the edge, or nil if the edge is not found.
  • OCCT: GCPnts_TangentialDeflection / BRep_Tool::Curve (via OCCTShapeGetEdgePolyline).
  • Example:
    if let pts = shape.edgePolyline(at: 0, deflection: 0.01) {
        // pts is [SIMD3<Double>], contour points
    }
    
  • Note: Edge indices may vary between runs for complex shapes; iterate to find a working index.
  • Performance: This rebuilds the shape’s edge map on every call. Use it for one-off lookups; to walk every edge use allEdgePolylines(deflection:maxPointsPerEdge:), which builds the map once, looping this instead is O(edges²) (#275).

allEdgePolylines(deflection:maxPointsPerEdge:)

Get all edges as discretised polylines.

public func allEdgePolylines(
    deflection: Double = 0.1,
    maxPointsPerEdge: Int = 1000
) -> [[SIMD3<Double>]]

Discretises every edge in a single bridge pass, building the shape’s edge map once. Also calls buildCurves3d(tolerance:) at 1e-5 beforehand to ensure lofted/swept shapes (which may have only pcurves) have explicit 3D curves before discretisation.

  • Parameters:
    • deflection: maximum chord deviation per edge. Must be at least Precision::Confusion() (1e-7); below that the result is [] (#2872), on the same reasoning as edgePolyline(at:deflection:maxPoints:).
    • maxPointsPerEdge: per-edge capacity, honoured within 2...Sampling.maximumSampleCount (10,000,000); outside that range the result is [] (#558).
  • Returns: Array of polylines, one per edge. Edges that fail discretisation (including degenerate ones) are skipped, the result is dense, so a polyline’s position does not reliably equal its edge index; use allEdgePolylinesIndexed when that mapping matters.
  • OCCT: BRepLib::BuildCurves3d + GCPnts_TangentialDeflection (via OCCTBRepLibBuildCurves3dForShape and OCCTShapeComputeAllEdgePolylines).
  • Performance: Prefer this over a for i in 0..<shape.edgeCount { shape.edgePolyline(at: i) } loop. edgePolyline(at:) rebuilds the edge map on every call, so that loop is O(edges²), 15 s for a 12k-edge shape, versus 0.02 s here (#275).
  • Example:
    let wireframe = shape.allEdgePolylines(deflection: 0.05)
    for polyline in wireframe {
        // draw polyline.map { CGPoint(x: $0.x, y: $0.y) }
    }
    

allEdgePolylinesIndexed(deflection:maxPointsPerEdge:)

As allEdgePolylines, but each polyline carries its original edge index.

public func allEdgePolylinesIndexed(
    deflection: Double = 0.1,
    maxPointsPerEdge: Int = 1000
) -> [(edgeIndex: Int, points: [SIMD3<Double>])]

Same single bulk bridge pass as allEdgePolylines (O(edges), #275), but the skip of a degenerate/failed edge (e.g. a sphere’s pole seams) no longer shifts later polylines out of the edgePolyline(at:) / edge(at:) index space, each pair records which edge it came from.

  • Parameters: as allEdgePolylines.
  • Returns: (edgeIndex, points) pairs, ascending by edgeIndex, one per successfully discretised edge.
  • Use when: mapping wireframe polylines back to topology, per-segment edge pick indices, polyline → TopoDS_Edge round-trips. Use the plain variant when only the geometry matters.
  • Example:
    for (edgeIndex, points) in shape.allEdgePolylinesIndexed(deflection: 0.05) {
        register(polyline: points, pickTarget: shape.edge(at: edgeIndex))
    }
    

Import

Shape.load(from:progress:)

Load a shape from a STEP file (URL form).

public static func load(from url: URL, progress: ImportProgress? = nil) throws -> Shape

Convenience alias for loadSTEP(fromPath:progress:).

  • Parameters: url, URL to the STEP file; progress, optional progress/cancellation channel.
  • Returns: Imported shape.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on other failure.
  • OCCT: STEPControl_Reader (via OCCTImportSTEPProgress).
  • Example:
    guard let url = Bundle.main.url(forResource: "part", withExtension: "step") else { return }
    let shape = try Shape.load(from: url)
    

Shape.load(fromPath:progress:)

Load a shape from a STEP file path.

public static func load(fromPath path: String, progress: ImportProgress? = nil) throws -> Shape
  • Parameters: path, file system path to the STEP file; progress, optional progress/cancellation.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader (via OCCTImportSTEPProgress).

Shape.loadSTEP(from:progress:)

Load a shape from a STEP file (alias with explicit naming).

public static func loadSTEP(from url: URL, progress: ImportProgress? = nil) throws -> Shape

Explicit alias for load(from:progress:). Identical behaviour.

  • OCCT: STEPControl_Reader.

Shape.loadSTEP(fromPath:progress:)

Load a shape from a STEP file path with optional progress.

public static func loadSTEP(fromPath path: String, progress: ImportProgress? = nil) throws -> Shape
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader (via OCCTImportSTEPProgress).

STEP Reader Control

Shape.stepRootCount(url:)

Get the number of transferable roots in a STEP file.

public static func stepRootCount(url: URL) -> Int

Use this to inspect a STEP file before importing specific roots with loadSTEPRoot(from:rootIndex:).

  • Parameters: url, URL to the STEP file.
  • Returns: Number of roots (0 if the file cannot be read).
  • OCCT: STEPControl_Reader::TransferRoots (via OCCTSTEPReaderNbRoots).
  • Example:
    let count = Shape.stepRootCount(url: stepURL)
    for i in 1...count {
        let root = try Shape.loadSTEPRoot(from: stepURL, rootIndex: i)
    }
    

Shape.stepRootCount(path:)

Get the number of transferable roots in a STEP file (path form).

public static func stepRootCount(path: String) -> Int
  • OCCT: STEPControl_Reader (via OCCTSTEPReaderNbRoots).

Shape.loadSTEPRoot(from:rootIndex:)

Import a specific root from a STEP file (1-based index).

public static func loadSTEPRoot(from url: URL, rootIndex: Int) throws -> Shape
  • Parameters: url, URL to the STEP file; rootIndex, 1-based root index.
  • Returns: The imported shape for that root.
  • Throws: ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader::TransferRoot (via OCCTImportSTEPRoot).

Shape.loadSTEPRoot(fromPath:rootIndex:)

Import a specific root from a STEP file by path (1-based index).

public static func loadSTEPRoot(fromPath path: String, rootIndex: Int) throws -> Shape
  • OCCT: STEPControl_Reader (via OCCTImportSTEPRoot).

Shape.loadSTEP(from:unitInMeters:progress:)

Import a STEP file, scaling the imported geometry into a specific target length unit.

unitInMeters names the target unit: the length, in meters, of one unit in the returned shape’s coordinate system. OCCT reads the length unit the STEP file itself declares in its header and scales the transferred geometry so the returned shape’s numbers come out in unitInMeters, whatever unit the file was authored in.

public static func loadSTEP(from url: URL, unitInMeters: Double, progress: ImportProgress? = nil) throws -> Shape
  • Parameters:
    • url: URL to the STEP file.
    • unitInMeters: length, in meters, of one unit in the returned shape’s coordinate system (e.g. 0.001 for millimeters, 1.0 for meters, 0.0254 for inches).
    • progress: optional progress/cancellation channel.
  • Returns: The imported shape, scaled into the specified unit system.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader::SetSystemLengthUnit (via OCCTImportSTEPWithUnitProgress).
  • Example:
    // A file authored in millimeters, imported unscaled (target unit = 1 mm).
    let mmShape = try Shape.loadSTEP(from: url, unitInMeters: 0.001)
    // The same file, imported scaled down into meters instead (target unit = 1 m).
    let meterShape = try Shape.loadSTEP(from: url, unitInMeters: 1.0)
    

Shape.loadSTEP(fromPath:unitInMeters:progress:)

Import a STEP file with a specific system length unit (path form).

public static func loadSTEP(fromPath path: String, unitInMeters: Double, progress: ImportProgress? = nil) throws -> Shape
  • OCCT: STEPControl_Reader (via OCCTImportSTEPWithUnitProgress).

Shape.stepShapeCount(url:)

Get the number of shapes in a STEP file after full transfer.

public static func stepShapeCount(url: URL) -> Int
  • Returns: Number of shapes (0 if the file cannot be read).
  • OCCT: STEPControl_Reader::NbShapes (via OCCTSTEPReaderNbShapes).

Shape.stepShapeCount(path:)

Get the number of shapes in a STEP file after full transfer (path form).

public static func stepShapeCount(path: String) -> Int
  • OCCT: STEPControl_Reader (via OCCTSTEPReaderNbShapes).

Robust STEP Import

Shape.loadRobust(from:progress:)

Load a STEP file with robust handling: sewing, solid creation, and shape healing.

public static func loadRobust(from url: URL, progress: ImportProgress? = nil) throws -> Shape

Recommended for STEP files that may contain disconnected faces needing sewing, shells needing conversion to solids, or geometry issues requiring healing.

Every phase is bounded by progress: the transfer spans fraction 0…0.5 and the repair (sewing, then healing) 0.5…1.0, matching their measured cost, repair is ~50% of the work on a solid. A wall-clock deadline in shouldCancel() therefore bounds the whole call, and cancelling mid-repair throws rather than returning the partially-repaired shape.

  • Parameters: url, URL to the STEP file; progress, optional progress/cancellation channel.
  • Returns: Processed shape, a plain solid for a single-body file, a compound of solids when the file holds several bodies.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader + BRepBuilderAPI_Sewing + BRepBuilderAPI_MakeSolid + ShapeFix_Shape (via OCCTImportSTEPRobustProgress).
  • Example:
    let shape = try Shape.loadRobust(from: stepURL)
    print(shape.isValid)   // typically true
    
  • Example, bounding an untrusted import with a deadline:
    final class Deadline: ImportProgress, @unchecked Sendable {
        private let start = Date()
        func progress(fraction: Double, step: String) { print("\(Int(fraction * 100))%") }
        func shouldCancel() -> Bool { Date().timeIntervalSince(start) > 10 }
    }
    
    do {
        let shape = try Shape.loadRobust(from: stepURL, progress: Deadline())
        print(shape.isValid)
    } catch ImportError.cancelled {
        // Gave up after 10s, repairing untrusted geometry can be pathological.
    }
    
  • Limitation, parsing is not covered: OCCT’s STEPControl_Reader::ReadFile takes no Message_ProgressRange, so the file is parsed before the progress indicator exists. Reading a large STEP file reports nothing and cannot be cancelled; fraction and the deadline bound only the transfer and repair that follow.
  • Multibody (v1.11.3): every body is sewn and solidified. Don’t assume .solid, check shapeType or iterate subShapes(ofType: .solid):
    let shape = try Shape.loadRobust(from: stepURL)
    let bodies = shape.shapeType == .solid ? [shape] : shape.subShapes(ofType: .solid)
    print("\(bodies.count) bodies")
    
  • Fixed in v1.11.3: every body after the first was silently discarded, a 10-body STEP returned 1 body as a valid solid, with no error (#302).
  • New in v1.11.2: progress:. The progress-capable bridge function existed but no Swift API reached it, so a robust STEP import could not be observed or cancelled at all (#300). Existing loadRobust(from:) call sites are unaffected, the parameter defaults to nil.

Shape.loadRobust(fromPath:progress:)

Load a STEP file with robust handling from a file path.

public static func loadRobust(fromPath path: String, progress: ImportProgress? = nil) throws -> Shape
  • OCCT: STEPControl_Reader + sewing + solid creation + healing (via OCCTImportSTEPRobustProgress).
  • Example:
    let shape = try Shape.loadRobust(fromPath: "/tmp/part.step")
    

Shape.loadWithDiagnostics(from:)

Load a STEP file with diagnostic information about the processing steps applied.

public static func loadWithDiagnostics(from url: URL) throws -> ImportResult

Returns an ImportResult struct containing the shape and information about what processing (sewing, solid creation, healing) was applied.

  • Parameters: url, URL to the STEP file.
  • Returns: ImportResult with shape, originalType, resultType, sewingApplied, solidCreated, solidsCreated, and healingApplied.
  • Throws: ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: STEPControl_Reader + OCCTImportSTEPWithDiagnostics.
  • Example:
    let result = try Shape.loadWithDiagnostics(from: stepFile)
    print(result.summary)   // e.g. "Shell → Solid (processing: sewing, solid creation, healing)"
    let shape = result.shape
    
  • Example, how many bodies did the file hold?
    let result = try Shape.loadWithDiagnostics(from: stepFile)
    if result.solidsCreated > 1 {
        print("\(result.solidsCreated) bodies, result.shape is a compound of solids")
    }
    
  • solidsCreated (new in v1.11.3): how many shells became solids. > 1 means a multibody file and shape is a compound of that many solids. Before v1.11.3 every body after the first was silently discarded, so this count is the fact that was quietly wrong, a truncated import still returned a valid solid (#302).

IGES Import

Shape.loadIGES(from:progress:)

Load a shape from an IGES file.

public static func loadIGES(from url: URL, progress: ImportProgress? = nil) throws -> Shape

IGES is a legacy CAD format still commonly used in manufacturing and older CAD systems.

  • Parameters: url, URL to the .igs or .iges file; progress, optional progress/cancellation.
  • Returns: Imported shape.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: IGESControl_Reader (via OCCTImportIGESProgress).
  • Example:
    let shape = try Shape.loadIGES(from: igesURL)
    

Shape.loadIGES(fromPath:progress:)

Load a shape from an IGES file path.

public static func loadIGES(fromPath path: String, progress: ImportProgress? = nil) throws -> Shape
  • OCCT: IGESControl_Reader (via OCCTImportIGESProgress).

Shape.loadIGESRobust(from:progress:)

Load an IGES file, healing the transferred geometry.

public static func loadIGESRobust(from url: URL, progress: ImportProgress? = nil) throws -> Shape

Recommended for IGES files whose geometry needs repair before use. This heals but does not sew, unlike loadRobust(from:), which sews STEP shells into solids. (Documented as “sewing and healing” before v1.11.2; the IGES path has never called BRepBuilderAPI_Sewing.)

Both phases are bounded by progress: transfer spans fraction 0…0.5 and healing 0.5…1.0, matching their measured cost, healing is 38–50% of the work (measured across box/sphere/cylinder/torus compounds), not a coda to the transfer. A wall-clock deadline in shouldCancel() therefore bounds the whole call, and cancelling mid-heal throws rather than returning the partially-healed shape.

  • Parameters: url, URL to the IGES file; progress, optional progress/cancellation channel.
  • Returns: Transferred shape with healing applied.
  • Throws: ImportError.cancelled if cancelled; ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: IGESControl_Reader + ShapeFix_Shape (via OCCTImportIGESRobustProgress).
  • Example:
    final class Deadline: ImportProgress, @unchecked Sendable {
        private let start = Date()
        func progress(fraction: Double, step: String) { print("\(Int(fraction * 100))%") }
        func shouldCancel() -> Bool { Date().timeIntervalSince(start) > 10 }
    }
    
    do {
        let shape = try Shape.loadIGESRobust(from: igesURL, progress: Deadline())
        print(shape.isValid)
    } catch ImportError.cancelled {
        // Gave up after 10s, healing untrusted geometry can be pathological.
    }
    
  • Limitation, parsing is not covered: OCCT’s IGESControl_Reader::ReadFile takes no Message_ProgressRange, so the file is parsed before the progress indicator exists. Reading a large IGES file reports nothing and cannot be cancelled; fraction and the deadline bound only the transfer and healing that follow.
  • Fixed in v1.11.2: healing previously ran outside the caller’s progress range entirely (ShapeFix_Shape::Perform() was called with no range), so shouldCancel() during healing was ignored, the heal ran to completion and the call returned a shape rather than reporting cancellation. Same family as the #286 defect fixed in v1.11.1 (#300).

Shape.loadIGESRobust(fromPath:progress:)

Load an IGES file from a path, healing the transferred geometry.

public static func loadIGESRobust(fromPath path: String, progress: ImportProgress? = nil) throws -> Shape
  • OCCT: IGESControl_Reader + ShapeFix_Shape (via OCCTImportIGESRobustProgress).
  • Example:
    let shape = try Shape.loadIGESRobust(fromPath: "/tmp/part.igs")
    

IGES Reader Control

Shape.igesRootCount(url:)

Get the number of transferable roots in an IGES file.

public static func igesRootCount(url: URL) -> Int
  • Returns: Number of roots (0 if the file cannot be read).
  • OCCT: IGESControl_Reader (via OCCTIGESReaderNbRoots).

Shape.igesRootCount(path:)

Get the number of transferable roots in an IGES file (path form).

public static func igesRootCount(path: String) -> Int
  • OCCT: IGESControl_Reader (via OCCTIGESReaderNbRoots).

Shape.loadIGESRoot(from:rootIndex:)

Import a specific root from an IGES file (1-based index).

public static func loadIGESRoot(from url: URL, rootIndex: Int) throws -> Shape
  • Parameters: url, URL to the IGES file; rootIndex, 1-based root index.
  • Throws: ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: IGESControl_Reader::TransferOneRoot (via OCCTImportIGESRoot).

Shape.loadIGESRoot(fromPath:rootIndex:)

Import a specific root from an IGES file by path (1-based index).

public static func loadIGESRoot(fromPath path: String, rootIndex: Int) throws -> Shape
  • OCCT: IGESControl_Reader (via OCCTImportIGESRoot).

Shape.igesShapeCount(url:)

Get the number of shapes in an IGES file after full transfer.

public static func igesShapeCount(url: URL) -> Int
  • OCCT: IGESControl_Reader::NbShapes (via OCCTIGESReaderNbShapes).

Shape.igesShapeCount(path:)

Get the number of shapes in an IGES file after full transfer (path form).

public static func igesShapeCount(path: String) -> Int
  • OCCT: IGESControl_Reader (via OCCTIGESReaderNbShapes).

Shape.loadIGESVisible(from:)

Import only visible entities from an IGES file.

public static func loadIGESVisible(from url: URL) throws -> Shape
  • Parameters: url, URL to the IGES file.
  • Throws: ImportError.readFailed(path:status:), carrying OCCT’s own IFSelect_ReturnStatus as an IOStatus (#1644), on failure.
  • OCCT: IGESControl_Reader with visibility filtering (via OCCTImportIGESVisible).

Shape.loadIGESVisible(fromPath:)

Import only visible entities from an IGES file (path form).

public static func loadIGESVisible(fromPath path: String) throws -> Shape
  • OCCT: IGESControl_Reader (via OCCTImportIGESVisible).

BREP Import

Shape.loadBREP(from:)

Load a shape from OCCT’s native BREP format.

public static func loadBREP(from url: URL) throws -> Shape

BREP is OCCT’s native exact B-Rep format. It preserves full precision and is useful for fast caching of intermediate results, debugging geometry, and archiving exact geometry.

  • Parameters: url, URL to the .brep file.
  • Returns: Imported shape.
  • Throws: ImportError.importFailed on failure, which includes a file that is not a complete BREP.
  • Damaged files: a file that is cut short, or that names a shape, location or curve basis it does not hold, or that declares a count (poles, knots, nodes, triangles) its text cannot supply, is refused through that error. Carried patches 0073 and 0074, which the pinned asset does not carry yet, are what make it so: BRepTools::Read otherwise indexed its shape table with the number a failed extraction left behind (SIGSEGV), looped on a stale count (OCCT#1608), wrote a word of 255 bytes or more past a char[255], and read a number of 32 characters or more as two (OCCT#1560). 0074 is the count rule: a negative pole, knot or node count sized an array backwards, a count past the end of the file sized an array of that many elements, and a triangle or a polygon named a node its triangulation lacks. The kernel reads a file that loses only its trailing white space, but the bridge’s end-of-stream check still refuses it. A shape the reader returns with a face that has no surface, or with an edge that has only a curve on a surface, no longer faults BRepCheck once carried patch 0077 is in the kernel (the bridge already refuses both before the analyzer, so isValid is false either way). Not covered: a coordinate, tolerance or parameter that is not a number or is of the wrong size (a nan trim parameter faults the constructor of the trimmed curve or surface), and a count the bridge’s non-seekable stream cannot measure up to 2^24.
  • OCCT: BRep_Builder / BRepTools::Read (via OCCTImportBREP).
  • Damaged input (#3487): a file that ends early, such as one cut short by an interrupted write, throws ImportError.importFailed. BRepTools::Read faults or never returns on one, and the fault is not catchable, so the bridge reads through a stream that throws where the file ends. Every strict prefix of a written BREP is refused, and so is a file whose trailing whitespace was stripped, because a written BREP ends in a space (+1 0 ) and a number ended by the end of the file is the mark of a cut. A word of 255 bytes or more in the text the reader takes is refused too (the reader copies words into a 255 byte buffer; the bridge checks in 64 KiB blocks, so a long word in trailing junk past the last block it needs is not seen). A file that is complete but wrong, such as a reference to a shape or curve that is not in the file or a count the body does not match, no longer faults the reader since v4.0.0-kernel.8 (#3490): it is refused, or returns what the reader could follow. Measured over 109,801 damaged files, none faulted or hung the reader; see Scripts/repro/3487/README.md. Not covered: what is returned is not validated. The shape can be invalid, and a check or an operation on a damaged one can still fail to return (a pcurve that is a circle of radius 0 hangs isValid, #3506), so check a file from an untrusted source before running anything long on it.
  • Example:
    let shape = try Shape.loadBREP(from: brepURL)
    

Shape.loadBREP(fromPath:)

Load a shape from a BREP file path.

public static func loadBREP(fromPath path: String) throws -> Shape
  • Throws: ImportError.importFailed, including for a file that ends early (see loadBREP(from:) above for what is and is not refused, #3487).
  • OCCT: BRep_Builder / BRepTools::Read (via OCCTImportBREP).

STL Import

Shape.loadSTL(from:)

Load a shape from an STL file.

public static func loadSTL(from url: URL) throws -> Shape
  • Parameters: url, URL to the .stl file.
  • Returns: Imported shape (a shell of triangulated faces).
  • Throws: ImportError.importFailed on failure.
  • OCCT: StlAPI_Reader (via OCCTImportSTL). Builds one planar TopoDS_Face per STL facet (BRepBuilderAPI_MakeShapeOnMesh); faces are unsewn, call sewn(tolerance:) or use loadSTLRobust(from:sewingTolerance:) if you need a connected shell/solid.
  • Winding is preserved exactly, including a global reversal (#375). Each facet’s vertex order is preserved exactly, confirmed empirically by round-tripping a uniformly reversed-winding box and finding all triangles consistently inward, with zero shared-edge orientation conflicts. If a round-tripped mesh comes back with locally inconsistent winding (some faces inward, some outward), the input STL itself is locally inconsistent, this method doesn’t introduce that defect, it faithfully reproduces one already in the file.
  • Example:
    let mesh = try Shape.loadSTL(from: stlURL)
    

Shape.loadSTL(fromPath:)

Load a shape from an STL file path.

public static func loadSTL(fromPath path: String) throws -> Shape
  • OCCT: StlAPI_Reader (via OCCTImportSTL).
  • Same winding-fidelity guarantee as Shape.loadSTL(from:) above.

Shape.loadSTLRobust(from:sewingTolerance:)

Load an STL file with robust healing (sew + solid creation + heal).

public static func loadSTLRobust(from url: URL, sewingTolerance: Double = 1e-6) throws -> Shape
  • Parameters:
    • url: URL to the STL file.
    • sewingTolerance: tolerance for sewing disconnected faces (default 1e-6).
  • Returns: Processed shape, a plain solid for a single-body file, a compound of solids when the mesh holds several disconnected bodies.
  • Throws: ImportError.importFailed on failure.
  • OCCT: StlAPI_Reader + BRepBuilderAPI_Sewing + BRepBuilderAPI_MakeSolid + ShapeFix_Shape (via OCCTImportSTLRobust).
  • Example:
    let solid = try Shape.loadSTLRobust(from: stlURL, sewingTolerance: 1e-5)
    print(solid.isValid)
    
  • Multibody: every disconnected body is sewn and solidified. Don’t assume .solid, check shapeType or iterate subShapes(ofType: .solid):
    let shape = try Shape.loadSTLRobust(from: stlURL)
    let bodies = shape.shapeType == .solid ? [shape] : shape.subShapes(ofType: .solid)
    print("\(bodies.count) bodies")
    
  • Fixed in v1.11.3: every body after the first was silently discarded, a 10-body STL returned 1 body as a valid solid, with no error (#302).

Shape.loadSTLRobust(fromPath:sewingTolerance:)

Load an STL file with robust healing from a path.

public static func loadSTLRobust(fromPath path: String, sewingTolerance: Double = 1e-6) throws -> Shape
  • OCCT: StlAPI_Reader + sewing + healing (via OCCTImportSTLRobust).

OBJ Import

Shape.loadOBJ(from:)

Load a shape from an OBJ file.

public static func loadOBJ(from url: URL) throws -> Shape
  • Parameters: url, URL to the .obj file.
  • Returns: Imported shape.
  • Throws: ImportError.importFailed on failure.
  • OCCT: RWObj_CafReader (via OCCTImportOBJ).
  • Example:
    let shape = try Shape.loadOBJ(from: objURL)
    

Shape.loadOBJ(fromPath:)

Load a shape from an OBJ file path.

public static func loadOBJ(fromPath path: String) throws -> Shape
  • OCCT: RWObj_CafReader (via OCCTImportOBJ).