StepKit for Apple platforms
29 releases · latest 0.19.0 · 2026-08-04

Every release, measured.

StepKit is built against a 451-file corpus spanning 25+ CAD writers, with a regression gate on every change — so a release note here can say what moved and by how much, not just what was attempted. This is the whole history, from the first tagged parser to today.

0.19.0
Latest

The core leaves Apple: Linux builds

Parsing, the B-rep model, tessellation, mesh repair and the STEP writer now build and test off-Apple, so they can run on a server or in a Linux CI leg. Rendering stays Metal, and therefore Apple-only.

Changed
  • StepParser and StepKernel no longer require Apple's simd framework. The vector types were always Swift standard library; only the framework's free functions, the matrix types and the quaternion were Apple-only, and a compatibility layer supplies exactly those where the framework is absent — with identical column-major semantics. No API or behaviour change: on Apple the shim compiles to nothing and the system framework is used as before.
  • Verified by building in clean Swift 6.1 and 6.2 Linux containers, and by a differential check of the compatibility layer's matrix maths against Apple's simd over 20 000 random matrices — worst relative error 2.4e-12, quaternion→matrix 7.9e-16. New tests assert the layout, multiplication and inverse contract against known values on both platforms, so a divergence fails one of them.
  • The package manifest omits the Apple-only rendering targets off-Apple; the portable core is unchanged everywhere.
Fixed
  • The async API no longer blocks Swift Concurrency's cooperative pool. The async load and tessellate overloads ran the pipeline in a detached task — on the cooperative pool — while the pipeline blocks its thread for parallel work. That starves the pool; on Linux the next parallel tessellation after an awaited one deadlocked. The work now runs on a dispatch thread and bridges back through a continuation, with a regression test that reproduces the exact ordering.
  • Cancellation actually reaches the pipeline now. The per-face and per-solid checkpoints tested the task's cancellation flag, which reads false on parallel worker threads because they carry no task context — so a cancelled load or tessellation could never stop early on any platform. An explicit cancellation token replaces it.
Added
  • A Linux CI job (Swift 6.1 container) building the portable core and running the test suites on every change, so portability cannot silently regress.
0.18.0

Classified analytic edges for viewer overlays

Added
  • Real CAD edges as public data. The tessellator's shared per-edge discretizations — until now a private cache — are surfaced as polylines that are bit-identical with the mesh's face-boundary vertices, so an overlay line sits exactly on the shaded surface instead of z-fighting near it. Each carries its provenance back to the B-rep edge and its adjacent faces.
  • Every edge is classified: boundary (an open shell's rim), sharp (a crease), tangent (a smooth blend or fillet boundary, below a configurable angle) or seam (a periodic surface's seam, with the same face on both sides). Classification measures the dihedral angle between the adjacent faces' outward normals along the edge — closed-form for plane, cylinder, cone, sphere and torus, STEP pcurve evaluation for freeform surfaces, oriented facet normals as a last resort. A viewer can draw crisp outlines and hide fillet blends.
  • Assemblies transform edge polylines per occurrence alongside their faces. Off by default — existing behaviour, memory use and the golden corpus are unchanged.
0.17.0

Assembly transforms, disambiguated the way OCCT does

Fixed
  • Occurrence transforms now apply the item-defined transformation in the CAx-IF-recommended direction. The loader previously assumed one ordering unconditionally — correct only for exporters that follow it. It now swaps the two axes when each is an item of the other representation, and anchors the direction on whichever representation belongs to the child product. Matches OpenCASCADE 7.9.3 on every ordering permutation, verified through the differential harness at 100 % agreement.
  • The two-level assembly test fixture was regenerated from a real OpenCASCADE export; the old hand-made one was degenerate — it named the same representation on both sides, so the transform direction was genuinely ambiguous. The degenerate file survives as its own OCCT-parity fixture, and three further ordering variants now guard the code path.
Changed
  • The writer makes each occurrence's target axis an item of the parent's shape representation, so readers that use item-to-representation membership to orient the transform — OpenCASCADE and StepKit's own loader included — resolve written assemblies without guessing.
0.16.0

Official iPad and iPhone support

Added
  • The package declares iOS 17 and the consumable libraries are verified to compile for arm64 iOS: no macOS-only API in any library target, cross-platform Metal rendering with runtime-compiled shaders, and ModelIO guarded behind a capability check. A CI job compiles the library stack for a generic iOS device on every push and pull request — so iPad compatibility is enforced, not discovered later. The command-line tools and the macOS window viewer stay macOS-only.
