Link Search Menu Expand Document

Wire

A Wire is a connected sequence of edges (curves) — the Swift analog of OCCT’s TopoDS_Wire. Wires serve two distinct roles: 2D profiles (closed planar cross-sections for extrusion, lofting, or sweeping) and 3D paths (open or closed spine curves along which profiles are swept). Obtain a wire by calling one of the static factory methods or by extracting wire sub-shapes from a Shape.

Topics


Initializers

Wire.init?(_ shape:)

Constructs a Wire by extracting the wire topology from a Shape. Returns nil if the shape is null or wraps a non-wire topology type.

public convenience init?(_ shape: Shape)

Inverse of Shape.fromWire(_:). Use when you have a wire-typed Shape (e.g. from Shape.wires or Shape.subShapes(ofType: .wire)) and need the typed Wire object. Round-trips cleanly with Shape.fromWire(_:).

  • Parameters: shape — a Shape wrapping a TopoDS_Wire.
  • Returns: nil if shape is null or not wire-typed.
  • OCCT: TopoDS::Wire — casts the underlying TopoDS_Shape to TopoDS_Wire after checking ShapeType() == TopAbs_WIRE.
  • Example:
    if let wire = Wire(someShape) {
        let len = wire.length
    }
    

(Internal, not public API: Wire.handle is an internal let handle: OCCTWireRef wrapping the underlying bridge object, released in deinit. See Memory Management.)


2D Profiles (for Extrusion/Sweep)

Wire.rectangle(width:height:)

Creates a rectangular profile centred at the origin in the XY plane.

public static func rectangle(width: Double, height: Double) -> Wire?

Corners are at (±width/2, ±height/2, 0). Both dimensions must exceed Precision::Confusion() (~1e-7).

  • Parameters: width — X dimension; height — Y dimension.
  • Returns: Closed 4-edge rectangular wire, or nil if either dimension ≤ 0 or construction fails.
  • OCCT: BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire — four straight edges assembled into a wire.
  • Example:
    if let rect = Wire.rectangle(width: 10, height: 5) {
        let box = Shape.extrude(profile: rect, direction: SIMD3(0, 0, 1), length: 20)
    }
    

Wire.circle(origin:normal:radius:)

Creates a full closed circular wire.

public static func circle(
    origin: SIMD3<Double> = .zero,
    normal: SIMD3<Double> = SIMD3(0, 0, 1),
    radius: Double
) -> Wire?

The circle lies in the plane perpendicular to normal passing through origin. The default produces a unit circle in the XY plane at the origin.

  • Parameters: origin — centre point; normal — plane normal; radius — circle radius (must be > 0).
  • Returns: Closed single-edge circular wire, or nil on failure.
  • OCCT: gp_Circ + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    if let circle = Wire.circle(origin: SIMD3(0, 0, 5), normal: SIMD3(0, 0, 1), radius: 3) {
        let cyl = Shape.extrude(profile: circle, direction: SIMD3(0, 0, 1), length: 10)
    }
    

Wire.polygon(_:closed:)

Creates a polygon wire from 2D points in the XY plane.

public static func polygon(_ points: [SIMD2<Double>], closed: Bool = true) -> Wire?

Points are connected in order with straight-line edges. Pass closed: true (the default) to add a closing edge from the last point back to the first, producing a profile suitable for extrusion.

  • Parameters: points — 2D vertex positions (minimum 2); closed — whether to close the polygon.
  • Returns: Polygonal wire, or nil if fewer than 2 points or OCCT construction fails (e.g. degenerate/coincident points).
  • OCCT: BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire — one linear edge per consecutive pair of points.
  • Example:
    let railProfile = Wire.polygon([
        SIMD2(0, 0),
        SIMD2(2.5, 0),
        SIMD2(2.5, 0.8),
        SIMD2(1.8, 0.8),
        SIMD2(1.8, 6.5),
        SIMD2(1.0, 7.0),
        SIMD2(0, 7.0),
        SIMD2(0, 0.8),
    ])
    
  • Note: Returns nil if any two consecutive points are coincident (degenerate edge).

Wire.polygon3D(_:closed:)

Creates a polygon wire from 3D points with straight-line edges.

public static func polygon3D(_ points: [SIMD3<Double>], closed: Bool = true) -> Wire?

Uses BRepBuilderAPI_MakePolygon for fast construction of rectilinear wires in 3D space. Prefer polygon(_:closed:) for 2D planar profiles.

  • Parameters: points — 3D vertex positions (minimum 2); closed — whether to close the polygon.
  • Returns: Wire shape, or nil if fewer than 2 points or construction fails.
  • OCCT: BRepBuilderAPI_MakePolygon — optimised builder for rectilinear polygon wires.
  • Example:
    let square = Wire.polygon3D([
        SIMD3(0, 0, 0), SIMD3(10, 0, 0),
        SIMD3(10, 10, 0), SIMD3(0, 10, 0)
    ], closed: true)
    

