* Kernel: the B-rep record and the native body file come from solidcore-record SolidCraft's portable B-rep record (`solidcraft_geom::brep`: brep.rs, topology, history, envelope, part, curve2) and the native body file (`solidcraft_kernel::native`: the tagged binary codec and the container) move to solidcore's `solidcore-record` crate: geometry as data, with structural validation and no maths (solidcore's docs/layers.md, stage 1). Every caller uses `solidcore_record::brep` and `solidcore_record::native`; the old copies are deleted. What computes from coordinates stays with the kernel, in geom's `brep`: - `brep::check`: the record's geometric checks (orthonormal frames, non-degenerate planes, unit revolution axes, non-singular placements). `check::validate` and `check::validate_built` are the record's validation and these together, and every caller of `Brep::validate` uses them, so what is accepted is unchanged; - `brep::label`: a surface's natural ranges (a cone's apex), `curve_interval`, `edge_curve_interval` and `form_turns`; - `brep::pcurve::eval`: a pcurve's point; - `geometry::check`, `placement::moved` and `split_edges` become functions on the record. The kernel's native module keeps the conversion between a body and its saved record (metadata.rs) and checks the decoded record's geometry. `native/analytic.rs`, which no module declared, is deleted. solidcore-record is pinned to its rev on solidcore's `layer-record` branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT * Geometry: Vec2, Vec3, Plane and the profile types come from solidcore-record The plain data of vectors and points, sketch planes and planar profiles (`Seg2`, `Loop2`, `Region2`) moves to `solidcore-record`, with its constants and component-wise operators only (owner decision, solidcore layer split). Their geometry stays in geom as traits: `Vec2Geom`, `Vec3Geom`, `PlaneGeom`, `Seg2Geom`, `Loop2Geom`, `Region2Geom`, `Seg2ArcFit`, `Loop2ArcFit` and `Region2ArcFit`, in scope with `use solidcraft_geom::prelude::*`. Every caller names the types from `solidcore_record`; geom no longer defines or re-exports them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT * Kernel: pin solidcore-record to solidcore main f5e8e9a29 solidcore-record is merged on solidcore main (tangency-labs/solidcore#2519); its crate there is identical to the rev pinned before but for its clippy configuration. The lock is regenerated against the merged rev. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
11 KiB
Kernel: moving from truck to SMLib
How SolidCraft's kernel moved from truck to SMLib (usd-brep, Apache-2.0), as it was written up while the work was on the kernel-smlib branch (tracked in #4012, now closed: the branch landed on develop). It is a record of the steps and their pictures, not the current contract: that is native-brep.md, with the case ladder in kernel-ladder.md and kernel-cases.md. truck has since been removed.
The pictures live on the pr-media-kernel-smlib branch.
Where it started (2026-10-21)
Schema 4: a plain B-rep that mirrors SMLib's model
solidcraft_geom::brep now holds:
- regions and shells;
- oriented faces, and faces with parameter domains;
- edge and vertex loops;
- oriented edges, each carrying a pcurve (the edge in the face's (u, v));
- edges with intervals, and vertices;
- tolerances per body, per edge and per vertex.
Names follow STEP's B-rep vocabulary. Analytic curves and surfaces are unbounded; their bounds live in the edge interval and the face domain. A validator enforces one canonical order of faces, edges and vertices, which persistent names depend on. The history types moved beside it, to solidcore_record::brep::history (solidcore's record crate). The contract is in docs/native-brep.md and tangency-labs/solidcore#342.
truck bodies round-trip through schema 4 exactly, and the model cache stores it. Every corpus design, sample and imported assembly reopens from the cache with bit-identical volumes, areas and face names:
| Filleted box | Cylinder cut by a tilted box |
|---|---|
![]() |
![]() |
![]() |
![]() |
Pcurves for every edge
Pcurves are exact on planes. Elsewhere they're a cubic 2D B-spline fitted within tolerance, never a polyline. A seam edge gets two pcurves one period apart, and poles are handled. Each face's loops, drawn in its (u, v):
A naming guard for the move
apps/solidcraft-cli/tests/naming_corpus.rs rebuilds 154 designs and checks every face and edge name, and every stored reference, against tests/naming/. Every operation that switches to SMLib must keep them. Re-record with NAMING_CORPUS_BLESS=1.
Next, as planned then
- SMLib linked statically behind C shims (
solidcraft-smlib). - SMLib bodies exported to schema 4 and shown in SolidCraft.
- Booleans, fillet, chamfer and shell through SMLib, compared side by side with truck.
Updates
SMLib B-reps in SolidCraft (2026-10-21)
Each body below was made by SMLib, converted to schema 4 (canonical order, our own pcurves, validate() and check_geometry() passing), decoded into a SolidCraft Body, and rendered next to truck's primitive of the same size. The faces, edges and vertices are SMLib's own: a cylinder is 3 faces, a whole sphere 1 face bounded by its seam, a torus 1 face.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
The torus's and the cylinder's loops in (u, v), from our pcurve routine:
The SMLib volumes are SMLib's exact mass properties and match the closed form. "Decoded" is SolidCraft's mesh-based measure of the decoded body, within 2e-4 relative. These pictures come from a first JSON export; the same path now goes through the fork's flat converter (capi-flat) and is being rebuilt there.
Also on kernel-smlib since the last update:
- the link crate (
solidcraft-smlib); - the STEP geometry crate (
solidcraft-step); - history out of SMLib operations, and a side-by-side runner for truck and SMLib booleans;
Brep::check_geometry, run before any record is decoded;- cones that narrow along their axis.
Landed on kernel-smlib: SMLib bodies into SolidCraft through schema 4, and back (2026-10-21)
(4d72f7e8, 1522e133). SMLib B-reps leave the C API as the fork's flat arrays, a port of SMLib's own converter without USD. They become schema 4 records: canonical order, our own pcurves, validate() and check_geometry() passing. With the kernel's smlib feature, body_from_smlib decodes them into a Body.
The reverse direction is in too. A round-trip test sends every primitive (box, cylinder, sphere, cone, two frustums, torus) from SMLib to schema 4 and back into SMLib, and each keeps its counts and its exact volume within 1e-7, and passes SMLib's own check at every level. The pictures above are re-rendered on this path. SMLib's whole sphere is now bounded correctly by its seam in (u, v):
SMLib operations in SolidCraft (2026-10-21)
(kernel-smlib 4067ab4c). Each result was built live by SMLib, exported to schema 4, decoded into a Body and rendered. Next to it is truck's result of the same operation on the same inputs. SMLib's own check passes and schema 4's check_geometry is ok in all nine.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
SMLib's volumes equal the analytic values in every case. Truck's are mesh-measured and close. The face counts match, except that SMLib keeps a through-hole as one cylindrical face with a seam where truck uses two halves. History is classified differently in two places. SMLib keeps an untouched face that truck calls Modified. A shell's inner walls are Split for SMLib and Generated (offsets) for truck. No face name moves because of it, since names are built from origins.
A bug this found, being fixed now. SMLib builds a hole-rim fillet as a NURBS torus that closes on itself in u. Our pcurve routine gives NURBS directions no period, so both uses of that face's seam got the same pcurve, the face enclosed nothing and wasn't drawn:
Fixed: SMLib's hole-rim fillet (2026-10-21)
(kernel-smlib 4067ab4c..0eacff3e). A spline surface that closes on itself is now periodic over its knot interval for pcurves and tessellation, so the fillet torus's two seam uses sit one period apart. The decoded volume is 6989.0174 against SMLib's 6988.9956 (3e-6), where it was 6839.73. check_geometry now also refuses a loop that encloses no (u, v) area or turns the wrong way.
| before | after |
|---|---|
![]() |
![]() |
truck and SMLib booleans, side by side (2026-10-21)
(runner on kernel-smlib 8665746e: cargo run --release -p solidcraft-kernel --features smlib --example smlib_booleans -- --seeds 128). 411 operations: 128 random box/cylinder/sphere/torus pairs and 9 named cases, each as union, intersection and cut. Each kernel is timed on the boolean alone.
| median | worst | total | |
|---|---|---|---|
| truck boolean | 8.8 ms | 6.2 s | 44.0 s |
| SMLib boolean | 1.8 ms | 25 ms | 1.1 s |
| moving operands into SMLib | 2.6 ms | 91 ms | |
| moving the result back | 4.4 ms | 522 ms |
The cut sequence: a block, a joined boss, then three cuts, the last splitting the block. truck slows down with each cut and SMLib stays flat. SMLib matches the analytic volume exactly at every step, with the same face and edge counts as truck.
| step | truck | SMLib |
|---|---|---|
| join Ø20 boss | 9 ms | 5 ms |
| slot | 61 ms | 5 ms |
| Ø6 cross hole | 378 ms | 7 ms |
| splitting slot | 383 ms | 4 ms |
Where SMLib isn't at parity yet:
- 11 of 411 operations fail on SMLib, where a cylinder or torus is tangent to a planar face. truck completes all of them. Filed as tangency-labs/solidcore#383.
- About 15 SMLib volumes are wrong although the body is right: the same body measured after export matches. Filed as tangency-labs/solidcore#384.
Also to fix on our side (the conversion back to SolidCraft):
- results built from SMLib's own spheres and tori;
- an edge that runs along only part of its spline;
- a slow export of a one-face torus.
On the 27 named operations, SMLib's volumes are within 1e-6 of the analytic values, and truck's within 1e-3 (it measures from a tessellation).

