0.15.1

An honesty flag for last-resort repair

Added
  • The volumetric repair always produces a watertight solid — but for open surfaces or thin-walled input the shape can move drastically (one file −96.8 %, another +124.7 %). A reliability flag and a typed diagnostic now say which happened: reliable means a faithfully recovered solid within 30 % volume; unreliable means closed but not the real shape — raise the resolution or don't use it. Input that encloses no volume at all is returned unchanged with the diagnostic, instead of a vacuously watertight empty mesh.
0.15.0

Last-resort volumetric repair

Added
  • When a mesh cannot be closed by construction — input defects, sculpted cracks, non-manifold soup — an opt-in pass forces it into a guaranteed watertight, closed 2-manifold solid, accepting a change of shape. Clean-room and dependency-free: a generalized winding number (robust inside/outside even for broken input) over a narrow band with flood-fill sign propagation, then marching tetrahedra with globally edge-keyed vertices (watertight by construction), then Taubin smoothing to de-staircase without shrinking.
  • Never automatic, and it reports how far the volume moved. A leaky kayak hull with 213 boundary and 33 non-manifold edges comes back watertight at −1 % volume; a clean sphere round-trips at roughly 0 %.
0.14.0

Writer v2, async API, and a support declaration

Added
  • Writer v2 — face colours and assembly structure. Colours are written as one presentation-style chain per distinct colour, with signatures verified against the AP214 EXPRESS schema; assemblies get a product chain per distinct product and a usage-occurrence edge per instance. Occurrence transforms round-trip to 1e-9, and OpenCASCADE reads the written assembly bit-identical to the original. Pcurves and PMI remain unwritten.
  • Async, cancellable load and tessellate, with cooperative checkpoints per solid and per face.
  • A formal STEP support declaration — the reference document for product UIs: format coverage, writer guarantees, measured quality metrics, the diagnostics honesty model and the known defect classes.
  • A diagnostic for boundaries lying far off their referenced surface — a writer bug in some helical-strip exports — so the file is called inconsistent instead of showing unexplained holes.
0.13.0

Conics, trimmed curves, a fuzzer and CI

Added
  • Trimmed-curve edge geometry. Edges referencing a trimmed curve had collapsed to straight chords; the basis curve now resolves as the geometry and a reversed sense flips the edge so circular and elliptical bases sample the correct arc. 24 corpus files carry the entity — one drops from 1211 to 702 boundary edges.
  • Hyperbola and parabola edges, which previously collapsed to chords: both load, evaluate to the ISO parameterization, and sample adaptively through their exact parameter inversions. One turbine part gains roughly 2 400 triangles of true conic geometry.
  • The corpus grew to 451 files (Onshape AP242 and Fusion 360 exports), a CI workflow, a DocC catalog, and a corpus-mutation fuzzer: deterministic mutations — bit flips, truncation, chunk splices, hostile numerals — each run through the full parse → load → tessellate pipeline in an isolated subprocess with a hang timeout. STEP files are untrusted input in a viewer, so graceful rejection is the pass condition.
Fixed
  • Semantic PMI honours the measure's own unit. A dimensional callout can carry a different unit than the geometry around it — the NIST files put geometry in millimetres and callouts in inches — so those values read 25.4× too small (a 0.438-inch hole reported as 0.438 mm).
  • A multistart fallback for singular-chart inversion: a collapsed control row, such as a lofted blade tip, makes the Jacobian vanish and both usual seeds converged to the wrong domain corner, leaving 21 mm residuals. Points whose residual stays above tolerance now retry from a coarse deterministic start lattice.
0.12.0

The STEP writer

Added
  • StepKit writes STEP, not just reads it. A loaded model's B-rep serializes back to a Part 21 file: every surface type including rational B-splines, every curve type, full topology from vertex to solid — voids and loose shells included — and a unit context. Validated three ways: a load → write → re-load → tessellate → compare round-trip passing 392 of 392 loadable corpus files with face count, watertightness and volume preserved; unit-test fixtures; and OpenCASCADE / FreeCAD importing the output as valid solids, matching a FreeCAD ground-truth volume exactly. Colours, assemblies, pcurves and PMI are not yet written.
  • Apex and pole chart splitting. A boundary point sitting on the revolution axis — a cone apex, a sphere pole — has no defined angle, and the arbitrary value zigzagged the loop across the parameter chart, corrupting everything after it. The apex now snaps to the incoming branch and splits into one copy per adjacent edge. Five more watertight files, plus two volume fixes confirmed against OpenCASCADE where StepKit had been 17.8 % over and 16.4 % under.