3D Paths (for Pipe Sweep)

Wire.line(from:to:)

Creates a straight line segment in 3D space.

public static func line(from start: SIMD3<Double>, to end: SIMD3<Double>) -> Wire?

Returns nil if start and end are within 1e-10 of each other (degenerate edge).

  • Parameters: from — start point; to — end point.
  • Returns: Single-edge wire, or nil if degenerate.
  • OCCT: BRepBuilderAPI_MakeEdge(p1, p2) + BRepBuilderAPI_MakeWire.
  • Example:
    if let spine = Wire.line(from: .zero, to: SIMD3(100, 0, 0)) {
        let pipe = Shape.pipeShell(spine: spine, profile: Wire.circle(radius: 5)!)
    }
    

Wire.arc(center:radius:startAngle:endAngle:normal:)

Creates a circular arc in 3D space from centre, radius, and angles.

public static func arc(
    center: SIMD3<Double>,
    radius: Double,
    startAngle: Double,
    endAngle: Double,
    normal: SIMD3<Double> = SIMD3(0, 0, 1)
) -> Wire?

Angles are in radians, measured from the X direction rotated into the plane defined by normal. The arc sweeps from startAngle to endAngle in the direction implied by normal (right-hand rule).

  • Parameters: center — arc centre; radius — arc radius (must be > 0); startAngle/endAngle — angular bounds in radians; normal — plane normal (default Z-up).
  • Returns: Single-arc-edge wire, or nil if radius ≤ 0 or angles are coincident.
  • OCCT: Geom_Circle + Geom_TrimmedCurve + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    let curve = Wire.arc(
        center: SIMD3(0, 0, 0),
        radius: 500,
        startAngle: 0,
        endAngle: .pi / 2,
        normal: SIMD3(0, 1, 0)
    )
    
  • Note: When the bend axis is not a canonical world axis, prefer arc(start:midpoint:end:) to avoid X-direction ambiguity.

Wire.arc(start:midpoint:end:)

Creates a circular arc wire through three specified points.

public static func arc(
    start: SIMD3<Double>,
    midpoint: SIMD3<Double>,
    end: SIMD3<Double>
) -> Wire?

Uses OCCT’s GC_MakeArcOfCircle to derive the centre and radius from the three points. The midpoint resolves the curvature direction. This avoids the X-direction ambiguity of the angle-based arc(center:radius:startAngle:endAngle:normal:) constructor.

  • Parameters: start — first endpoint; midpoint — a point on the arc; end — second endpoint.
  • Returns: Single-arc-edge wire, or nil if the three points are collinear or coincident.
  • OCCT: GC_MakeArcOfCircle + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    if let arc = Wire.arc(
        start: SIMD3(0, 0, 0),
        midpoint: SIMD3(5, 5, 0),
        end: SIMD3(10, 0, 0)
    ) {
        let len = arc.length
    }
    

Wire.path(_:closed:)

Creates a 3D path wire from an array of 3D points connected by straight-line edges.

public static func path(_ points: [SIMD3<Double>], closed: Bool = false) -> Wire?

For a smooth interpolated path, use interpolate(through:) or bspline(_:) instead.

  • Parameters: points — 3D waypoints (minimum 2); closed — if true, adds an edge from last to first point.
  • Returns: Straight-segment wire, or nil if fewer than 2 points or construction fails.
  • OCCT: BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire — one linear edge per consecutive pair.
  • Example:
    if let path = Wire.path([
        SIMD3(0, 0, 0), SIMD3(50, 0, 0), SIMD3(100, 50, 0)
    ]) {
        let swept = Shape.sweep(profile: Wire.circle(radius: 2)!, along: path)
    }
    

Wire.bspline(_:)

Creates a smooth B-spline wire approximated through control points.

public static func bspline(_ controlPoints: [SIMD3<Double>]) -> Wire?

The resulting curve passes near the control points (it is an approximation, not an interpolation). For a curve that passes exactly through every point, use interpolate(through:).

  • Parameters: controlPoints — 3D control points (minimum 2).
  • Returns: Smooth B-spline wire, or nil if fewer than 2 points or GeomAPI_PointsToBSpline fails.
  • OCCT: GeomAPI_PointsToBSplineGeom_BSplineCurve + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    let easement = Wire.bspline([
        SIMD3(0, 0, 0),
        SIMD3(50, 0, 0),
        SIMD3(100, 10, 0),
        SIMD3(150, 30, 0),
        SIMD3(180, 50, 0)
    ])
    

NURBS Curves

Wire.nurbs(poles:weights:knots:multiplicities:degree:)

Creates a NURBS (Non-Uniform Rational B-Spline) curve with full explicit control.

public static func nurbs(
    poles: [SIMD3<Double>],
    weights: [Double]? = nil,
    knots: [Double],
    multiplicities: [Int32]? = nil,
    degree: Int32
) -> Wire?

