Helical Sweeps
Sweeping a profile along a helix is how you build worms, augers, screw conveyors, and screw threads. OCCTSwift gives you two routes, and picking the right one matters:
Shape.helicalSweep(profile:…)— sweeps a profile along an exact analytic helix into a standalone helicoid (a helical ridge / auger flight). Great on its own; not something to boolean onto a shaft.Shape.threadedRod(customProfile:…)— composes a custom tooth profile with a core cylinder directly, with no boolean, into a smooth, valid, analytic threaded rod (worm / screw).
If you only need a standard fastener thread (ISO, Unified, ACME, trapezoidal, square, buttress…), reach for threadedShaft / threadedHole with a ThreadForm spec instead — this page is for custom helical profiles.
A standalone helicoid
helicalSweep runs the profile along the helix using an auxiliary-spine framing, so the section stays roughly radial. A triangular rib over a few turns gives an auger-style flight:
let R = 3.0, crest = 6.0, pitch = 4.0
// rib profile in the (radial, axial) plane: inner edge at radius R, peak at `crest`.
guard let rib = Wire.polygon3D([SIMD3(R, 0, 0),
SIMD3(crest, 0, pitch * 0.4),
SIMD3(R, 0, pitch * 0.8)], closed: true),
let ridge = Shape.helicalSweep(profile: rib,
axisOrigin: .zero, axisDirection: SIMD3(0, 0, 1),
radius: R, pitch: pitch, turns: 3) else { return }
// ridge.isValidSolid == true — a valid helicoid on its own
helicalSweep — a standalone helical ridge |
Framing caveat. The auxiliary-spine framing isn’t exactly radial: the result bulges ~10–15% beyond the nominal radius for moderate profiles, and for narrow / fine-pitch profiles (e.g. ISO V forms) it balloons badly. Use
helicalSweepfor coarse worm/auger ribs, not precise fastener threads — and build threads withthreadedRod/threadedShaft, which don’t use this path.
A worm / screw thread from a custom profile
To turn a custom tooth into an actual threaded rod, use Shape.threadedRod(customProfile:…). You give it a ThreadProfile — the tooth cross-section in normalized (axial, depth) coordinates: axial runs 0…1 over one pitch, and depth runs 0 (crest, at nominalDiameter / 2) to 1 (root, at nominalDiameter / 2 − cutDepth). Here a symmetric trapezoidal worm tooth:
guard let tooth = ThreadProfile(vertices: [
.init(axial: 0.000, depth: 1), .init(axial: 0.125, depth: 1), // root half-flat
.init(axial: 0.375, depth: 0), .init(axial: 0.625, depth: 0), // flanks up to the crest flat
.init(axial: 0.875, depth: 1), .init(axial: 1.000, depth: 1), // flank back down to the root
]),
let worm = Shape.threadedRod(customProfile: tooth, nominalDiameter: 12,
pitch: 5, cutDepth: 1.8, length: 22) else { return }
// worm.isValidSolid == true — smooth, analytic (a handful of B-spline faces → a small STEP),
// and built with NO boolean, so it's BRepCheck-valid where a boolean compose is not.
threadedRod — a smooth worm from a custom profile |
🖱️ Drag to orbit · scroll to zoom · auto-rotating. (Models exported straight from the snippets above via Exporter.writeGLTF.)
The profile must be smooth-rod-buildable — a real crest flat and at most two flanks (trapezoidal / ACME / square / buttress / worm forms qualify). Check with tooth.supportsSmoothRodBuild. A pointed-crest or many-flank (rounded / knuckle) profile returns false, and threadedRod returns nil rather than silently producing an invalid result.
Don’t boolean a helicoid onto a cylinder
The intuitive way to make a thread — helicalSweep a rib, then union (or subtract) it with a coaxial cylinder whose surface is coincident with the helicoid’s inner edge — does not work:
// ❌ DON'T: OCCT's boolean engine can't resolve the coincident/tangent helicoid faces.
let core = Shape.cylinder(radius: 3, height: 13)!
let bad = core.union(ridge) // BRepCheck-INVALID (volume ~right, topology not)
// subtracting a continuous helicoid collapses to volume 0. fuzzyValue / healed() / sewn()
// don't recover either — this is inherent, not a tuning problem (OCCTSwift #225, #213, #181).
threadedRod exists precisely to avoid this: it lofts the thread region (ruled:false cam slices of the profile, swept along the exact helix) and sews it to the core — the boolean engine is never invoked, so the result is valid and analytic.
See also
- Threads — standard fastener forms (
threadedShaft/threadedHole,ThreadSpec). - Helices & Springs —
Wire.helix, sweeping a circle into a coil. - Lofting & Sweeps — the general sweep/loft primitives.
- API mapping:
../../API_REFERENCE.md