0.11.0

Seam splitting and T-junction healing

Added
  • A periodic seam split for tube faces. A face whose two boundary loops each wind once around the periodic direction is an annulus of the period cylinder and simply cannot embed in a flat parameter chart — and some writers omit the seam edges most exporters insert. Such faces are now cut at an existing vertex pair and rebuilt as one simple polygon whose two seam sides share bit-identical 3D samples, so the seam welds shut and neighbouring faces are untouched. Eleven more watertight files and large crack reductions corpus-wide: one car wheel loses 1509 boundary edges, a French press drops from 401 to 13.
  • T-junction healing. When a vertex of one edge lies on the interior of another — common in real input — the triangulator now reports the split, the tessellator lifts it to 3D, verifies it sits on the chord, and inserts it into the shared edge discretization so both sides see the same chain. 332 insertions across 15 corpus files.
Fixed
  • Constraint recovery conforms to on-segment vertices after flip exhaustion instead of silently giving up — the previous behaviour left unrecovered constraints, which means cracks.
0.10.3

Arc sense — a project-lifetime bug

Fixed
  • Arc sampling honours the edge's sense flag. A reversed edge means the complementary arc joins the endpoints, but every circle and ellipse was sampled counter-clockwise regardless — 270° sweeps where the file meant 90°. Unmasked by a new corpus family whose writer uses the flag heavily. Seven more watertight files; one ventilator drops from 1362 to 52 boundary edges, another part from 2270 to 6.
  • Closed-direction Newton inversion wraps across the seam, normalized to the seed's branch. A degenerate toroidal surface loads as a torus.
Added
  • A UV inspector: a self-contained HTML parameter-space debugger with double-precision pan and zoom, layer toggles, a locked-crossing metrics table, edge provenance and inversion residuals — the tool most of the following releases were diagnosed with.
  • A diagnostic for boundaries winding the periodic direction twice or more — missing seam edges, invalid input, provably unrepairable — so they are attributed rather than left mysterious.
0.10.2

Loop-global branch selection

Added
  • When both the independent per-point inversion and the predictor-corrector trace leave a B-spline boundary self-intersecting in parameter space, up to four valid preimages per point are enumerated and a cyclic dynamic program picks one per point, minimising the closed path length — the loop-global choice a greedy trace cannot make. Every candidate reproduces its shared 3D point within tolerance by construction, and the result is adopted only when the loop comes out simple.
  • Instrumentation that answered a design question with a measurement: the locked constraint crossings turn out to be far apart in 3D, so the proposed splitting backstop would have fused genuinely distant geometry. It was declined on the evidence rather than shipped on the hunch.
0.10.1

Knot-aware B-spline edges

Changed
  • B-spline edges were sampled at a fixed multiple of their control-point count, silently ignoring the chord tolerance — over-sampling bland curves and under-sampling clustered-knot ones. They now seed at the curve's distinct knots, so continuity breaks become sample points, and bisect while the chord sag or the turn angle exceeds tolerance. Eight more watertight files, no regressions, 30–43 % fewer triangles on spline-heavy parts, and threaded-part volumes moving toward the OpenCASCADE reference.
0.10.0

The audit release

A multi-agent expert audit of the whole STEP → mesh pipeline, followed by eight implementation waves. Watertight meshes went from 274 to 299 of 403 corpus files, results became deterministic (byte-identical corpus runs), the triangulator became jitter-free on exact predicates, and the harness got twice as fast.

Changed
  • The triangulator was rewritten around walk-based point location and connected cavity carving. The previous insertion scanned every triangle (quadratic build) and carved the cavity by a global test with no connectivity check — one predicate misfire on a distant triangle silently produced overlapping triangles. Fifteen more watertight files; one bolt goes from 15 988 boundary edges to 13, a hex nut from 4168 to 0, and one part's tessellation from 93 seconds to 19.
  • Giant-boundary faces are no longer dropped: the point cap rose from 8 000 to 100 000 and over-cap planar faces route through the triangulator instead of being skipped. One connector board falls from 190 417 boundary edges to 45.
  • Closed B-spline surfaces get seam handling. Every spline face had been tessellated as non-periodic, so a surface that closes on itself — extruded gears, bottles, revolved bodies exported as NURBS — had no seam treatment at all. A timing pulley drops from 1440 boundary edges to 19.