Passes directly to Geom_BSplineCurve. Provides exact representation of conic sections (circles, ellipses) and is the standard for CAD data exchange. When weights is nil, all weights default to 1.0 (non-rational B-spline). When multiplicities is nil, all knot multiplicities default to 1.

  • Parameters:
    • poles — control points (minimum 2).
    • weights — per-pole weights (nil = uniform 1.0).
    • knots — distinct knot values (minimum 2).
    • multiplicities — per-knot multiplicity (nil = all 1).
    • degree — curve degree (1 = linear, 2 = quadratic, 3 = cubic; must be ≥ 1).
  • Returns: NURBS wire, or nil if parameters are invalid.
  • OCCT: Geom_BSplineCurve constructor + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    // Rational quadratic B-spline — can represent exact quarter-circle arcs
    let poles = [SIMD3(0, 0, 0), SIMD3(1, 1, 0), SIMD3(2, 0, 0)]
    let weights = [1.0, 0.707, 1.0]
    let knots = [0.0, 1.0]
    let mults: [Int32] = [3, 3]
    if let arc = Wire.nurbs(poles: poles, weights: weights,
                            knots: knots, multiplicities: mults, degree: 2) {
        let len = arc.length
    }
    

Wire.nurbsUniform(poles:weights:degree:)

Creates a NURBS curve with an automatically-generated clamped uniform knot vector.

public static func nurbsUniform(
    poles: [SIMD3<Double>],
    weights: [Double]? = nil,
    degree: Int32
) -> Wire?

Simplified NURBS creation: the bridge computes the clamped uniform knot vector automatically. The curve starts at the first control point and ends at the last. Requires at least degree + 1 control points.

  • Parameters: poles — control points (minimum degree + 1); weights — per-pole weights (nil = 1.0); degree — curve degree (≥ 1).
  • Returns: NURBS wire, or nil if poles.count < degree + 1 or construction fails.
  • OCCT: Geom_BSplineCurve + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire (knot/multiplicity arrays computed internally).
  • Example:
    let controlPolygon: [SIMD3<Double>] = [
        SIMD3(0, 0, 0), SIMD3(10, 5, 0),
        SIMD3(20, 0, 0), SIMD3(30, 5, 0), SIMD3(40, 0, 0)
    ]
    if let curve = Wire.nurbsUniform(poles: controlPolygon, degree: 3) {
        let len = curve.length
    }
    

Wire.cubicBSpline(poles:)

Creates a cubic (degree-3) non-rational B-spline wire.

public static func cubicBSpline(poles: [SIMD3<Double>]) -> Wire?

Shorthand for nurbsUniform(poles:weights:degree:) with degree = 3 and uniform weights. Cubic B-splines offer C² continuity (smooth curvature) and good local control. Requires at least 4 control points.

  • Parameters: poles — control points (minimum 4).
  • Returns: Cubic B-spline wire, or nil if fewer than 4 poles or construction fails.
  • OCCT: Delegates to OCCTWireCreateNURBSUniformGeom_BSplineCurve (degree 3, uniform weights).
  • Example:
    let transitionPoles: [SIMD3<Double>] = [
        SIMD3(0, 0, 0), SIMD3(20, 0, 0),
        SIMD3(40, 2, 0), SIMD3(60, 8, 0),
        SIMD3(80, 20, 0), SIMD3(90, 30, 0)
    ]
    if let easement = Wire.cubicBSpline(poles: transitionPoles) {
        let swept = Shape.sweep(profile: Wire.circle(radius: 1)!, along: easement)
    }
    

Wire From Edges

Wire.wireFromEdges(_:)

Creates a wire by assembling individual edges in order.

public static func wireFromEdges(_ edges: [Edge]) -> Wire?

Edges should be geometrically connectable (shared vertices or within tolerance). OCCT connects them during construction.

  • Parameters: edges — array of Edge objects (minimum 1).
  • Returns: Connected wire, or nil if the array is empty or BRepLib_MakeWire fails.
  • OCCT: BRepLib_MakeWire — adds each TopoDS_Edge in order and connects within tolerance.
  • Example:
    let edges = shape.edges()
    if let wire = Wire.wireFromEdges(Array(edges.prefix(3))) {
        let len = wire.length
    }
    

Wire From Curve2D on Plane

Wire.fromCurve2D(_:origin:normal:xAxis:)

Creates a 3D wire by embedding a 2D curve into a geometric plane without discretisation.

public static func fromCurve2D(_ curve: Curve2D,
                               origin: SIMD3<Double> = .zero,
                               normal: SIMD3<Double> = SIMD3(0, 0, 1),
                               xAxis:  SIMD3<Double> = SIMD3(1, 0, 0)) -> Wire?

