Releases: SecondMouseAU/OCCTSwift
v1.8.2 — smooth multi-start threadedShaft direct build (#257)
Feature. Multi-start threads (threadedShaft(starts: N), N > 1) now build via the smooth, boolean-free direct path instead of falling to the faceted boolean cut (which produced disconnected notches, #254).
The single-start cam-slice loft (#213) is generalised to N teeth tiling the turn at lead = N·pitch, giving a continuous interleaved multi-helix — a low-face-count, BRepCheck-valid solid with the crest exactly at the nominal major radius. Partial-length multi-start (thread + plain shank) closes via per-start shoulder faces.
| starts | faces | mesh crest (nominal 5.0) |
|---|---|---|
| 1 | 7 | 5.000 |
| 2 | 12 | 5.000 |
| 3 | 17 | 5.000 |
Scope: piecewise-linear forms (ISO/Unified, trapezoidal/ACME, square, buttress). Rounded (knuckle / rounded Whitworth), tapered (NPT/BSPT), and non-cylinder targets still use the cut path. Single-start behaviour unchanged.
Key detail: the loft samples per pitch, not per lead — sampling per turn under-samples each tooth at N > 1 and the ruled:false loft balloons the crest radially past nominal.
Swift-only — no xcframework rebuild. Full thread suite (54 tests) green. Closes #257.
v1.8.1 — fix: single-start threadedShaft always smooth; deprecate .boolean (#254)
Fix. threadedShaft(build: .boolean) produced a faceted, disconnected thread — a helical scatter of rectangular notches instead of a continuous groove — because it forced the screw-loft boolean cut path, whose tightly-wound helical cutter is the classic OCCT BOP failure (cf. #213/#225). The solid was isValid with ~the right volume, so only rendering exposed it.
.boolean only existed to clamp a supposed crest "overshoot" (#222) — but #232 established that overshoot is a Bnd_Box control-hull artifact. Verified here: the direct build's crest measures exactly nominal by both boundingBoxOptimal() and mesh vertices, while .bounds over-reads +14–21%.
With no remaining reason to prefer it, single-start coaxial-cylinder threads now take the smooth, boolean-free direct build (#213) for every build mode, and ThreadBuild.boolean is deprecated (treated as .auto). Use .auto or .direct.
.auto/.direct single-start behaviour is unchanged. Swift-only — no xcframework rebuild.
Known limitation: multi-start (starts > 1) and non-cylinder targets still use the faceted cut path (no direct alternative yet) — tracked separately.
Full thread suite (49 tests) green. Closes #254.
Note: the separately-reported helical-flank "ridges" (#255) are a renderer normals artifact, not a geometry bug — see OCCTSwiftViewport#81.
v1.8.0 — Exporter.writeBREP(allowInvalid:)
Feature (additive). Exporter.writeBREP (and the Shape.writeBREP instance wrapper) gain allowInvalid: Bool = false. When true, the shape.isValid pre-check is skipped and the shape is serialized as-is — BREP is OCCT's lossless native format and BRepTools::Write does not require a valid shape, and loadBREP already doesn't gate on read. Lets an in-progress reconstruction (a compound of loose analytic faces) be persisted and reloaded for measurement / diagnostics.
Default false preserves the existing gate; other exporters unchanged. Enables OCCTMCP #41. No xcframework change.
PR #248.
v1.7.11 — fromPointGrid degree clamp prevents a BRepMesh hang (#244)
Bug fix. No new operations; no xcframework change (same v1.7.1 binary).
Surface.fromPointGrid now clamps the B-spline fit degree to min(uCount, vCount) − 1. Passing a degMax higher than the grid supports (the default degMax: 8 on a 7×7 grid) over-parameterised the fit: a degree-8 surface from only 7 samples/direction oscillates (Runge phenomenon) and can self-overlap in 3D. The resulting face is topologically valid (BRepCheck passes) but geometrically rippling, so BRepMesh's adaptive refinement never converged — an in-process, uninterruptible hang (the OCCTReconstruct blocker). The clamp keeps the fit well-posed; the 7×7 case now meshes in ~40 ms.
Why not an interruptible mesh? A watchdog-based bounded mesh was prototyped and rejected — BRepMesh doesn't poll UserBreak during heavy meshing (verified: a fine sphere ran ~13 min / 5 GB ignoring a 0.01s deadline), so an in-process time bound can't be made both reliable and safe. Prevention (well-posed surfaces) is the robust fix.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.10 — degenerate-hole crash fix (#234)
Bug fix + docs. No new operations; no xcframework change (same v1.7.1 binary).
- #234 —
faceAddHolerejects degenerate hole wires. A 2-vertex / zero-area / collinear hole wire was accepted, producing an invalid face whose extruded prism SIGSEGV'd OCCT'sShapeFix(Shape.healed()) — an uncatchable OS signal.faceAddHolenow returnsnilfor a hole wire with < 3 distinct vertices or all-collinear points, breaking the crash chain at the source. - #178 — loft polar-iterator fix is upstream. The
BRepFill_CompatibleWiresguard (#176) shipped in OCCT 8.0.0p1; the carried local patch was dropped. Stale CLAUDE.md / test references corrected (the #176 regression passes against the unpatched p1 xcframework). - #210 — context7. Runnable-snippet doc comments on the core ops + a CLAUDE.md doc-standards rule. The Swift API is now indexed and queryable on context7 (
/gsdali/occtswift).
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.9 — face from surface bounded by a wire / UV polygon
Additive, source-compatible. Trim a curved analytic surface (cylinder / cone / sphere / B-spline) to a non-rectangular region — closes #233.
Surface.toFace(uvBoundary: [SIMD2<Double>])— a closed UV-space boundary polygon → 2D edges with pcurves on the surface →BRepBuilderAPI_MakeFace(surface, wire)+BuildCurves3d. (Ground-truthed: a cylinder region trims to 39.75 vs 60 for the full UV box.)Shape.face(from: Surface, boundary: Wire)— a 3D boundary wire: exactMakeFace+ShapeFix_Facewhen the wire lies on the surface, else a fallback that projects the wire's ordered points to UV and trims by that polygon (handles sampled boundary polylines — the reconstruction case; seam-crossing boundaries not handled by the fallback).
Surfaces 86→88, total 4,290 operations. No xcframework change (same v1.7.1 binary).
Also lands the #232 investigation (doc + tests, no behavior change): Shape.bounds over-reports for B-spline/faceted geometry — threaded solids are bounded exactly to length/depth; Issue232BoundsTests asserts the true (mesh-vertex) extent.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.8 — cookbook: surfaces from points + working with meshes
Documentation-only release. No code, API, or xcframework change — SPM consumers get the same v1.7.1 binary; only the tag and docs advance.
Two new cookbook pages (snippets compile-checked against the shipped API):
- Surfaces from Points (#230) — fit a B-spline
Surfacethrough 3D points: a regular grid viaSurface.fromPointGrid(GeomAPI_PointsToBSplineSurface), a scattered cloud viaSurface.plateThrough(GeomPlate), and deform-to-targets vianlPlateDeformed(NLPlate). - Working with Meshes (#231) — operating on the
Meshtype: build from vertex/index arrays, inspect, triangle ↔ B-Rep face picking, mesh-level booleans,toShape, and SceneKit / RealityKit / Metal interop.
The cookbook now spans twelve pages: Booleans · Threads · Helices · Lofting & Sweeps · Helical Sweeps · Healing & Validity · Meshing & Export · XCAF Assemblies · Topology Graph · Gordon Surfaces · Surfaces from Points · Working with Meshes.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.7 — cookbook: Gordon surfaces
Documentation-only release. No code, API, or xcframework change — SPM consumers get the same v1.7.1 binary; only the tag and docs advance.
New cookbook page on Gordon surfaces (#229) — skinning a surface through a network of crossing profile + guide curves via Surface.gordon / Surface.gordonReport (GeomFill_Gordon):
- The grid-closure requirement (≥2 profiles × ≥2 guides, shared corners) and
surface.toFace(). - Build diagnostics —
gordonReportreturns status +isApproximateinstead of barenil;allowApproximateFallbackfor a sampled fallback. - The lower-level
networkSurface(GeomFill_NetworkSurface) and its knot-alignment caveat. - A Gordon-vs-loft-vs-fill decision table.
Snippets compile-checked; figure rendered from the same network the page shows.
The cookbook now spans ten pages: Booleans · Threads · Helices · Lofting & Sweeps · Helical Sweeps · Healing & Validity · Meshing & Export · XCAF Assemblies · Topology Graph · Gordon Surfaces.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.6 — cookbook complete: healing, meshing, XCAF, topology
Documentation-only release. No code, API, or xcframework change — SPM consumers get the same v1.7.1 binary; only the tag and docs advance.
Completes the issue #210 cookbook area list — the Swift-API counterpart to OCCT's own user guides — with the final four areas (#228). Every snippet was compile- and run-checked against the shipped API.
- Healing & Validity — checks (
isValid/isValidSolid/isSelfIntersecting/analyze), orientation (signedVolume/orientedForward), and repair (healed/fixed/unified/upgraded/ sew / free-bounds). - Meshing & Export —
mesh+MeshParameters, theMeshtype,toShape, a deflection table, and STL / OBJ / PLY / STEP / IGES / BREP / glTF export + import. - XCAF Assemblies —
Documenttrees, components & instancing, colors & materials, structured STEP / GLB round-trip (with a rendered two-colour assembly figure). - Topology Graph — node counts, adjacency / shared edges /
sameDomainFaces, durable UIDs, and history tracking.
The cookbook now covers Booleans, Threads, Helices, Lofting & Sweeps, Helical Sweeps, Healing & Validity, Meshing & Export, XCAF Assemblies, and Topology Graph.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/
v1.7.5 — threadedRod from a custom profile (#225)
Additive, source-compatible. Builds a smooth worm/screw from a custom radial tooth profile directly — no boolean — closing the actionable part of #225.
Why
helicalSweep a profile then union/subtract it with a coaxial cylinder is invalid (union) or collapses to zero (subtract), and no fuzzyValue / heal pass recovers it — OCCT's BOP can't resolve the coincident/tangent helicoid faces (consistent with #213, #181). The boolean compose path was never the way; the direct build is.
New API
Shape.threadedRod(customProfile:nominalDiameter:pitch:cutDepth:length:axisOrigin:axisDirection:leftHanded:)— composes a custom tooth profile with the core by sewing (no boolean) → BRepCheck-valid, analytic (handful of B-spline faces → sub-MB STEP). Returnsnilrather than silently producing an invalid boolean.ThreadProfile.supportsSmoothRodBuild— public predicate (real crest flat, ≤ 2 flanks).Shape.helicalSweep(…)doc now warns against the boolean-compose anti-pattern and points tothreadedRod.
The issue's worm (root r3, crest r6) via threadedRod: valid, 9 faces, ~0.74 MB STEP (vs the ~6.8 MB faceted fallback).
Docs
New Helical Sweeps cookbook page (helicalSweep helicoids vs. threadedRod worms, and why the boolean compose fails), with rendered figures.
No xcframework change — same binary as v1.7.1. Thread Features 22→23, total 4,288 operations.
📖 Docs & cookbook: https://gsdali.github.io/OCCTSwift/