Added
  • Typed diagnostics replace plain strings on both channels, so previously silent B-rep losses — dropped face bounds, dropped coedges, substituted vertices, stalled ear clipping — are recorded. Leaky files with no explanation at all: 28 down to 21.
  • Exact triangulation predicates, now the default, with coordinate jitter retired: an adaptive double phase escalating to double-double arithmetic, plus everything jitter used to paper over — exact duplicate merging, both-sides cavity carving, splitting at exactly-collinear vertices. Five more watertight files.
  • Implicit-knot and polyline curve types as edge geometry — the single largest loader-side leak cause, at 1266 face-level losses where edges silently became straight chords.
Fixed
  • Solids with declared internal cavities were skipped entirely and no notion of a void shell existed, so hollow parts either lost geometry or counted their cavities as material. One connector's volume moved from 2.5 % over the OpenCASCADE reference to 0.2 % under.
  • Edge endpoints are pinned to their topological vertices. Curve sampling re-evaluated endpoints, so a vertex sitting off-curve by more than the weld tolerance — STEP files routinely carry a looser vertex tolerance — gave adjacent edges two different mesh vertices at their shared junction: a guaranteed crack.
  • Tessellation is deterministic. Hole repair visited loops in dictionary order, so the fill commit order — and with it the corpus watertight total, 270 or 274 depending on the run — drifted between runs. Two consecutive full-corpus snapshots are now byte-identical.
  • Degree-based angle units were silently ignored, so cone semi-angles authored in degrees were read as radians; 71 corpus files declare degrees. Flattened assembly meshes became topologically sound: vertices weld per instance, mirrored instances flip winding, and metrics are computed on welded topology instead of reporting every sharp edge as a boundary.
0.9.0

Instancing, LOD and PMI

Four opt-in capabilities for large assemblies and manufacturing data, each additive and each shipping with zero watertight regressions and zero volume drift.

Added
  • Instanced parts: one mesh per distinct part plus its per-occurrence world transforms, instead of a flattened body. A 62-part, 408-occurrence board assembly drops to 22 % of the flat vertex memory — losslessly.
  • Decimation to a triangle budget for heavy assemblies, by linear-time vertex clustering: 1.9 million triangles down to 66 thousand. Opt-in; the full-resolution mesh is untouched.
  • Graphical PMI: AP242 tessellated annotation geometry becomes 3D polylines — dimension lines, leaders, symbol outlines — which the renderer can overlay unlit.
  • Semantic, machine-readable PMI parsed from the AP242 graph: dimensions with nominal values and asymmetric tolerance bands, geometric tolerances (flatness, perpendicularity, position, profile and more) with zone magnitude and referenced datums, and datum-feature labels. Verified against the NIST PMI test suite.
0.8.0

Mass properties and USDZ

Added
  • Volume, surface area, centre of mass and the inertia tensor of a closed solid, by the tetrahedron-covariance method, validated against an analytic sphere — enough for quoting and manufacturability checks.
  • USDZ export for AR Quick Look on Apple platforms.
Fixed
  • A curvature-adaptive interior grid for freeform faces, sized to each face's sagitta instead of a fixed grid, so a flat face gets no interior points at all. An 0402 resistor drops from 4952 triangles to 328; the corpus total falls 27 %, from 13.6 to 9.9 million — which is felt directly on device.
0.7.0

Short-form entities and metre-scale models

Added
  • Short-form STEP entity names — the abbreviated ISO type names — canonicalised at parse time, plus faceted breps and polygonal loops. Files that used to fail with “no solid found” now load.
Fixed
  • A relative-deflection floor caps absurd triangle counts on metre- and kilometre-scale models: a 732-metre part drops from 8.4 to 1.6 million triangles, while parts up to about half a metre are unchanged because the absolute tolerance still dominates.
Tooling
  • A differential harness comparing StepKit's mesh against OpenCASCADE's file by file — the instrument most of the geometry fixes in the following releases were found with.
0.6.0

Pcurve-driven NURBS tessellation, opt-in