Lifts the exact parametric representation of a Curve2D into 3D space, preserving the original curve geometry. The 2D U axis maps to xAxis; the 2D V axis maps to normal × xAxis. The resulting wire is suitable for sweep/extrude/loft operations.

  • Parameters:
    • curve — any Curve2D (segment, arc, B-spline, etc.).
    • origin — where the 2D origin maps in 3D.
    • normal — outward normal of the plane (default: Z axis = XY plane).
    • xAxis — 3D direction the 2D X axis maps to (default: X axis).
  • Returns: 3D wire on the specified plane, or nil on failure.
  • OCCT: BRepBuilderAPI_MakeEdge(Geom2d_Curve, Geom_Plane) + BRepLib::BuildCurves3d + BRepBuilderAPI_MakeWire.
  • Example:
    let arc = Curve2D.arcOfCircle(center: .zero, radius: 10,
                                  startAngle: 0, endAngle: .pi)!
    if let wire3D = Wire.fromCurve2D(arc,
                                     origin: SIMD3(0, 0, 5),
                                     normal: SIMD3(0, 0, 1),
                                     xAxis:  SIMD3(1, 0, 0)) {
        // use as profile for Shape.pipeShell(spine:profile:)
    }
    

Wire Composition

Wire.join(_:)

Joins multiple wires into a single connected wire.

public static func join(_ wires: [Wire]) -> Wire?

Wires should be geometrically connected (end of one near the start of the next). OCCT attempts to connect them within tolerance. The result contains all edges from all input wires.

  • Parameters: wires — array of wires to join (minimum 1).
  • Returns: Combined wire, or nil if the array is empty or BRepBuilderAPI_MakeWire fails.
  • OCCT: BRepBuilderAPI_MakeWire::Add(TopoDS_Wire) — adds each wire to the builder in order.
  • Example:
    let s1 = Wire.line(from: .zero, to: SIMD3(100, 0, 0))!
    let arc = Wire.arc(center: SIMD3(100, 50, 0), radius: 50,
                       startAngle: -.pi/2, endAngle: 0)!
    let s2 = Wire.line(from: SIMD3(150, 50, 0), to: SIMD3(150, 200, 0))!
    if let path = Wire.join([s1, arc, s2]) {
        let swept = Shape.pipeShell(spine: path, profile: Wire.circle(radius: 5)!)
    }
    

Curve Analysis

The following members are declared in the Wire extension for curve analysis (v0.9.0). All parametric queries take a normalised parameter in [0, 1] where 0 = wire start and 1 = wire end, mapped linearly across the OCCT parameter range via BRepAdaptor_CompCurve.


curveInfo

Returns comprehensive curve information in a single call.

public var curveInfo: CurveInfo? { get }