Added
  • A NURBS face boundary's parameter values can be read from the STEP pcurve through a one-dimensional search — no two-dimensional inversion, so no branch-jump tangle — and are used only when the resulting loop comes out simple. It cuts hairline cracks on freeform parts sharply (one button −20 % boundary edges) but perturbs a few already-watertight files, so it stays off by default. Shipping it honestly beats shipping it on.
0.5.0

Assembly placement

Added
  • Per-occurrence transforms compose down the assembly tree, so a part used N times is instanced N times at the places it actually belongs — instead of all copies collapsing onto the origin.
0.4.0

The product and assembly tree

Added
  • Product definitions and usage occurrences resolve into a named structure tree, with each leaf part associated to its faces — what a structure browser and per-part show/hide are built on. Geometry unchanged.
0.3.0

Colours

Added
  • Presentation colours resolve per face — a body base colour plus face-level overrides, including the ISO predefined colour names — and render as per-vertex colour. A red push-button or a 3 mm LED comes out in its real colours instead of one grey blob; colourless files keep the default grey.
0.2.6

glTF import

Added
  • Read binary or JSON glTF into a mesh, walking the scene graph with node transforms — the first import format beyond STEP, and a round-trip of the exporter shipped one release earlier.
0.2.5

Mesh export: STL, OBJ, glTF

Added
  • Lossless export to binary STL, Wavefront OBJ and self-contained binary glTF, in millimetres and native Z-up, with a command-line tool that picks the format from the output extension. StepKit's first interop writer — until now it could only read.
0.2.4

Curved hairline cracks close

Fixed
  • Hole repair handles loops whose best-fit-plane projection self-intersects, via a centroid-fan fallback the planar ear clipper could not do. A freeform exhaust manifold closes fully; 6149 boundary edges disappear across NURBS parts. Deliberately kept moderate, so genuinely missing faces are not papered over.
0.2.3

Degenerate and duplicate triangles

Fixed
  • Zero-area and same-winding duplicate triangles are dropped at finalize — winding-sensitive, so zero-thickness sheets survive. Three more watertight files and 42 730 fewer non-manifold edges across the corpus.
0.2.2

Post-tessellation hole filling

Added
  • Small open boundary loops on closed shells are triangulated shut, closing hairline cracks regardless of the per-face cause, and guarded so the repair can never create a non-manifold edge. Four more watertight files, 8940 fewer boundary edges across 60-plus files.
0.2.1

Weld per shell, not globally

Fixed
  • Coincident or duplicated solids — a part stored as two separate breps, common in pipe and duct reductions — fused into a spuriously non-manifold mesh. Vertices now weld per shell and watertightness is measured per solid: nineteen more watertight files, from 235 to 257 of 403, with no regressions.
0.2.0

Watertightness and render quality

Added
  • Flip-based constraint recovery in the triangulator, plus curved seam-rim and NURBS-revolution stitching: 235 of 403 corpus files come out watertight, with no regressions.
  • Crease-aware normals — smooth within a surface, hard across sharp edges — so chamfers lose their dark shading triangles while cylinders and fillets stay smooth.
  • The regression gate: snapshot the corpus, check every later build against the golden baseline. Every release since is measured against it.
0.1.0

First tagged release

The pipeline end to end, written from scratch: parse a STEP file, resolve it into a B-rep, tessellate it into a shared-edge mesh, render it with Metal.

Added
  • An ISO 10303-21 tokenizer and parser building the raw entity graph, including complex records and references.
  • A resolved B-rep model and a loader mapping AP203, AP214 and AP242 files to geometry and topology, with unit resolution and shared edge and vertex memoization.
  • Analytic surfaces — plane, cylinder, cone, sphere, torus, surfaces of extrusion and of revolution — plus NURBS curves and surfaces with de Boor evaluation and Gauss–Newton point inversion.
  • An own constrained Delaunay triangulator and an ear-clipping triangulator with linear-time hole bridging. No third-party CAD or meshing library, at any point.
  • Watertight tessellation by construction: every topological edge is discretized once and shared, so adjacent faces meet on bit-identical points, and winding is made globally consistent and outward.
  • Metal rendering with runtime-compiled shaders (no offline toolchain), an interactive viewer, a headless PNG renderer, and the corpus inspection tool.

Still pre-1.0.

While StepKit is on 0.x the public API can change between minor versions; breaking changes are called out under Changed. If you are building something that needs native STEP, get in touch — I'll keep you posted on where it goes next.