Encapsulates length, closure/periodicity status, and the start/end 3D points. More efficient than calling length, point(at:0), and point(at:1) separately.

  • Returns: CurveInfo struct, or nil if the wire is degenerate or OCCT fails.
  • OCCT: BRepAdaptor_CompCurve + GCPnts_AbscissaPoint::Length per GeomAbs_CN interval, subdivided to convergence — the same measurement length makes, so the two cannot disagree (#603).
  • Example:
    if let info = Wire.circle(radius: 5)?.curveInfo {
        print(info.length, info.isClosed)  // ≈ 31.4, true
    }
    

length

Returns the total arc length of the wire.

public var length: Double? { get }
  • Returns: Length in model units, or nil if measurement fails. Returns nil (not 0) for degenerate wires.
  • OCCT: BRepAdaptor_CompCurve + GCPnts_AbscissaPoint::Length per GeomAbs_CN interval, subdivided until two successive levels agree to 1e-9 relative (#603).
  • Note: A BRepAdaptor_CompCurve reports one interval per edge span, so a wire of lines and circles was always exact — but one elliptical edge in it measured 1.485% long before #603.
  • Example:
    let len = Wire.line(from: .zero, to: SIMD3(10, 0, 0))?.length  // 10.0
    

point(at:)

Returns the 3D position on the wire at a normalised parameter.

public func point(at parameter: Double) -> SIMD3<Double>?
  • Parameters: parameter — value in [0, 1] (0 = start, 1 = end).
  • Returns: 3D position, or nil on failure.
  • OCCT: BRepAdaptor_CompCurve::Value(actualParam).
  • Example:
    if let arc = Wire.arc(center: .zero, radius: 10, startAngle: 0, endAngle: .pi) {
        let mid = arc.point(at: 0.5)  // midpoint of the arc
    }
    

tangent(at:)

Returns the unit tangent vector at a normalised parameter.

public func tangent(at parameter: Double) -> SIMD3<Double>?
  • Parameters: parameter — value in [0, 1].
  • Returns: Normalised tangent vector in the direction of travel, or nil on failure.
  • OCCT: BRepAdaptor_CompCurve::D1 — first derivative, then normalised.
  • Example:
    let line = Wire.line(from: .zero, to: SIMD3(10, 0, 0))
    let t = line?.tangent(at: 0.5)  // SIMD3(1, 0, 0)
    

curvature(at:)

Returns the curvature (1/radius) at a normalised parameter.

public func curvature(at parameter: Double) -> Double?

A straight line has curvature 0; a circle of radius R has curvature 1/R.

  • Parameters: parameter — value in [0, 1].
  • Returns: Curvature value ≥ 0, or nil where the wire has none there: a parameter it cannot be evaluated at, or a point whose first derivative is null (a cusp), where the formula divides by zero. That second case returned 0 until #595 — a straight wire’s real answer — because only the error path reached this optional. Unlike Curve3D.curvature(at:) there is no infinity sentinel to report at a cusp: BRepAdaptor_CompCurve computes the formula directly rather than through GeomLProp_CLProps.
  • OCCT: BRepAdaptor_CompCurve::D2 — uses the formula κ = d1 × d2 / d1 ³.
  • Example:
    let circle = Wire.circle(radius: 10)
    let k = circle?.curvature(at: 0.5)  // ≈ 0.1 (1/10)
    

curvePoint(at:)

Returns position, unit tangent, curvature, and principal normal in a single call.

public func curvePoint(at parameter: Double) -> CurvePoint?

More efficient than calling point(at:), tangent(at:), and curvature(at:) separately when multiple values are needed. The normal field is nil when curvature is 0 (straight segment).

  • Parameters: parameter — value in [0, 1].
  • Returns: CurvePoint struct (position, tangent, curvature, optional normal), or nil on failure.
  • OCCT: BRepAdaptor_CompCurve::D2 — position and both derivatives in one call.
  • Example:
    if let cp = Wire.circle(radius: 5)?.curvePoint(at: 0.25) {
        print(cp.position, cp.tangent, cp.curvature)
    }
    

offset3D(distance:direction:)

Translates the entire wire in 3D space along a direction.

public func offset3D(distance: Double, direction: SIMD3<Double>) -> Wire?

This is a rigid translation (not a parallel-curve offset). Use offset(by:joinType:) for planar parallel-curve offsetting.

  • Parameters: distance — translation magnitude; direction — direction vector (normalised internally).
  • Returns: Translated wire, or nil on failure.
  • OCCT: BRepBuilderAPI_Transform with a gp_Trsf translation.
  • Example:
    let bottom = Wire.circle(radius: 5)!
    if let top = bottom.offset3D(distance: 10, direction: SIMD3(0, 0, 1)) {
        // top is a circle at Z=10
        let lofted = Shape.loft(profiles: [bottom, top])
    }
    

Curve Interpolation

Wire.interpolate(through:closed:tolerance:)

Creates a smooth B-spline wire that passes exactly through all specified points.

public static func interpolate(
    through points: [SIMD3<Double>],
    closed: Bool = false,
    tolerance: Double = 1e-6
) -> Wire?

Unlike bspline(_:) (which approximates), the interpolated curve passes through every point. Produces a Geom_BSplineCurve via GeomAPI_Interpolate.

  • Parameters: points — points the curve must pass through (minimum 2); closed — periodic (closed) curve; tolerance — interpolation precision.
  • Returns: Interpolated B-spline wire, or nil on failure.
  • OCCT: GeomAPI_Interpolate + BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire.
  • Example:
    let waypoints: [SIMD3<Double>] = [
        SIMD3(0, 0, 0), SIMD3(10, 5, 0),
        SIMD3(20, 0, 0), SIMD3(30, 5, 0)
    ]
    if let path = Wire.interpolate(through: waypoints) {
        let len = path.length
    }
    

Wire.interpolate(through:startTangent:endTangent:tolerance:)

Creates a smooth interpolating B-spline wire with constrained end tangents.

public static func interpolate(
    through points: [SIMD3<Double>],
    startTangent: SIMD3<Double>,
    endTangent: SIMD3<Double>,
    tolerance: Double = 1e-6
) -> Wire?

Useful for ensuring smooth connections with adjacent curves — the wire enters at startTangent and exits at endTangent. The result is always open (not closed), since tangent constraints are applied at both endpoints.

  • Parameters: points — points the curve must pass through (minimum 2); startTangent — desired tangent direction at points[0]; endTangent — desired tangent direction at points.last; tolerance — interpolation precision.
  • Returns: Interpolated B-spline wire, or nil on failure.
  • OCCT: GeomAPI_Interpolate::Load(startTangent, endTangent) + Perform() + BRepBuilderAPI_MakeEdge.
  • Example:
    let points: [SIMD3<Double>] = [SIMD3(0, 0, 0), SIMD3(10, 10, 0)]
    if let curve = Wire.interpolate(
        through: points,
        startTangent: SIMD3(1, 0, 0),
        endTangent: SIMD3(0, 1, 0)
    ) {
        let len = curve.length
    }
    

CAM Operations

JoinType

Join-style enum for wire offset corner treatment.

public enum JoinType: Int32 {
    case arc = 0
    case intersection = 1
}
  • arc — corners are rounded with arcs.
  • intersection — edges are extended to their intersection (sharp corners).

offset(by:joinType:)

Offsets the wire by a distance to produce a parallel curve in the plane.

public func offset(by distance: Double, joinType: JoinType = .arc) -> Wire?

Positive distance expands outward; negative contracts inward. The wire must be planar. Internally creates a BRepBuilderAPI_MakeFace from the wire, then calls BRepOffsetAPI_MakeOffset. Only the outermost contour of the result is returned; inner contours (holes) are discarded.

  • Parameters: distance — offset magnitude (positive = outward, negative = inward); joinType — corner handling style.
  • Returns: Offset wire, or nil if the wire is non-planar, the offset degenerates, or BRepOffsetAPI_MakeOffset fails.
  • OCCT: BRepOffsetAPI_MakeOffset with GeomAbs_Arc or GeomAbs_Intersection.
  • Example:
    let contour = Wire.rectangle(width: 40, height: 40)!
    let toolRadius = 3.0
    if let toolPath = contour.offset(by: toolRadius) {
        // toolPath is where the tool centre travels around the outside
    }
    if let pocketPath = contour.offset(by: -toolRadius) {
        // pocketPath keeps the tool inside the pocket boundary
    }
    
  • Note: Non-planar wires will return nil because BRepBuilderAPI_MakeFace requires planarity.

Convenience Extensions

Wire.railProfile(headWidth:headHeight:webThickness:baseWidth:baseHeight:totalHeight:)

Creates a simplified rail cross-section profile.

public static func railProfile(
    headWidth: Double,
    headHeight: Double,
    webThickness: Double,
    baseWidth: Double,
    baseHeight: Double,
    totalHeight: Double
) -> Wire?

Builds a closed 12-vertex polygon representing a symmetric I-rail cross section (base foot, vertical web, head). Delegates to polygon(_:closed:). For precise profiles matching published standards, use polygon(_:closed:) with exact dimensions.

  • Parameters: headWidth — width of the head (running surface); headHeight — height of the head; webThickness — web vertical thickness; baseWidth — base foot width; baseHeight — base foot height; totalHeight — total height from base bottom to head top.
  • Returns: Closed wire, or nil if polygon construction fails.
  • OCCT: Delegates to BRepBuilderAPI_MakeEdge + BRepBuilderAPI_MakeWire via polygon(_:closed:).
  • Example:
    if let rail = Wire.railProfile(
        headWidth: 14, headHeight: 10,
        webThickness: 5, baseWidth: 25,
        baseHeight: 6, totalHeight: 50
    ) {
        let extruded = Shape.extrude(profile: rail, direction: SIMD3(1, 0, 0), length: 1000)
    }
    

2D Fillet

filleted2D(vertexIndex:radius:)

Applies a 2D fillet (circular arc) to a specific vertex of a planar wire.

public func filleted2D(vertexIndex: Int, radius: Double) -> Wire?

Creates a face from the wire, applies ChFi2d_Builder::AddFillet at the indexed vertex, and returns the outer wire of the resulting face. Vertex indices are 0-based in traversal order.

  • Parameters: vertexIndex — 0-based vertex index; radius — fillet radius (must be > 0).
  • Returns: Wire with the corner replaced by an arc, or nil on failure.
  • OCCT: ChFi2d_Builder::AddFillet + BRepTools::OuterWire.
  • Example:
    if let rect = Wire.rectangle(width: 10, height: 5) {
        let rounded = rect.filleted2D(vertexIndex: 0, radius: 1.0)
    }
    
  • Note: The wire must be planar. Returns nil if radius is too large to fit, or if the vertex index is out of range.

filletedAll2D(radius:)

Applies 2D fillets to all vertices of a planar wire.

public func filletedAll2D(radius: Double) -> Wire?

Attempts to fillet every vertex with the given radius. If some vertices cannot be filleted (radius too large for adjacent edge lengths), ChFi2d_Builder may return the partially-filleted or original wire rather than nil.

  • Parameters: radius — fillet radius for all corners.
  • Returns: Wire with rounded corners, or nil on failure.
  • OCCT: ChFi2d_Builder::AddFillet applied to each vertex in the TopTools_IndexedMapOfShape.
  • Example:
    if let rect = Wire.rectangle(width: 10, height: 5) {
        let rounded = rect.filletedAll2D(radius: 1.0)
    }
    

2D Chamfer

chamfered2D(vertexIndex:distance1:distance2:)

Applies a 2D chamfer (straight cut) to a specific vertex of a planar wire.

public func chamfered2D(vertexIndex: Int, distance1: Double, distance2: Double) -> Wire?

Creates a straight line that cuts across the corner, set back distance1 along one adjacent edge and distance2 along the other. Uses ChFi2d_Builder::AddChamfer.

  • Parameters: vertexIndex — 0-based vertex index; distance1 — setback along the first edge; distance2 — setback along the second edge.
  • Returns: Wire with the corner replaced by a chamfer edge, or nil on failure.
  • OCCT: ChFi2d_Builder::AddChamfer + BRepTools::OuterWire.
  • Example:
    if let rect = Wire.rectangle(width: 10, height: 5) {
        let chamfered = rect.chamfered2D(vertexIndex: 0, distance1: 1.0, distance2: 1.0)
    }
    
  • Note: Asymmetric chamfers (distance1 ≠ distance2) are supported. Returns nil if either distance exceeds adjacent edge length.

chamferedAll2D(distance:)

Applies symmetric 2D chamfers to all vertices of a planar wire.

public func chamferedAll2D(distance: Double) -> Wire?

Applies equal distance1 == distance2 == distance chamfers to each adjacent-edge pair. If some corners cannot be chamfered, the builder may return a partially-chamfered result.

  • Parameters: distance — chamfer setback on both edges at each corner.
  • Returns: Wire with chamfered corners, or nil on failure.
  • OCCT: ChFi2d_Builder::AddChamfer applied to each adjacent edge pair.
  • Example:
    if let rect = Wire.rectangle(width: 10, height: 5) {
        let chamfered = rect.chamferedAll2D(distance: 1.0)
    }
    

Helix Curves

Wire.helix(origin:axis:radius:pitch:turns:clockwise:)

Creates a constant-radius helical wire.

public static func helix(
    origin: SIMD3<Double> = .zero,
    axis: SIMD3<Double> = SIMD3(0, 0, 1),
    radius: Double,
    pitch: Double,
    turns: Double,
    clockwise: Bool = false
) -> Wire?

All of radius, pitch, and turns must be > 0. The default winding is counter-clockwise when viewed from the positive axis direction.

  • Parameters: origin — axis base point; axis — helix axis direction; radius — helix radius; pitch — axial distance per turn; turns — number of full turns; clockwise — winding direction.
  • Returns: Helical wire, or nil if any of radius/pitch/turns ≤ 0 or construction fails.
  • OCCT: HelixBRep_BuilderHelix::SetParameters + Perform.
  • Example:
    if let spring = Wire.helix(radius: 5, pitch: 2, turns: 10) {
        let coil = Shape.pipeShell(spine: spring, profile: Wire.circle(radius: 0.5)!)
    }
    

Wire.helixTapered(origin:axis:startRadius:endRadius:pitch:turns:clockwise:)

Creates a tapered (conical) helical wire where the radius varies linearly.

public static func helixTapered(
    origin: SIMD3<Double> = .zero,
    axis: SIMD3<Double> = SIMD3(0, 0, 1),
    startRadius: Double,
    endRadius: Double,
    pitch: Double,
    turns: Double,
    clockwise: Bool = false
) -> Wire?

All of startRadius, endRadius, pitch, and turns must be > 0.

  • Parameters: origin — axis base point; axis — helix axis direction; startRadius — radius at the start; endRadius — radius at the end; pitch — axial distance per turn; turns — number of turns; clockwise — winding direction.
  • Returns: Tapered helical wire, or nil if any required value ≤ 0 or construction fails.
  • OCCT: HelixBRep_BuilderHelix::SetParameters (overload with start/end diameter) + Perform.
  • Example:
    if let cone = Wire.helixTapered(startRadius: 10, endRadius: 2, pitch: 3, turns: 5) {
        let len = cone.length
    }
    

Wire Explorer

Members in this section provide ordered traversal of a wire’s edges via OCCT’s BRepTools_WireExplorer, which visits edges in connected sequence.


orderedEdgeCount

The number of edges in this wire in ordered traversal sequence.

public var orderedEdgeCount: Int { get }
  • OCCT: BRepTools_WireExplorer — counts edges by iterating the explorer.
  • Example:
    let rect = Wire.rectangle(width: 10, height: 5)!
    #expect(rect.orderedEdgeCount == 4)
    

orderedEdgePointCount(at:)

Returns the number of discretised points for an edge at a given ordered index.

public func orderedEdgePointCount(at index: Int) -> Int
  • Parameters: index — 0-based edge index in traversal order.
  • Returns: Point count, or 0 if the index is out of range.
  • OCCT: BRepTools_WireExplorer + GCPnts_TangentialDeflection with 0.01 rad angular and 0.1 chord deflection.
  • Example:
    let count = wire.orderedEdgePointCount(at: 0)
    

orderedEdgePoints(at:maxPoints:)

Returns the discretised 3D points of an edge by its ordered traversal index.

public func orderedEdgePoints(at index: Int, maxPoints: Int? = nil) -> [SIMD3<Double>]?

When maxPoints is nil, allocates a buffer sized to all discretised points (no truncation). When provided, limits the returned array to maxPoints elements.

  • Parameters: index — 0-based edge index; maxPoints — optional output capacity, honoured within 1...Sampling.maximumSampleCount (10,000,000); outside that range the result is nil (#558).
  • Returns: Array of 3D points along the edge, or nil if the index is out of range or the edge is degenerate.
  • OCCT: BRepTools_WireExplorer + BRepAdaptor_Curve + GCPnts_TangentialDeflection.
  • Example:
    let rect = Wire.rectangle(width: 10, height: 5)!
    for i in 0..<rect.orderedEdgeCount {
        if let pts = rect.orderedEdgePoints(at: i) {
            // pts are the discretised points of edge i
        }
    }
    #expect(rect.orderedEdgePoints(at: 99) == nil)
    

Wire Edge Access

edges()

Returns all edges of this wire as typed Edge objects.

public func edges() -> [Edge]

Converts the wire to a Shape via Shape.fromWire(_:), then calls shape.edges(). Returns an empty array if conversion fails.

  • Returns: Array of Edge objects, or [] if the wire cannot be converted.
  • OCCT: Pure-Swift delegation to Shape.edges().
  • Example:
    let edges = Wire.rectangle(width: 10, height: 5)!.edges()
    #expect(edges.count == 4)
    

allEdgePolylines(deflection:maxPointsPerEdge:)

Returns all edges as discretised polylines.

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

Convenience wrapper: converts to Shape, then calls shape.allEdgePolylines(deflection:maxPointsPerEdge:).

  • Parameters: deflection — maximum chord deviation; 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), or [] on failure.
  • OCCT: Delegates to Shape.allEdgePolylines.
  • Example:
    let polylines = Wire.circle(radius: 5)!.allEdgePolylines(deflection: 0.05)
    

edgePolyline(at:deflection:maxPoints:)

Returns a single edge’s polyline by index.

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

Convenience wrapper: converts to Shape, then calls shape.edgePolyline(at:deflection:maxPoints:).

  • Parameters: index — 0-based edge index; deflection — chord deviation; 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).
  • Returns: Polyline for the edge, or nil if index is out of range.
  • OCCT: Delegates to Shape.edgePolyline.
  • Example:
    if let pts = Wire.rectangle(width: 10, height: 5)!.edgePolyline(at: 0) {
        // pts are the discretised points of the first edge
    }
    

bounds

The axis-aligned bounding box of the wire.

public var bounds: (min: SIMD3<Double>, max: SIMD3<Double>) { get }

Converts to Shape via Shape.fromWire(_:), then returns shape.bounds. Returns (.zero, .zero) if conversion fails.

  • Returns: Tuple of min and max corners of the AABB.
  • OCCT: Delegates to Shape.bounds (uses BRepBndLib::Add).
  • Example:
    let bb = Wire.rectangle(width: 10, height: 5)!.bounds
    // bb.min ≈ SIMD3(-5, -2.5, 0), bb.max ≈ SIMD3(5, 2.5, 0)
    

Wire Topology Analysis

WireAnalysis

Result of analyze(tolerance:) below: a wire-topology health report backed by ShapeAnalysis_Wire.

public struct WireAnalysis: Sendable {
    public var isClosed: Bool
    public var hasSmallEdges: Bool
    public var hasGaps: Bool
    public var hasSelfIntersection: Bool
    public var isOrdered: Bool
    public var minGap: Double
    public var maxGap: Double
    public var edgeCount: Int
}
Field Meaning
isClosed Whether the wire forms a closed loop (ShapeAnalysis_Wire::CheckClosed).
hasSmallEdges Whether the wire has small/degenerate edges (CheckSmall).
hasGaps Whether there are 3D gaps between consecutive edges (CheckGaps3d).
hasSelfIntersection Whether the wire self-intersects (CheckSelfIntersection).
isOrdered Whether edges are in connected order (CheckOrder).
minGap Minimum 3D gap distance between consecutive edges (MinDistance3d).
maxGap Maximum 3D gap distance between consecutive edges (MaxDistance3d).
edgeCount Number of edges in the wire.

(Per-field anchors below, for cross-reference; the table above has the actual meaning of each.)

WireAnalysis.minGap


analyze(tolerance:)

Analyses wire topology for potential issues: closure, gaps, self-intersections, and edge ordering.

public func analyze(tolerance: Double = 1e-6) -> WireAnalysis?

Wraps ShapeAnalysis_Wire to detect: whether the wire is closed, whether small or degenerate edges exist, 3D gaps between consecutive edges, self-intersections, and whether edges are in connected order.

  • Parameters: tolerance — analysis precision (default 1e-6).
  • Returns: WireAnalysis struct, or nil if analysis fails.
  • OCCT: ShapeAnalysis_WireCheckClosed, CheckSmall, CheckGaps3d, CheckSelfIntersection, CheckOrder, MinDistance3d, MaxDistance3d.
  • Example:
    let rect = Wire.rectangle(width: 10, height: 5)!
    if let a = rect.analyze() {
        #expect(a.edgeCount == 4)
        #expect(a.isClosed)
        #expect(!a.hasSelfIntersection)
    }