Use the newly ported revolution surface's virtual iso constructor and B-spline base. Add 18 direct canonical-cylinder comparisons against the real C++ class. Refresh cache reports after the upstream dispatch changes and retain explicit CSI and classification deferrals. All required local gates pass; full differential validation covers 133 units with 85 green, 48 partial and zero failed. Part 9 compares 727/778 cases exactly. Co-Authored-By: OpenAI Codex <noreply@openai.com>
83 KiB
solidcore status
Last updated: 2026-10-13, PR workflow (surface.trim_srf_cache verified partial in PR #4). Update a summary line here when your unit changes it; per-unit state lives in plan/modules.toml.
Handoff (2026-10-13): from one local swarm to pull requests
The original local swarm (one machine, up to 10 agents) was stopped here so that others can contribute. From now on all work goes through PRs; see AGENTS.md section 4.
- Where things get ticked off:
plan/PROGRESS.md(checklist, regenerated onmainby.github/workflows/progress.ymlfromplan/modules.tomland openport: <id>PRs). The source of truth staysplan/modules.toml; evidence per unit isreports/difftest/<unit>.json. - Ready now:
curve.curve_p9,surface.plane_p1,surface.surf_of_revolution_p1,surface.surface_p9.surface.trim_srf_cacheis being contributed in PR #4, resuming the interruptedorigin/unit/surface.trim_srf_cacheWIP. - Throughput limit: the dependency chain, not people. Often only 2 to 5 units are
ready at once. A useful non-port task: find
depends_onlinks that are only 1 or 2 calls and could become back-edges (section 4a), which would widen the ready set. - CI was red on
mainbefore this handoff (newer clippy on GitHub; a test-only FFI buffer that needed ~37 GB on GitHub's runner). Fixed here: toolchain pinned to 1.98.0, andtopology.database_io's FFI wrapper now sizes buffers exactly. - Last unit, after everything else:
audit.recursion(bounds input-driven recursion; wasm has no stack guard page).
Progress
Counts are from plan/modules.toml. Line counts are the upstream function-body lines
(est_lines) assigned to each unit.
| Units planned | 261 (65 layers), 387,903 upstream lines |
| Green | 83 (104,137 lines): geom.extent3d, geom.vector2d, nlib.knots, nlib.math, topology.database_io, geom.tol, nlib.geom, geom.assert_array, geom.context, geom.extent1d, geom.matrix, geom.periodic_extent1d, geom.vector3d, nlib.basis_basic, nlib.srf_basic, geom.axis2_placement, geom.extent2d, geom.geom_utility_p2, geom.hash_table, geom.local_solve1d, geom.local_solve_nd, geom.polynomial, nlib.basis_adv, nlib.bezier, nlib.funcs_basic, geom.geom_utility_p1, geom.polar_box, nlib.crv_basic, geom.pseudo_box, nlib.crv_fit_p1, nlib.crv_tool_p1, nlib.iges, nlib.srf_tool_p1, curve.hermite_curve, geom.point_set, nlib.crv_fit_p2, nlib.crv_nurb, nlib.funcs_adv, nlib.srf_fit, nlib.crv_fit_p3, nlib.crv_geom, nlib.crv_tool_p2, nurbs.nurbs_crv, topology.topology, nlib.crv_adv_p1, nlib.crv_common, nlib.spiral, nlib.crv_adv_p2, nlib.srf_common, nlib.crv_approx, nlib.crv_offset, nlib.srf_nurb, nlib.srf_tool_p2, nurbs.b_spline_curve_n_lib, nurbs.nurbs_srf, nlib.srf_adv_p2, nlib.srf_adv_p3, nlib.srf_tool_p3, nlib.srf_tool_p4, curve.conic, curve.projected_curve, nlib.srf_adv_p4, nlib.srf_adv_p1, nlib.srf_adv_p5, nlib.srf_adv_p6, nlib.srf_offset, curve.offset_curve, surface.b_spline_volume, nurbs.b_spline_curve_p1, curve.parabola, primitive.api_curves, nurbs.b_spline_curve_p2, curve.curve_p2, curve.curve_cache, curve.hyperbola, curve.line, curve.ellipse_p2, curve.curve_p6, curve.curve_p7, curve.ellipse_p1, curve.curve_p8, curve.composite_curve_p1, curve.composite_curve_p2 |
| Partial | 31 (41,289 lines): geom.attribute, geom.tree, geom.object, geom.grid, geom.solution_array, geom.global_solver, geom.tools, geom.transform, geom.a_object, nurbs.b_spline_surface_p1, nurbs.b_spline_curve_p3, curve.crv_in_volume, curve.tangent_field, nurbs.b_spline_surface_n_lib_p1, curve.iso_curve, nurbs.b_spline_surface_n_lib_p2, nurbs.b_spline_surface_p2, surface.volume_p1, curve.curve_p4, surface.twist_volume, curve.curve_p3, curve.curve_p1, curve.crv_on_surf, surface.surface_p2, surface.curve_bounded_surface, surface.surface_cache_p1, curve.curve_p5, surface.trim_srf_cache, surface.plane_p1, surface.unbend_volume, surface.bend_volume |
| Ported (green + partial) | 114 units, 145,426 upstream lines (37.5%); about 150,000 Rust lines in crates/solidcore/src |
| Claimed | 0 (claims now use draft PRs) |
| Blocked | 0 |
| Todo | 147 (242,477 lines) |
What Wave 0 produced
- C++ reference kernel (agent A).
harness/cpp/build.shbuilds upstream SMLib and SM_API at c9c5979 with CMake and Ninja intobin/cpp/(static libs inbin/cpp/lib, pluscompile_commands.jsonandffi.env). It uses no TBB and makes no upstream patches.harness/cpp/run_tests.shruns the upstream suites. Details are inplan/baseline.md. - Port plan (agent B). The plan is in
plan/modules.toml(260 units),plan/order.mdandplan/closure.md. The call-graph tooling is inharness/callgraph/(run.sh,units.json,why.py). - Scaffolding (agent C). This covers LICENSE, NOTICE, ATTRIBUTION, README, AGENTS.md,
workspace lints, CI and the
crates/solidcoreskeleton (math,error,kernel,topologyarenas, and module stubs). - difftest harness (agent D). It has three parts:
crates/solidcore-ffi(shims built fromharness/cpp/shim/*.cpp),crates/difftest(runner, comparison and reports) and theharness.selftestunit. To add a unit you create 4 new files and edit no shared file. Seecrates/difftest/README.md. - Wave 0 close (this pass):
- Integration check, all green: build.sh, selftest difftest, workspace fmt/clippy/tests and the wasm32 check.
- Fixed the docs to use
difftest --unit <id>. - Added
SolidError::Nlib/NlibCode. - Ported the first unit and wrote
plan/findings.md.
Baseline (C++ reference, plan/baseline.md)
- Build: a clean build with
-j6undernicetakes 111 s (271 Ninja steps). An incremental build takes about 7 s, and a no-op build under 1 s. - SM_API_test_app: PASS. It reports 115 calls; that is 112 real tests passing, 0
failing and 2 skipped. The two skipped tests are stubbed because they compile
upstream/Examples. Runtime is 50 s. - prog_test_app: PROG_TEST SUCCESS (16 default suites, 46 s). All 17 suites pass when
run one at a time, including
stitch.
Green units
| Unit | Rust module | difftest | Notes |
|---|---|---|---|
harness.selftest |
(harness only) | 28 cases, max_rel_err 0 | not a plan unit |
nlib.knots |
nurbs::knots |
2953 cases (1753 fixture + 1200 generated), max_rel_err 0 | bit-identical; a 51,753-case run was also clean |
geom.vector2d |
geom::vector2d |
4060 cases (60 fixture + 4000 generated), max_rel_err 0 | bit-identical; a 200,060-case run was also clean |
topology.database_io |
topology::database_io |
2059 cases (59 fixture + 2000 generated), max_rel_err 0 | stream state and position match per step; a 200,059-case run was also clean |
nlib.math |
nurbs::math |
3076 cases (76 fixture + 3000 generated), max_rel_err 0 | bit-identical; a 100,076-case run was also clean |
geom.periodic_extent1d |
geom::periodic_extent1d |
4054 cases (54 fixture + 4000 generated), max_rel_err 0 | bit-identical; upstream quirks in findings.md |
geom.assert_array |
geom::assert_array |
3734 cases (14 fixture + 720 label-table + 3000 generated), max_rel_err 0 | bit-identical; all 86 label tables ported here, lengths checked against libSMLib.a symbol sizes |
geom.matrix |
geom::matrix |
4044 cases (44 fixture + 4000 generated), max_rel_err 0 | bit-identical; a 100,044-case run was also clean; upstream quirks in findings.md |
geom.extent1d |
geom::extent1d |
3091 cases (91 fixture + 3000 generated), max_rel_err 0 | bit-identical; wired extent3d intervals and tol MapTo1d / GetScaledZero(SmExtent1d) |
geom.polynomial |
geom::polynomial |
6038 cases (38 fixture + 6000 generated), max_rel_err 0 | bit-identical; quartic UB with tol > 1 noted in findings.md |
nlib.srf_basic |
nurbs::srf_basic |
3220 cases (220 fixture incl. Surface.sms / offset_nurb.sms / DropCurveSurface.sms + 3000 generated), max_rel_err 0 | bit-identical; a 100,220-case run was also clean; NL_SURFACE as Surface (storage/index split), quirks in findings.md |
geom.extent2d |
geom::extent2d |
3069 cases (69 fixture + 3000 generated), max_rel_err 0 | bit-identical; a 30,069-case run was also clean; wired tol GetScaledZero(SmExtent2d); PeriodicWrap hang note in findings.md |
nlib.basis_basic |
nurbs::basis_basic |
2596 cases (1096 fixture incl. every pt_TestFiles .smc/.sms knot vector + 1500 generated), max_rel_err 0 | bit-identical; a 201,096-case run was also clean; upstream endless loops / OOB reads noted in findings.md |
geom.local_solve_nd |
geom::local_solve_nd |
3043 cases (43 fixture + 3000 generated), max_rel_err 0 | bit-identical (full Evaluate trace of a shared test equation set compared); a 30,043-case run was also clean; wired tol GetScaledZero(SmEvalNFunctionsObject); quirks in findings.md |
geom.hash_table |
geom::hash_table |
619 cases (19 fixture incl. SmHealData coincident-vertex replays + 600 generated), max_rel_err 0 | bit-identical; a 10,019-case run was also clean; upstream use-after-free and quirks in findings.md |
geom.local_solve1d |
geom::local_solve1d |
3316 cases (316 fixture + 3000 generated), max_rel_err 0 | bit-identical (full Evaluate trace of a shared test function, outputs and solver state compared); a 20,246-case run was also clean; interval-less constructor and other quirks in findings.md |
geom.polar_box |
geom::polar_box |
3030 cases (30 fixture programs incl. pt_TestFiles control-polygon edges + 3000 generated programs on 4 box registers), max_rel_err 0 | bit-identical; Union UB case (stale basis after ReSet), aliased Union and region-to-arc quirk in findings.md |
nlib.bezier |
nurbs::bezier |
2223 cases (223 fixture incl. SM_API_test rectangle/polygon segments, FitCrvPoints.pts, Points.pts, Surface.sms + 2000 generated), max_rel_err 0 | bit-identical; a 50,223-case run was also clean; dangling-matrix, NL_PascalTri bounds and recursion-limit quirks in findings.md |
nlib.funcs_basic |
nurbs::funcs_basic |
3130 cases (130 fixture + 3000 generated), max_rel_err 0 | bit-identical; a 50k-seed run was also clean; notes in findings.md |
nlib.basis_adv |
nurbs::basis_adv |
1744 cases (244 fixture + 1500 generated), max_rel_err 0 | bit-identical; a 100,244-case run was also clean; N_BasisDerivsArray unbounded span walk and N_BasisInsertKnots X[mx+1] read in findings.md |
geom.geom_utility_p1 |
geom::geom_utility::p1 |
4185 cases (185 fixture incl. SM_API_test rectangle/polygon, FitCrvPoints.pts, Points.pts, PointSet sections + 4000 generated), max_rel_err 0 | bit-identical; file-static helpers tested via a namespaced compile of SmGeomUtility.cpp; best-fit-line eigenvector, silhouette and FALSE-status quirks in findings.md |
geom.axis2_placement |
geom::axis2_placement |
4048 cases (48 fixture + 4000 generated), max_rel_err 0 | bit-identical; promoted from partial when AreCoaxial was wired to geom.geom_utility_p1 |
geom.geom_utility_p2 |
geom::geom_utility::p2 |
3270 cases (70 fixture + 3200 generated), max_rel_err 0 | bit-identical; promoted from partial when its p1 back-edges were wired; PointSetType is p1's |
geom.extent3d |
geom::extent3d |
3086 cases (86 fixture + 3000 generated), max_rel_err 0 | bit-identical; promoted from partial when geom.pseudo_box wired Circumscribe (axis-map quirk in findings.md) |
geom.pseudo_box |
geom::pseudo_box |
3011 cases (11 fixture incl. my_test_pseudobox + 3000 generated programs on 4 box registers), max_rel_err 0 | bit-identical; wired extent3d Circumscribe, tol GetScaledZero(SmPseudoBox), transform EvaluateBoundingBoxSimple (aligned branch) |
nlib.funcs_adv |
nurbs::funcs_adv |
2080 cases (80 fixture incl. 6 pt_TestFiles .smc curves + 2000 generated), max_rel_err 0 | bit-identical; the three upstream products share one generic body; inputs rescaled in place as upstream |
nlib.crv_tool_p1 |
nurbs::crv_tool::p1 |
2143 cases (143 fixture on the six pt_TestFiles .smc curves, in-place and out-of-place C++ forms + 2000 generated), max_rel_err 0 | bit-identical; promoted from partial when nlib.crv_nurb wired N_CrvGetMinMaxWeightsAndPts (rational N_CrvRemoveKnot) |
nlib.srf_fit |
nurbs::srf_fit |
1556 cases (56 fixture on the TestSmCreateSurfaceFromPoints 5x7 grid and the 11 PointSet sections + 1500 generated), max_rel_err 0 | bit-identical; a 30,056-case run was also clean; N_FitSrfInterpParams always returns NL_NO, LstSqApprox ruling overwrites the caller grid (copy in Rust) |
geom.tol |
geom::tol |
3440 cases, max_rel_err 0 | bit-identical; SmRelation pointer-hash order sorted on both sides |
geom.context |
geom::context |
2013 cases (13 fixture scripts + 2000 generated), max_rel_err 0 | bit-identical; wired the Option<&Context> overloads of geom.tol |
nlib.geom |
nurbs::geom |
3173 cases (173 fixture + 3000 generated), max_rel_err 0 | bit-identical |
geom.vector3d |
geom::vector3d |
6114 cases (114 fixture from my_test_vector3d + edge cases, 6000 generated), max_rel_err 0 | bit-identical; Vec3 lives here |
nlib.iges |
nurbs::iges |
2029 cases (29 fixture + 2000 generated), max_rel_err 0 | bit-identical; entity data passed as Entity126 / Entity128 |
nlib.srf_tool_p1 |
nurbs::srf_tool::p1 |
3132 cases (132 fixture + 3000 generated), max_rel_err 0 | bit-identical; extract_patch takes a Rectangle |
nlib.crv_fit_p1 |
nurbs::crv_fit::p1 |
2084 cases (84 fixture + 2000 generated), max_rel_err 0 | bit-identical; M[2i+1][-1] write ported as is (findings.md) |
nlib.crv_fit_p3 |
nurbs::crv_fit::p3 |
2242 cases (242 fixture on FitCrvPoints.pts / Points.pts and the upstream driver call shapes, edge cases + 2000 generated), max_rel_err 0 | bit-identical; errors upstream ignores in the end-derivative estimate are returned, NL_INHERITED reads the caller curve's knots (findings.md) |
nlib.spiral |
nurbs::spiral |
2032 cases (32 fixture from the helix tests and edge cases + 2000 generated), max_rel_err 0 | bit-identical; upstream crashes on its early input errors (ffi rejects them), span-limit *NumPoints quirk ported (findings.md) |
geom.point_set |
geom::point_set |
2029 cases (29 fixture + 2000 generated), max_rel_err 0 | bit-identical; unprojected tight-box angles and centroid block quirk kept (findings.md) |
topology.topology |
topology::topology |
657 cases (57 fixture + 600 generated scripts), max_rel_err 0 | bit-identical; SmTopologyChangeCallback::Execute unused upstream and not ported; dangling-callback UB on tracker destruction skipped (findings.md) |
nlib.crv_nurb |
nurbs::crv_nurb |
2871 cases (871 fixture on the six pt_TestFiles .smc curves, SM_API_test rectangle/polygon/circle and reversed-line curves, edge cases + 2000 generated), max_rel_err 0 | bit-identical; promoted from partial when nlib.crv_geom wired N_CrvArcLength (N_CrvGetDegenSegs) |
nlib.crv_geom |
nurbs::crv_geom |
1723 cases (223 fixture on the six pt_TestFiles .smc curves + edge cases, 1500 generated incl. circles, lines, degenerate and closed curves), max_rel_err 0 | bit-identical; closest-point best values on failure and polyline non-termination quirks in findings.md |
nlib.crv_tool_p2 |
nurbs::crv_tool::p2 |
2124 cases (124 fixture on the six pt_TestFiles .smc curves and curve pairs, in-place and out-of-place C++ forms, degenerate-segment edge cases + 2000 generated), max_rel_err 0 | bit-identical; RemoveKnots / ReduceDegree(Once) / MakeCompatible* / RemoveDegenSegs; wired N_CrvsMakeCompatible into crv_basic |
nurbs.nurbs_crv |
nurbs::nurbs_crv |
4089 cases (1089 fixture on the six pt_TestFiles .smc curves, SM_API_test rectangle/polygon edges and edge cases + 3000 generated), max_rel_err 0 | bit-identical; sm_BasisEval flags the domain ends, sm_CrvTrim returns NL_INP_ERR (findings.md) |
nlib.crv_basic |
nurbs::crv_basic |
3417 cases (417 fixture incl. the six pt_TestFiles .smc curves, .sms cross-boundary derivatives, SM_API_test rectangle/polygon segments, my_test_reversed_line_join lines + 3000 generated), max_rel_err 0 | bit-identical; promoted from partial when nlib.crv_tool_p2 wired N_CrvsMakeCompatible (N_CrvsAreCoincident maps its NLib errors to false, as upstream) |
nlib.crv_adv_p1 |
nurbs::crv_adv::p1 |
2670 cases (670 fixture on the six pt_TestFiles .smc curves, SM_API_test rectangle/hexagon edges and edge cases + 2000 generated), max_rel_err 0; a 100,670-case run was also clean | bit-identical; ST_curxcdr / ST_curxcrr as Curve::extend_by_*_at_depth; in-place ST_curxcrr G2 recursion frees garbage upstream (findings.md) |
nlib.crv_adv_p2 |
nurbs::crv_adv::p2 |
2815 cases (815 fixture on the six pt_TestFiles .smc curves, SM_API_test rectangle/hexagon edges and edge cases + 2000 generated), max_rel_err 0; a 300,815-case run was also clean | bit-identical; remove_knots shares its body with N_CrvRemoveKnotsDerivConstraints; rational N_CrvSumDiffCrv over different ranges corrupts the heap upstream (findings.md) |
nlib.crv_approx |
nurbs::crv_approx |
1646 cases (146 fixture: sm_ApproxCircArc-style counts and arc fits, ApproximateRationalCurve-style approximations of the six pt_TestFiles .smc curves and exact NURBS circles, edge cases + 1500 generated), max_rel_err 0 | bit-identical; degree 2 parabolic arcs with parallel tangents and failed tangent projections use uninitialised points upstream (ffi rejects them; the port errors) |
curve.hermite_curve |
curve::hermite_curve |
2131 cases (131 fixture + 2000 generated), max_rel_err 0 | green once curve.curve_p2 wired SmCurve::IsKindOf (93 deferred before) |
nurbs.b_spline_curve_n_lib |
nurbs::b_spline_curve_n_lib |
2282 cases (282 fixture + 2000 generated), max_rel_err 0; a 100,282-case run was also clean | green once curve.curve_p2 wired SmCurve::ApproximateLength (57 deferred before) |
curve.conic |
curve::conic |
3240 cases (240 fixture: all 15 smCurveTest.cpp IntersectConic scenarios with their solution-count asserts, MakeDupin indicatrix conics, edge cases + 3000 generated), max_rel_err 0; --perturb SENSITIVE |
bit-identical; first CurveKind added after Hermite; wired SmCurve::ReadFromDB(SmConic_TYPE) in curve.curve_p2; Evaluate zero-tangent fix reads an unwritten slot upstream (findings.md) |
curve.projected_curve |
curve::projected_curve |
2727 cases (227 fixture: the SmTransSweepGeometry parallel and SmRotationalSweepGeometry rotation projections of a FitCrvPoints cubic, perspective views, ownership-gated edits, MakeOrthoNormal edge cases, ReadFromDB streams v34/v36 ASCII and binary + 2500 generated), max_rel_err 0; --perturb SENSITIVE |
bit-identical; CurveKind::Projected; wired SmCurve::ReadFromDB(SmProjectedCurve_TYPE) in curve.curve_p2; upstream MakeOrthoNormal only unitizes zero vectors and Transform needs UNSURE ownership (findings.md) |
nurbs.b_spline_curve_p1 |
nurbs::b_spline_curve::p1 |
2216 cases (216 fixture on the six pt_TestFiles .smc curves, smSweepTest lines/circles, edge cases + 2000 generated), max_rel_err 0; a 30,216-case run was also clean | green once curve.curve_p1 wired SmCurve::TessellateByBisection (4 deferred before) |
curve.parabola |
curve::parabola |
3044 cases, max_rel_err 0, 0 deferred | bit-identical; CurveKind::Parabola runs the SmBSplineCurve methods on its base (as Hyperbola); GlobalPointSolveSTEP wired to curve.curve_p1 at merge (206 deferred before) |
nurbs.b_spline_curve_p2 |
nurbs::b_spline_curve::p2 |
2328 cases (328 fixture: the six pt_TestFiles .smc curves through every op and via ReadFromDB, my_test_reversed_line_join lines, SM_API_test rectangle/polygon joins, arc/line joins, Bezier box cases, edge cases + 2000 generated), 2239 bit-identical; a 20,328-case run was also clean | promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.curve_p2 |
curve::curve::p2 |
3021 cases (21 fixture: prog_test line and circle curves, ApproximateLength(ivl, 20), circle seam continuity, Hermite base defaults, ReadFromDB dispatch incl. conics + 3000 generated over B-spline, Hermite and arc curves, static helpers, solver box tests), 3016 bit-identical; --perturb SENSITIVE |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.curve_cache |
curve::curve_cache |
638 cases (38 fixture: lines, cubics, C0 quadratic, closed, degenerate, 2D, rational arc and Hermite curves through Tessellate with every option, sub/beyond/disjoint/reversed intervals, GetAnalyticCurve, type queries + 600 generated), 636 bit-identical |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.hyperbola |
curve::hyperbola |
2062 cases (62 fixture: smCurveTest.cpp my_test_analytic_curves hyperbola (CreateCanonical, AdjustSTEPInterval(-1,2), evaluation, GlobalPointSolveSTEP, ReverseParameterization), the smSurfaceTest.cpp extrusion-generator hyperbola, edge cases, ReadFromDB records + 2000 generated), 2030 bit-identical; --perturb SENSITIVE |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.line |
curve::line |
2103 cases (103 fixture: my_test_reversed_line_join, smTopoTest canonical lines, my_test_analytic_curves / my_test_bsplinecurve_nlib lines, SM_API lines, edge cases + 2000 generated op programs), 2102 bit-identical; a 40,103-case run was also clean; --perturb SENSITIVE |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.ellipse_p2 |
curve::ellipse::p2 |
2102 cases (102 fixture: my_test_analytic_curves circle/ellipse, my_test_drop_curve_near_seam circles, smCurveTest circle/circle and line/circle configurations as SmEllipse, edge cases + 2000 generated op programs), 2005 bit-identical; a 30,102-case run was also clean; --perturb SENSITIVE; constructor co-ported from curve.ellipse_p1 |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.curve_p6 |
curve::curve::p6 |
3136 cases (136 fixture: SM_API_test rectangle / hexagon edges (boxes, planarity, coincidence, gaps, drops), my_test_reversed_line_join line pairs, smCurveTest DeformToEndPointTargets B-spline and line steps, circle boxes, edge cases + 3000 generated), 3048 bit-identical; 30k-seed run clean; --perturb SENSITIVE |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.curve_p7 |
curve::curve::p7 |
3027 cases (27 fixture: SM_API_test rectangle / hexagon edge pairs, my_test_reversed_line_join segments, arc plane intersection / classification with comb, edge cases: coincident, tolerance-limit gaps, degenerate Hermite, self-intersecting loop, projections + 3000 generated: whole SolveIt and single leaf-pair LocalSolve, GlobalPropertyAnalysis over all 29 properties), 1716 bit-identical; --perturb SENSITIVE; co-ported sm_FindAdditionalCcTangentGuess |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.ellipse_p1 |
curve::ellipse::p1 |
2028 cases (28 fixture: my_test_analytic_curves circle/ellipse, smSurfaceTest / smTopoTest / smCurveTest circles (CreateCanonical, AdjustSTEPInterval, EditParameterization, copy), edge cases + 2000 generated programs over the constructors, CreateCanonical and ReadFromDB records), 1896 bit-identical; --perturb SENSITIVE; CurveKind::Ellipse (ellipses and circles); &Kernel threaded through the Curve methods whose ellipse override rebuilds the polar converter |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.curve_p8 |
curve::curve::p8 |
3058 cases (58 fixture: SM_API_test rectangle / polygon edge pairs, my_test_reversed_line_join line pairs, with and without the coincidence check; edge cases: line and B-spline line crossings / overlaps / self intersections, vertex touch, parabola tangency, parallel gaps at the tolerance limit, coincident cubics, degenerate Hermite, looping self intersection, disjoint boxes + 3000 generated GlobalCurveIntersect calls), 1968 bit-identical; --perturb SENSITIVE; wired the TrimGap stub in nurbs.b_spline_curve_p3 |
promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.composite_curve_p1 |
curve::composite_curve::p1 |
3093 cases, all bit-identical | promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
curve.composite_curve_p2 |
curve::composite_curve::p2 |
661 cases, all bit-identical | promoted to green by curve.curve_p5 (0 deferred after its stubs were wired) |
Partial units
Every non-deferred difftest case passes; deferred cases wait on the listed unit (never reported as green).
| Unit | Rust module | difftest | Deferred on |
|---|---|---|---|
geom.tree |
geom::tree |
618 cases (18 fixture + 600 generated), 611 bit-identical; a 20,018-case run was also clean | 7 (surface.surface_cache_p3: GetBezierPtr and GetGwNurbPointer read the cache's patch data, which a bare tree reaches once the tree construction hands it over; GetOwnerObject of surface trees wired by surface.surface_cache_p1) |
geom.attribute |
geom::attribute |
2029 cases (29 fixture + 2000 generated), 1991 bit-identical; a 200,029-case run was also clean | 38 (topology.shell: merge-region Merge) |
geom.object |
geom::object |
624 cases (24 fixture + 600 generated), 593 bit-identical | 31 (every non-empty SmShape: topology.face_p2 11, topology.brep_p1 10, topology.vertex 6, topology.edge 4) |
geom.grid |
geom::grid |
615 cases (15 fixture + 600 generated), 613 bit-identical; a 30,015-case run was also clean | 2 (surface.surface_cache_p3 1: SmSurfaceGridElement::Init needs a bare tree node's patch data; surface.surface_p1 1: ExpandHierarchy via smsurf_GetSurfaceCache / CacheMakeOrValidate) |
geom.solution_array |
geom::solution_array |
810 cases (10 fixture + 800 generated), 754 bit-identical | 56 (object geometry: curve.curve_p2 16, topology.face_p2 14, topology.edge 12, surface.surface_p2 9, topology.vertex 5) |
geom.a_object |
geom::a_object |
2040 cases (40 fixture + 2000 generated), 1872 bit-identical; a 50,040-case run was also clean | 168 (topology.brep_cache 131: new SmBrepCache; topology.shell 37: SmRegion::IsVoid) |
geom.tools |
geom::tools |
913 cases, 721 bit-identical | 192 (curve.curve_p2 68, topology.brep_p1 64, topology.vertex 60) |
geom.transform |
geom::transform |
3042 cases, 3039 bit-identical | 3 (surface.volume_p2: SmVolume::ReadFromDB / EvaluatePoint); surface.volume_p1 wired |
geom.global_solver |
geom::global_solver |
620 cases, 611 bit-identical | 9 (curve.curve_p2: opaque solution objects not yet resolvable to a Curve, see findings) |
nurbs.b_spline_surface_p1 |
nurbs::b_spline_surface::p1 |
2120 cases (120 fixture on the three pt_TestFiles .sms surfaces incl. ReadFromDB, prog_test cone/bilinear patches, canonical, sweep, edge cases + 2000 generated), 2058 bit-identical | 62 (surface.surface_p1 43: offset, degenerate-curve test, STEP inversion solves; surface.surface_p7 19: EvaluateNormal in polar boxes; IsKindOf wired by surface.surface_p2) |
nurbs.b_spline_curve_p3 |
nurbs::b_spline_curve::p3 |
2195 cases (195 fixture: the six pt_TestFiles .smc curves through every op, my_test_curve_interface JoinWith / GetControlPoint, my_test_reversed_line_join lines, SM_API_test rectangle/polygon joins and gaps, edge cases + 2000 generated), 2036 bit-identical | 159 (curve.curve_p5 159; GlobalCurveIntersect wired by curve.curve_p8, GlobalPropertyAnalysis by curve.curve_p7, SmCurve::AssertValid by curve.curve_p4, SmCurve::IsLine co-ported by curve.curve_cache) |
nurbs.b_spline_surface_n_lib_p1 |
nurbs::b_spline_surface_n_lib::p1 |
665 cases (65 fixture: my_test_bsplinesurface_nlib offset/sweep/swung, my_test_surface_interface ops on the surf1 net, surf1 Coons patches, the three pt_TestFiles .sms surfaces and 3D .smc curves through every op, edge cases + 600 generated), 638 bit-identical; --perturb SENSITIVE |
27 (surface.surface_p5: SmSurface::IsSingularity in CreateExtendedSurface) |
nurbs.b_spline_surface_n_lib_p2 |
nurbs::b_spline_surface_n_lib::p2 |
654 cases (54 fixture: my_test_surface_interface skinning of surf1 iso curves, smAdvancedTest TrimWithDomain fractions, the three pt_TestFiles .sms surfaces through Evaluate / TrimWithDomain / skinning / bilinear corner blends, degenerate-side zero-tangent fixes, edge cases + 600 generated), 628 bit-identical; --perturb SENSITIVE; 20k-seed run clean |
26 (curve.crv_on_surf 17: ApproximateCurve in G1 corner blends; surface.surface_p7 5: EvaluateNormal in n-sided blends; curve.curve_p5 4) |
nurbs.b_spline_surface_p2 |
nurbs::b_spline_surface::p2 |
763 cases (63 fixture: my_test_surface_interface surf1 through SwapUV / iso curves / SplitAt / CopySubPatch / AdjustSTEPUVDomain / control point ops, my_test_sp_analy CreateSurfOfRevolution, my_create_nurb_surf SetCanonical, the three pt_TestFiles .sms surfaces, revolutions of three .smc curves, edge cases + 700 generated), 718 bit-identical; --perturb SENSITIVE |
45 (surface.surface_p1 20: Evaluate1stDerivatives in non-rectangular trimmed planes, SmSurface::IsDegeneratePoint; curve.curve_p5 11: IsArc in spheres from three arcs; surface.surface_p7 9: IsPlanar in GetMaxAnalyticDomain; surface.plane_p2 5: IsNurbSurfacePlane in CopyAndAddAnalytics) |
curve.tangent_field |
curve::tangent_field |
655 cases, 511 bit-identical | 144 (surface.surface_p7 132, surface.surface_p1 12; GetNaturalUVDomain wired by surface.surface_p2, the runner now builds the shim bilinear surface); SmCrvOnSurf::ReadFromDB wired by curve.crv_on_surf |
curve.iso_curve |
curve::iso_curve |
2874 cases (874 fixture: iso curves of Surface.sms / offset_nurb.sms / DropCurveSurface.sms, bilinear and NURB-less edge cases, ReadFromDB streams + 2000 generated), 2754 bit-identical; --perturb SENSITIVE |
120 (surface.surface_p6 120; SmSurface::ReadFromDB wired by surface.surface_p2); the SmBSplineSurface::Evaluate back-edge was wired in style review batch #13, SmLine by curve.line |
curve.crv_in_volume |
curve::crv_in_volume |
2030 cases, 1822 bit-identical | 208 (intersect.curve_class_p4 175: uncached breaks; surface.volume_p2 33: EvaluatePoint, ReadFromDB); the real Volume replaced the stand-in (surface.volume_p1) |
surface.volume_p1 |
surface::volume::p1 |
2886 cases (886 fixture: plain / transform / oriented / compound chains through every op, TestSmTransform rotations and translations, lifts of every form and order, ConcatEvals, edge cases + 2000 generated), 2847 bit-identical; a 20,886-case run was also clean; --perturb SENSITIVE |
39 (surface.unbend_volume: its simple maps; twist wired) |
surface.twist_volume |
surface::twist_volume |
1572 cases (72 fixture: constructor repairs, evaluations n 0..5 with / without a next map, bounding boxes, inverse and solves, predicates, copies, equality, ASCII reads + 1500 generated), 1569 bit-identical; a 20,072-case run was also clean; --perturb SENSITIVE |
3 (surface.volume_p2: SmVolume::ReadFromDB) |
curve.curve_p4 |
curve::curve::p4 |
3259 cases (259 fixture: SM_API_test rectangle / polygon edges (also the far 1e-9 / 1.5e-6 ones), my_test_reversed_line_join line pairs, the my_test_drop_point unit circle through every curve property + 3000 generated: chains, closed / degenerate tests, polar boxes, AssertValid, solution filter, projected distance, Newton evaluators, analytic intersections), 3210 bit-identical; a 30,259-case run was also clean; --perturb SENSITIVE |
49 (surface.surface_p8 49: SmSurface::GlobalCurveIntersect in HasPlaneCrossings); ellipse intersections wired by curve.ellipse_p1 |
curve.curve_p3 |
curve::curve::p3 |
3076 cases (76 fixture: SM_API_test rectangle / hexagon loops (normals, gap fixing, linearity, plane intersections), smCurveTest line at-distance and circle drop-point / extrema solves + 3000 generated), 2334 bit-identical; --perturb SENSITIVE |
742 (curve.curve_p5: LocalPropertyAnalysis 385, SmCCIntersectENFO::Evaluate 136, base SmCurve::Length 120, SmFindCCNormalENFO::Evaluate 101) |
curve.curve_p1 |
curve::curve::p1 |
3083 cases (83 fixture: smCurveTest my_test_point_extrema / my_test_drop_point line and circle solves, the circle centre, SM_API_test rectangle / hexagon edges through every op, edge cases + 3000 generated), 2493 bit-identical; a 12,083-case run was also clean; --perturb SENSITIVE |
590 (curve.curve_p5: LocalPropertyAnalysis 310, SmFindCCNormalENFO::Evaluate 163, SmCCIntersectENFO::Evaluate 73, IsArc 44) |
curve.crv_on_surf |
curve::crv_on_surf |
2094 cases (94 fixture: smTopoTest loop UV-gap edges on the bulge patch, smMergeTest plane boundary via the UV-line constructor, face trim curves on Surface.sms / DropCurveSurface.sms + 2000 generated op programs incl. upstream WriteToDB records), 1598 bit-identical; --perturb SENSITIVE; CurveKind::CrvOnSurf; ApproximateCurve / DropPoint / CalculateBoundingBox dispatch to the override |
496 (intersect.curve_class_p4 294: uncached breaks; surface.surface_p7 147: EvaluateNormal; surface.surface_p1 50: Evaluate2ndDerivatives; curve.curve_p5 5; SmSurface::ReadFromDB wired by surface.surface_p2) |
surface.surface_p2 |
surface::surface::p2 |
869 cases (69 fixture: the three pt_TestFiles .sms surfaces through queries / Evaluate / knots / edits / copies / SmSurfaceEval / ReadFromDB, base SmSurface() defaults, TestSmPrincipalDirectionPairing + 800 generated), 756 bit-identical; --perturb SENSITIVE; introduces Surface / SurfaceKind (B-spline only) and SurfaceEvaluate |
113 (surface.surface_p5 36: extensions; curve.curve_p5 33: CC local solves and plane splits; surface.surface_p7 18: GetMaxAnalyticDomain; surface.surface_p1 13: STEP inversion solves; surface.plane_p2 11: CopyAndAddAnalytics; intersect.adv_surface_intersector 1; topology.trimming_tools 1) |
surface.curve_bounded_surface |
surface::curve_bounded_surface |
535 cases (135 fixture: Surface.sms / DropCurveSurface.sms / offset_nurb.sms bases with the pt_TestFiles .smc boundaries through state / queries / Evaluate / SmSurface defaults / operator== / SetContext, WriteToDB-layout ReadFromDB records incl. the dropped implicit flag and the UV-read-with-3D-type bug + 400 generated), 359 bit-identical; --perturb SENSITIVE; SurfaceKind::CurveBounded |
176 (surface.surface_p1 85: EvaluateSimple / EvaluateSTEP / EvaluateSTEPPoint / operator==; surface.surface_p3 56: EvaluateGeometric / EvaluateNormalSection; surface.surface_p7 25: EvaluateNormal; surface.surface_p5 10: EvaluateNormalAtSingularity) |
surface.surface_cache_p1 |
surface::surface_cache::p1 |
872 cases (72 fixture: the three pt_TestFiles .sms surfaces through boundary bounding planes, Bezier approximation, size tolerances, corners, iso boundary curves, knot continuity, BuildTreeTop over their knot spans and polar boxes, hand-built decompositions through FindUnmarkedLeafNode / GetConnectedRegion / MarkFaceContainment / GetFirstBezierPatch, alternate cache matching, static helper edge cases + 800 generated), 709 bit-identical; a 20,072-case run was also clean; --perturb SENSITIVE; SurfaceCache keeps the patch / aux arenas, tree nodes carry the arena slot (SurfaceAux::slot) |
163 (surface.surface_p7 107: EvaluateNormal in every polar box and at collinear boundary samples; surface.surface_p5 37: IsSingularity in every Bezier approximation; surface.surface_p1 19: GetFace) |
surface.surface_cache_p2 |
surface::surface_cache::p2 |
905 cases (105 fixture: the three pt_TestFiles .sms surfaces (and curve-bounded surfaces over them) through Bezier patch blocks, n-th bounding planes, TestAgainstTolerances on hand-built patch / aux-data / head nodes at the tolerance limits, SubdivideByBlock without surface, SM_API_test rectangle / hexagon planes, alternate cache eviction + 800 generated incl. 1e-4 / 1e4 scaled surfaces), 721 bit-identical; --perturb SENSITIVE; wired the SmPlane::IsSingularity override into the cache's Bezier approximation |
184 (surface.plane_p1 53: plane iso curves; surface.surface_p1 47: GetFace in the B-spline bounding box; surface.surface_p5 43: IsSingularity, curve-bounded iso curves; surface.surface_p7 28: EvaluateNormal; surface.surface_p9 13: FindTessellationSplits in every SubdivideByBlock with a surface) |
curve.curve_p5 |
curve::curve::p5 |
3097 cases (97 fixture: SM_API_test rectangle / hexagon edges through clean-up, duplicates, IsLine / IsLinear / IsArc / Length / IsDomainBoundary, my_test_reversed_line_join lines, closest-point inversions, Newton Jacobians, edge cases + 3000 generated over 16 ops), 3067 bit-identical; a 20,097-case run was also clean; --perturb SENSITIVE; wiring its 13 stubs promoted 12 units to green |
30 (mass.integrator 28: SmIntegrator::IntegrateIt in Length of curved spans; curve.curve_p2 2: GetNaturalInterval through a solution handle in TrimGap) |
Ready set
2026-10-13 (after surface.surface_cache_p1): surface.trim_srf_cache
(surface.surface_cache_p2 also waits for curve.curve_p5 and surface.surface_p9,
surface.surface_cache_p3 for both). curve.curve_p5 has landed (partial), so surface.surface_cache_p2 and
surface.surface_cache_p3 now wait only on surface.surface_p9. New curve classes become CurveKind
variants of curve::curve::Curve (see plan/findings.md, curve.curve_p2); a merge that
adds a variant must also extend the exhaustive matches in curve::curve_cache.
Recent findings (details in plan/findings.md)
-
curve.curve_p5(LocalPropertyAnalysis,IsArc,Length, the curve/curve Newton equations, ...) wired its 13 stubs and promoted 12 units to green. UpstreamCrvsCleanupcrashes inTrimGapnext to a closed curve, so those cases are not generated. The wiring also exposed gaps in dependent runners: solution handles without the curve's interval, the ellipse conversion's out-parameter, and an uninitialised branch slot ofSmGlobalSolver::SolveIt. Each was fixed, or the case was excluded as undefined behaviour. -
No Rust-vs-C++ disagreement so far: every non-deferred case of the 110 ported units is bit-identical (
max_rel_err = 0). -
curve.curve_p7ports the curve/curve intersection local solve andGlobalPropertyAnalysis;CciGlobalSolvernow carries its curves and kernel (with_curves), and a pruned curve tree (aux data without full tree) is rejected (its leaves are not spans; upstream reads them as spans). -
curve.curve_p6boundsGlobalCoincidenceChecker's recursion (MAX_COINCIDENCE_DEPTH) and retags the ellipse stub tocurve.ellipse_p1;curve.curve_p1co-portsIsLinedispatch andCacheMakeOrValidate's cache build. -
curve.iso_curve:SmIsoCurve::Evaluatewrites the surface's[nu + 1][nv + 1]derivative array (one order zero) straight into the curve's array; the port flattensBSplineSurface::evaluate's rows. The 355 cases deferred onnurbs.b_spline_surface_n_lib_p2now pass bit-identical (that unit's porter had found no stub becausecurve.iso_curvemerged later). -
curve.curve_cacheco-portedSmFindCurveDistanceExtremaEFO::Evaluate(curve.curve_p4) andSmCurve::IsLine(curve::curve::p5::is_line_base); porters ofcurve.curve_p4/_p5must not port them twice.sm_ComputeConstantsstops at the first non-zero polygon angle with angle tolerance0(kept). -
curve.hyperbola: a NURB-less hyperbola (failedMakeNurb) segfaults upstream inGlobalPointSolveSTEP;curve.curve_p1's porter decides the port (SM_ERR). -
Nested polymorphic reads (
SmCurve::ReadFromDB-> projected/offset/iso curve ->SmCurve::ReadFromDB) have no depth limit upstream; the port bounds them atMAX_READ_NESTING= 64. -
curve.tangent_field:SmTangentFieldderives fromSmCrvOnSurf;SetAll,GetNaturalInterval,IsKindOf,operator==areCO-PORT(curve.crv_on_surf). -
nlib.srf_adv_p5: with rails and rational input the derivative curves share the derivative surfaces' knot arrays, so in-place knot writes reachder(ported withmake_compatible_knot_tol_traced).nlib.srf_adv_p1: the periodic wrap ofN_SrfEvalPtDerivsUnboundedcan loop forever (port:NotConverged);N_CreateSkinSpinereadspSpineParams[-1]for one profile with end derivatives. -
nurbs.b_spline_curve_p3:IsSegmentLinereturns a failed status as a "true"SmBoolean;AssertValidtest 4 checks the start pair, and its radius-of-curvature tests never fire.JoinWithmay degree-elevate the other curve in place. -
nlib.srf_adv_p4:N_ApproxCrossBoundaryDerivsignoresN_VectorsAngle's status and can refine with an uninitialised knot;N_MergeKnotVectorsloops forever on NaN knots; the Coons / skin-boundary routines modify their input curves (port returns errors for the UB paths, compares curves after the call). -
nurbs.b_spline_curve_n_lib:DampenKnotSpacingcan loop forever (port stops afterDAMPEN_SPLIT_LIMITsplits);CreateHelixSegmentlets through inputs that crashN_CrvApproxSpiral.nurbs.b_spline_surface_p1: the data constructor drops the v knots whens > r(kept); file readers bound header-declared sizes. -
nurbs.b_spline_curve_p1:SmBSplineCurve.hbindsEvaluate's defaultbNonZeroTangents = FALSE(SmCurve.hsaysTRUE), so the zero-tangent fix only acts when two or more derivatives are asked; curves built withSmBSplineCurve(lDim)have a NULL NURB that nearly every query dereferences (port:SM_ERR). -
nlib.crv_nurb:N_CrvGetMinMaxWeightsAndPtsalways returnspmax = 0; the twoN_CrvReversecodings (in place / new curve) agree only whenm = n + p + 1(both kept asreverse/reversed);N_CrvGetTypereports degree-1 non-line polylines as free-form. -
geom.point_set:SetUpTightPseudoBoxdiscards its plane projections (angles use raw points);SetUpCentroiddrops the last 50 points when the size is a multiple of 50, soGetCentroiddepends on call order (both kept). -
curve.hermite_curve:Evaluateleaves entrynum_derivsunset fornum_derivs >= 4(port and shim return zero there). -
nlib.crv_tool_p1: green sincenlib.crv_nurblanded (N_CrvGetMinMaxWeightsAndPtsalways returnspmax = 0);N_CrvRemoveKnotcan finish a removal and still return its error flag; the insert/split/refine routines do no input checks (port returnsInvalidInput). Knot-valued fixture parameters use{"knot": i}because serde_json can parse decimals 1 ulp off. -
geom.transform:EvaluateBoundingBoxSimpledereferences a nullpOptParamPseudoBoxwhen an output is requested (port:InvalidInput). -
nlib.basis_adv:N_BasisDerivsArraywalks spans forward without a bound (reads pastU[m]for an inner parameter at or past the domain end) and has no zero-denominator guard;N_BasisInsertKnotsreadsX[mx+1](port skips the no-op comparison). -
nlib.bezier: elevation/product matrices are built on a stack freed before return (dangling storage upstream);NL_PascalTriis[18][18](degree sums above 17 read past it; port returnsInvalidInput). -
geom.polar_box:Unionloops over an unset count afterReSet(UB; port returnsInvalidInput), andp->Union(t, *p)dropsp's own extent (kept). -
geom.local_solve1d: the interval-less constructor makes every Newton step a huge step against the Newton direction (kept); always pass an interval. -
geom.hash_table:SmObjsInVoxels::IncMaxItemCountleaves every bucket item pointing into the freed point-item array when a model with items grows (port uses indices; shim refuses it);FindCoincidentPairsonly pairs items with the bucket head and overwrites stored points through a reference (both kept). -
geom.attribute: several upstream undefined-behaviour paths (double delete, NULL dereference inSmMergeRegionAttribute::Merge, writes past "other" lists) returnInvalidInputin Rust and are skipped on both sides; shim liveness is predicted. -
geom.polynomial:SolveQuarticEqnreads an uninitialised cubic root when the tolerance is above 1 (port uses0.0; FFI rejects such inputs). -
geom.tol:SmRelationreturns pointer-hash order upstream; the port sorts, and the shim sorts the C++ results before comparing. The small-topology zone tolerance is 1000x larger, not smaller. -
geom.extent3d: upstreamContainsLineSeg3dORs its two end-point tests (kept). -
geom.tree: a point tree (SmTree(point, ...)) divides by zero inAddToSpatialTreeupstream (the ffi wrapper refuses it);SmObjectList::FindObjectreturns the last item as "previous" when not found (kept). -
topology.database_io: short binary reads are undefined behaviour upstream; the port returnsInvalidInputwith the same stream state. -
nlib.math: theN_SrfPowerBasisEvalPtback-edge intonlib.srf_basicwas removed; the Rust function takes the control net directly.
Style review batch #1 (2026-10-12)
Reviewed geom.vector2d, geom.extent3d, topology.database_io, nlib.math,
geom.tol and geom.tree against AGENTS.md. Numerics and operation order were not
changed, and every unit re-ran difftest with unchanged reports.
geom.tree: the neighbour searches (left/right/bot/top_neighbors,neighbors) now returnResult<Vec<NodeId>>instead of filling a caller out-param. The fourGetXNeighborsrecursions and the fourGetXSidebodies are one direction-generic loop and one recursion. Cyclic parent/child links now returnInvalidInputinstead of looping forever or overflowing the stack. This applies tofind_node,propagate_to_parents,rm_from_spatial_tree,first_bezier_patchand the neighbour searches.geom.extent3d:approx_axisnames its near-centre tests (same comparisons).geom.tol: removed alet _ = small;.topology.database_io: erroroperationpaths now includeDatabaseReader::.- No change was needed in
geom.vector2dornlib.math.nlib.mathkeeps a justified module-levelallow(clippy::if_not_else)(a pedantic lint, not a denied one). - Left for
geom.vector3d(claimed):sm_sqrt,sm_asin,sm_acos,SmVector3d::GetMaxDimension/DistanceBetweenand thesmos_Max/Minfamily (SmMath.h) are duplicated as privateCO-PORT(geom.vector3d)helpers ingeom/vector2d.rs,geom/extent3d.rs,geom/tol.rsandgeom/tree.rs. When that unit lands, replace them with the shared versions.grep -rn "CO-PORT(geom.vector3d)"finds every site.
Style review batch #2 (2026-10-12)
Reviewed geom.periodic_extent1d, geom.matrix, geom.assert_array, nlib.geom,
geom.vector3d and geom.context against AGENTS.md. Numerics and operation order were
not changed; every touched unit re-ran difftest with the same counts and
max_rel_err = 0 (also at --seeds 4000), plus fmt, clippy (with and without cpp),
tests and the wasm32 check.
geom.vector3d: the lastCO-PORT(geom.vector3d)copies (sm_sqrtingeom/matrix.rs;sm_sqrt,DistanceBetween,GetMaxDimensioningeom/tree.rs) now callvector3d::sqrt,Vec3::distanceandVec3::max_dimension.sqrtlost a branch that duplicated its result;length/closer_thanreuselength_squared/distance_squared.geom.matrix:is_symmetricis one short-circuiting iterator chain.geom.periodic_extent1d:add_interval's integercase_idwith a dead_arm is a privateSeamCaseenum;complement/set_min_maxreuseset_full; error paths namePeriodicExtent1d::.geom.context:Context::newbuilds its fields directly;MarkLockerrors name the failing method; the header listsMarkLock::context2.geom.assert_array:AssertReportequality is one&&chain.nlib.geom: API changetransform_matrix_from_axes(from: Frame, to: Frame)replaces eight positionalVec3s (newnurbs::geom::Frame); the clamps invectors_cos_angle/vectors_angleare single expressions;ColinearDim::flagisconst.
Style review batch #3 (2026-10-12)
Reviewed geom.extent1d, geom.attribute, geom.polynomial, geom.axis2_placement,
nlib.srf_basic and geom.extent2d against AGENTS.md. Numerics and operation order
were not changed. Every touched unit (also geom.extent3d, geom.tol,
geom.geom_utility_p2, geom.matrix, geom.vector2d, geom.vector3d) re-ran difftest
with unchanged reports and max_rel_err = 0 (also at --seeds 4000), plus fmt, clippy
(with and without cpp), tests and the wasm32 check.
- Shared
SmMath.hhelpers.math::{max2, min2, max3, min3, max4}(smos_Maxfamily) andgeom::tol::{is_zero, is_zero_squared, is_zero_to_tol, are_same, are_same_to_tol}(SM_IS_ZERO*,SM_ARE_SAME*) replace 18 private copies inextent1d,extent2d,extent3d,tol,geom_utility/p2,matrix,vector2d,vector3dandaxis2_placement. New code should import these instead of adding privatesmax/is_zerohelpers. geom.extent2d/geom.extent3d: the privatelerp_exact/classify_coordcopies callExtent1d::evaluate/classify_point; extent2d error paths nameExtent2d::.geom.axis2_placement: API changeAxis2Placement::from_to(from: &Self, to: &Self)replaces six positional vectors (build each withAxis2Placement::new).nlib.srf_basic: API changeSurface::with_indices(net, u, v, idx),set_size_indices(idx)andsize_arrays(slot, idx)take a newSurfaceIndices { n, m, p, q, r, s }(getterSurface::indices), removing anallow(clippy::too_many_arguments);is_flat_cheapusescombine2_pts/Vec3::lengthinstead of privateN_VectorCombine/N_VectorMagnitudecopies;Direction::flag/from_flagareconst.geom.attribute:region_type::{SOLID, VOID, INFINITE}replace the 0/1/2 literals; the two accumulation loops ofSmMergeRegionAttribute::Mergeare oneappend_new_regions;set_regionsbounds-checks once.- No change was needed in
geom.polynomial.
Style review batch #4 (2026-10-12)
Reviewed geom.geom_utility_p2, nlib.basis_basic, geom.hash_table, geom.object,
geom.local_solve_nd and geom.local_solve1d against AGENTS.md. Numerics and operation
order were not changed; every unit re-ran difftest with unchanged counts and
max_rel_err = 0 (geom.local_solve1d and geom.geom_utility_p2 also at
--seeds 4000, geom.local_solve1d at --seeds 200000), plus fmt, clippy (with and
without cpp), tests and the wasm32 check.
geom.geom_utility_p2: API changesegment_segment_perspective_intersect(.., view: &PerspectivePlane { point, unit_normal, eye }, tolerance)andline_coplanar_ellipse_intersect(line_pnt, line_vec, ellipse: &CoplanarEllipse { center, x_axis, x_radius, y_axis, y_radius }, tolerance).inverse_4x4builds each cofactor minor in a loop (minor_3x3) instead of 16 hand-unrolled nine-argument calls; the privatedeterminant_3x3stub now takes&[[f64; 3]; 3]. Noallow(clippy::too_many_arguments)is left in the file.geom.local_solve1d:eval_intoevaluates into anIterationValue(not/f/f'out-params);SolveByBrenthas one bisection fallback instead of three copies; the Brent min/max checks ofSolveItand the bracket-end choice ofSolveOscillatingare one branch each (same comparisons); newSolverAlgorithm::is_brent; a discardedcontains_valuecall was removed;solve_by_bisectiontakesmutparameters.nlib.basis_basic: operation paths inlined (noOP_*indirection);Errors:prose became# Errorssections.geom.hash_table,geom.local_solve_nd:# Errorssections on every fallible public fn. No other change was needed there or ingeom.object.- Outside the batch:
nurbs::geom's header used a redundant explicit intra-doc link that failedcargo docwith-D warnings(nlib.geomdifftest re-run, unchanged).
Style review batch #5 (2026-10-12)
Reviewed geom.polar_box, nlib.bezier, nlib.funcs_basic, nlib.basis_adv,
geom.grid and geom.geom_utility_p1 against AGENTS.md. Numerics and operation order
were not changed; every unit re-ran difftest with unchanged counts and
max_rel_err = 0 (geom.grid still 613 pass + 2 deferred), plus fmt, clippy, tests
and the wasm32 check. No public API changed.
- Shared status codes.
error::{SM_ERR, SM_ERR_WARNING}andSolidError::sm_err(operation)replace the private copies inpolar_box,gridandgeom_utility/p1. Twelve other files still carryconst SM_ERR: i32 = 1001and/or a privatefn sm_err(grep -rn "const SM_ERR\|fn sm_err" crates/solidcore/src); new code should import the shared ones, and a later pass can migrate the rest. nlib.bezier:N_BezGetDegreeElevationMatrixandN_BezFuncMultiplyBezCrvMatrixrun identical loops; both call one privateband_matrix_into.given_or_localreplaces the hand-rolled null-matrix fallbacks;reduce_degreeshares its left/right loops between odd and even degrees.geom.grid:Setup3DDDA's three per-axis blocks are one privateAxisStep::new;GetVoxelAddress's outside test is one expression.geom.polar_box:PolarBoxState::is_unknown(uninitialised or unbounded) servesCross's early exits andcompute_polar_coord; redundant resets dropped.geom.geom_utility_p1: checks that return the same error are one||chain;PointSetType::codereads the discriminant.nlib.funcs_basic,nlib.basis_adv: removedallow(clippy::too_many_arguments / too_many_lines)(allowed workspace-wide);CurveFunction::compacttruncates in place.
Style review batch #6 (2026-10-12)
Reviewed geom.solution_array, nlib.crv_basic, geom.tools, geom.transform,
geom.global_solver and geom.pseudo_box against AGENTS.md. Numerics and operation
order were not changed; every unit (and nlib.crv_tool_p1, merged meanwhile) re-ran
difftest with unchanged counts and max_rel_err = 0 (geom.solution_array,
nlib.crv_basic, geom.global_solver, geom.tools also at --seeds 4000), plus fmt,
clippy (with and without cpp), tests, cargo doc -D warnings and the wasm32 check.
geom.solution_array: API changeSolution::classify_solution_endreturns a newEndClassenum (BelowStart,AtStart,Within,AtEnd,AboveEnd,Inconsistent;code()gives upstream's -2..3) instead of ani64.# Errorssections on every fallible public fn;remove_atusessaturating_sub.geom.global_solver: API changeGlobalSolve::solve_it(&SolveRequest { operation, solution_requested, tolerance_3d, best_answer_so_far_sq, opt_vectors }, solutions)replaces five positional arguments (SolveRequest::default()is Intersect / All / 0 /SM_BIG_DOUBLE/ no vectors).sm_CombineMinMaxmatches onEndClass;FindBestSubdivisionNodereturns its swap flag directly; the perspective radius loop iterates the four offsets;# Errorssections.geom.transform,geom.pseudo_box,geom.global_solver: the privateSM_ERRconsts /sm_errfns are replaced byerror::SM_ERR/SolidError::sm_err. Removedtransform's unusedVOLUME_TYPEconst and itslet _placeholders. Twelve files still carry a private copy (grep -rn "const SM_ERR\|fn sm_err" crates/solidcore/src).geom.tools: theCurveMaxDistanceBetweenstub takes the sample count as a parameter (nolet _ = COINCIDENCE_SAMPLES); the edge range is one expression.nlib.crv_basic:OP_*operation paths inlined; "Errors with" prose became# Errorssections;equalsis one short-circuiting chain;is_planarskips non-normalisable corners withlet ... else(same sums and comparisons).
Style review batch #7 (2026-10-12)
Reviewed nlib.iges, nlib.srf_tool_p1, nlib.crv_tool_p1, nlib.crv_fit_p1,
curve.hermite_curve and geom.a_object against AGENTS.md. Numerics and operation
order were not changed; every unit re-ran difftest at --seeds 2000 before and after
merging main with unchanged counts and max_rel_err = 0 (nlib.crv_tool_p1 is now
green with 2143 cases since nlib.crv_nurb landed), plus fmt, clippy (with cpp),
tests and the wasm32 check.
nlib.iges: API changeiges_126_curve(&Entity126 { k, m, t, w, pts, v })andiges_128_surface(&Entity128 { k1, k2, m1, m2, s, t, w, pts, u, v })replace six / ten positional arguments (the IGES entity data as named fields).nlib.srf_tool_p1: API changeextract_patch(sur, rect: Rectangle)replaces four corner floats (the rectangle is also what the result is reparametrised to);OP_*paths inlined;KnotVectorimported;span_ul/span_ur/span_vl/span_vrinstead ofsul/sur_/...nlib.crv_tool_p1,nlib.crv_fit_p1:OP_*operation paths inlined into each routine's localOP.curve.hermite_curve:mut pseudo_boxparameter instead of a rebinding; direct dimension test inapprox_eq.geom.a_object: API change theboolean_opconst module became theBooleanOperationenum (allSmBooleanOperationTypevalues,from_code); region propagation matches on it, the DIFFERENCE/SLICE/... comment now sits on the default arm.SolidError::sm_errinstead of a hand-builtUpstream;assert_validis one expression;Attributeimported.- Not changed (judged faithful structure, not transliteration): the u/v twin loops of
the surface tools,
notify's seven positional arguments (same shape asgeom::object::notify), the rational weight check ofremove_knot.
Style review batch #8 (2026-10-12)
Reviewed geom.point_set, nlib.funcs_adv, nlib.crv_nurb, nlib.srf_fit,
nlib.crv_fit_p2 and topology.topology against AGENTS.md. Numerics and operation
order were not changed; every touched unit (also nlib.crv_fit_p1, and
nlib.crv_geom / nlib.crv_tool_p2 after merging main) re-ran difftest with
unchanged reports, counts and max_rel_err = 0, plus fmt, clippy (with and without
cpp), tests and the wasm32 check. No public signature changed.
- Shared NLib error constructor.
SolidError::nlib(operation, code)replaces the privatefn nlibin the four NLib units of the batch (andcrv_fit::p1). Thirteen older files still carry their own copy (grep -rn "^fn nlib(" crates/solidcore/src); new code should callSolidError::nlib. - Shared signed-index helpers. Two crate-private modules hold the upstream
NL_INDEXarray access the ports keep:nurbs::index(isize:get,set,get2,set2,storage,sx,ux,oob; used byfuncs_adv,crv_nurb) andnurbs::fit_arrays(i64:at,put,at2,put2,alloc,alloc2,Block,mget/msetforRM[i][j],banded,full,basis,copy_knots,curve, ...; used bycrv_fit::p1,crv_fit::p2,srf_fit), removing about 450 lines of copies.crv_tool/p1.rs,srf_tool/p1.rsandiges.rsstill have privateisizecopies that can move tonurbs::index; new NLib ports should import these modules instead of copying helpers. nlib.crv_nurb:pascalreturns the triangle (no&mutout-param),get_tri/set_trifolded into the genericget2/set2;greville_abscissareads one checked knot window;OP_*paths inlined (eval_tangentandreversednow name themselves).nlib.funcs_adv,nlib.crv_fit_p2,nlib.srf_fit:OP_*paths inlined;crv_fit_p2dropped twoallow(clippy::too_many_lines)the workspace grants.nlib.srf_fit: theindex/start/endbookkeeping shared by the u and v passes oflst_sq_approxis one privateBandvalue.geom.point_set:unit_orthoreturns the new[x, y, z]axes instead of writing three&mutout-params; the containment count matches on ranges.topology.topology:Notify's two per-target propagation blocks are onepropagatehelper (the copy mark reset still applies only topOther1); the record lookups share one error;stop_trackinglost a dead store.- Not changed (judged faithful structure):
topology::notify's eight positional arguments (same shape asa_object::notify), the per-direction loops ofcrv_fit::p2::approx_lst_sq(upstream's second copy of the band bookkeeping differs fromsrf_fit's in its zeroing),point_set's eight min/max angle updates.
Style review batch #9 (2026-10-12)
Reviewed nurbs.b_spline_curve_p1, nlib.srf_nurb, nlib.crv_adv_p2,
nlib.srf_tool_p2, nlib.crv_offset and nurbs.nurbs_srf against AGENTS.md (earlier
in the day nlib.crv_geom and nlib.crv_tool_p2 moved to nurbs::index /
SolidError::nlib, commits ad631e4, 3c02e52). Numerics and operation order were
not changed; every touched unit (also nurbs.nurbs_crv, and nlib.crv_approx after
merging main) re-ran difftest with unchanged reports at the default seeds and a
2000-seed run, all max_rel_err = 0, plus fmt, clippy, tests and the wasm32 check.
- Shared
TO_EUCLID.nurbs::nurbs_crv::to_euclid(crate-private; divides byw, unlikeCPoint::to_euclid, which multiplies by the reciprocal asN_CPtToPtEuclid) replaces the copies inb_spline_curve/p1.rs,nurbs_srf.rsand the inline one incrv_eval. - Public API change:
BSplineCurve::edit_parameterization(&Extent1d)lost its unusednotifyflag (notifications are omitted per the module deviations). The difftest runner still passesnotifyto the C++ side. SolidError::nlib/SolidError::sm_errreplace the private forwarding fns insrf_nurb,crv_adv/p2,srf_tool/p2andb_spline_curve/p1;nurbs_srfusesnurbs::index::oob.NL_MAXismath::max2insrf_tool/p2andcrv_offset.crv_adv/p2:OP_*paths inlined; the end-constraint test ofremove_knots_deriv_constraintsis one boolean expression.crv_offset: one sample-count error constructor.nurbs_srfdropped anif_not_elseallow that no longer applies.- Not changed (judged faithful or out of scope):
srf_tool/p2keeps importingsrf_tool/p1's privateVec<Vec<_>>index helpers (at2/put2have nonurbs::indexcounterpart for ragged nets);b_spline_curve/p1keeps its module-levelOP_*constants (several are shared by more than one method);TypeFinder's labelled-section state machine insrf_nurb.
Style review batch #10 (2026-10-12)
Reviewed nlib.crv_approx, nlib.srf_tool_p3, nlib.srf_adv_p3, nlib.srf_tool_p4,
nurbs.b_spline_surface_p1 and nurbs.b_spline_curve_n_lib against AGENTS.md (merge
b458c36). Numerics and operation order were not changed; every touched unit (also
nurbs.b_spline_curve_p1, nurbs.nurbs_crv, nlib.crv_tool_p2, nlib.crv_nurb and,
after merging main, nlib.srf_adv_p2) re-ran difftest with unchanged reports and
status, plus fmt, clippy, tests and the wasm32 check.
SolidError::nl_ser(op)(crate-private): upstreamSER(SM_ERR)after a failed NLib call, lettingUnportedthrough. Replaces the privatenl_errcopies inb_spline_curve/p1.rs,b_spline_surface/p1.rsandb_spline_curve_n_lib.rs; use it as.map_err(SolidError::nl_ser(OP))in newSm*ports.- Shared
NL_BIGD:nurbs::math::BIGDispub(crate); the private copies innurbs_crv,crv_tool/p2,crv_nurbandsrf_tool/p3are gone. b_spline_surface/p1usesnurbs_crv::to_euclid(same branches and divisions) and listsknot_type/is_nurb_borrowedin its header table; it andb_spline_curve_n_libcallSolidError::sm_errdirectly.b_spline_curve_n_lib: oneextension_continuitymapping (SM_G1/G1G2/CINFINITY->NL_*) for bothCreateExtendedCurveoverloads.srf_adv/p3:SolidError::nlibinstead of a private wrapper;MAX_EXTEND_DEPTHin the header table.crv_approx:math::max2forNL_MAX,f64 * Vec3forN_VectorScale(same products).- Not changed (judged faithful or out of scope):
srf_adv/p3's localg2/s2/netragged-net helpers (same shape assrf_tool/p1'sat2/put2, which arepub(super));n_lib'sknot_at/cpt_at/vec_at(different error reasons);BSplineCurve::approximate_arc's seven arguments (upstream signature, all required);make_unit_ortho_into's out-params (upstream's partial writes on failure).
Style review batch #11 (2026-10-12)
Reviewed nlib.srf_adv_p1, nlib.srf_adv_p2, nlib.srf_adv_p4, nlib.srf_adv_p5,
nurbs.b_spline_curve_p2 and nurbs.b_spline_curve_p3 against AGENTS.md (merge
6472fe3). Numerics and operation order were not changed; every touched unit (also
nlib.srf_adv_p3, nurbs.b_spline_curve_p1 and, after merging main, nlib.srf_offset)
re-ran difftest with unchanged counts and status, plus fmt, clippy, tests and the
wasm32 check.
nurbs::index::{at, put, at2, put2}: bounds-checkedusizeaccess (a[i],a[i][j]ofVecrows) next to the signedget/set.srf_adv/p1andp4use them andindex::oobinstead of private copies;p1,p2,p4callSolidError::nlibdirectly;p5'soobdelegates toindex::oob.- One
KN_TOL:srf_adv/p2's duplicateKNOT_SNAP_TOLis gone; it usessrf_adv::p3::KNOT_TOL(same value, same upstream constant). srf_adv/p5: thecolinearalias is gone (callscpts_are_colinear);srf_adv/p4listsMat3in its header table;srf_adv/p1drops a redundant#[allow(clippy::too_many_lines)](allowed workspace-wide).b_spline_curve/p2: errors fromcreate_analytic_curve,has_repeated_end_control_points,is_line,number_of_unique_knotsandmatch_weightscarry their own operation path instead of the sharedOP_NURB; the privatenurb_p2/nurb_p2_mutcopies are replaced byp1'snurb_ref/nurb_ref_mut(pub(super)).- Not changed (judged faithful or out of scope):
p5's op-lessat/putwrappers (one operation per file); the upstream-named fields ofSkinBoundaryOptions(der,cmp,cnt, ...; FFI/difftest API); the per-fileinvalidhelpers ofb_spline_curve/p1..p3; theAnalyticCurveempty enum (untilcurve.line/curve.ellipse_p1land).
Style review batch #12 (2026-10-12)
Workflow batch #4: reviewed nlib.srf_offset, nlib.srf_adv_p6, curve.curve_p2,
curve.conic and nurbs.b_spline_surface_n_lib_p1 against AGENTS.md (merge
bd22019). curve.parabola was still claimed (not merged) and is left for a later
batch. Numerics and operation order were not changed; every touched unit (also
nurbs.b_spline_surface_p1, topology.database_io and, after merging main,
curve.hyperbola and curve.projected_curve) re-ran difftest with unchanged counts and
status, plus fmt, clippy, tests and the wasm32 check.
error::SM_ERR_NULL_POINTER+SolidError::null_pointer(op)(upstreamNERon aNULLpointer): replaces four private copies of the1006constant and thener/no_stream/ inlineUpstream { code: SM_ERR_NULL_POINTER, .. }builders inb_spline_surface_n_lib/p1,b_spline_surface/p1,curve/curve/p2andtopology/database_io(whosepub constis gone: import it fromcrate::error).srf_adv/p6:nurbs::index::{at, put, oob}andSolidError::nlibinstead of privateoob/nlib/atcopies;*mt(v, i, op)? = xwrites areput(v, i, x, op)?, and the rescaled derivative curves are rescaled through one slot borrow.srf_offset:offset_patch_netis aSurfacemethod (was an associated fn taking the patch);UNIT_SQUARElisted in the header table.conic:Conic::TYPEiscurve::curve::p2::CONIC_TYPE(no privateCURV_BASE_TYPE); error operation paths name the methods (curve::conic::Conic::evaluate, ...).curve/curve/p2:stack_lookdestructureslast_chunk::<7>()instead of indexing with anunwrap_or(0.0)fallback.- Not changed (judged faithful or out of scope):
b_spline_surface_n_lib/p1's identicalbuild_nlib_curve/create_nlib_curve(two upstream statics) and itsinvalidhelper;srf_adv/p6'srf/mtreference accessors (no shared equivalent yet);curve/curve/p2'su64 as usizemultiplicities of Hermite/conic knots; privateCURV_BASE_TYPEcopies inhermite_curve,hyperbolaandb_spline_curve/p1(usecurve::curve::p2::*_TYPEnext time those files are touched).
Style review batch #13 (2026-10-12)
Workflow batch #5: reviewed curve.tangent_field, curve.hyperbola,
curve.projected_curve, curve.curve_cache, curve.iso_curve and curve.offset_curve
against AGENTS.md (merges 16559c3, a0b635d). Numerics and operation order were not
changed. Every touched unit (also curve.hermite_curve, nurbs.b_spline_curve_p1,
curve.curve_p2, nurbs.b_spline_surface_n_lib_p2) re-ran difftest with unchanged
counts and status, except curve.iso_curve (see below), plus fmt, clippy, tests and
the wasm32 check.
- Back-edge wired:
curve.iso_curve'sUNPORTED(nurbs.b_spline_surface_n_lib_p2)stub forSmBSplineSurface::Evaluatenow callsBSplineSurface::evaluate(EvaluateFlags, rows flattened). Deferred dropped from 640 to 285, still partial. - Type codes:
Hyperbola::TYPE,HermiteCurve::TYPEandBSplineCurve::TYPEusecurve::curve::p2::{HYPERBOLA,HERMITE_CURVE,BSPLINE_CURVE}_TYPE; the hyperbola's owner-surface codes come fromgeom::assert_array::labels. No privateCURV_BASE_TYPEremains outsidecurve::curve::p2. - Shared helpers:
curve::curve::p2::at(an evaluation entry orUNDEF) ispub(crate)and replaces copies incurve_cache,hyperbolaandtangent_field(crv_in_volumestill has one);curve_cacheusesSolidError::nl_serinstead of its privategw_ser;offset_curvebuilds its null-base error withSolidError::null_pointer. - Enum codes:
SurfParamType::from_codeandProjectionType::from_code(returningOption) replace the ad hoc matches iniso_curveandprojected_curve. - Smaller fixes:
curve_cachegets amax_stephelper (was computed twice) and anOP_PARENToperation constant; redundant#[allow(too_many_arguments / too_many_lines)]attributes were removed (both lints are allowed workspace-wide); fully qualified paths became imports. - Not changed (judged faithful or out of scope):
offset_curve's rawSmBooleani32flags and no-opset_context;tangent_field's generic surfaceS;hyperbola'sglobal_point_solve_stepunused arguments (consumed oncecurve.curve_p1lands); the privateignored/ignore_statuscopies incurve_cache,hyperbola,curve::curve::p2,b_spline_curve/p3andgeom_utility/p2. A sharederrorhelper would remove them, but that touches units outside this batch.
Style review batch #14 (2026-10-12)
Workflow batch #6: reviewed nurbs.b_spline_surface_n_lib_p2, curve.crv_in_volume,
curve.line, nurbs.b_spline_surface_p2, curve.ellipse_p2 and surface.volume_p1
against AGENTS.md (merges 4be3702 and its follow-up). Numerics and operation order were
not changed. Every touched unit re-ran difftest with unchanged counts and status: the six
above plus geom.solution_array, curve.curve_p2, curve.hyperbola,
curve.curve_cache, nurbs.b_spline_curve_p3 (partial) and geom.geom_utility_p2,
geom.local_solve1d (green). fmt, clippy, tests and the wasm32 check also passed.
- Shared error helpers in
crate::error:SolidError::invalid_input,SolidError::unported,SolidError::is_unported, and the crate-levelignored(Result<Option<T>>, failure ->None) /ignore_statusfor calls whose status upstream ignores (Unportedstill propagates). These replace the privatebad/invalid/unported/is_unported/keep_unported/or_ignoredcopies in this batch's units and theignored/ignore_status/ignore_failurecopies incurve_cache,hyperbola,curve::curve::p2,b_spline_curve/p3,geom_utility/p2andlocal_solve1d. That was the follow-up batch #13 asked for. nurbs.b_spline_surface_n_lib_p2: the privateSM_ERR_NULL_POINTER/nerbecameSolidError::null_pointer, and itsgetbecamenurbs::index::at.curve.crv_in_volume: dropped the leftover pass-through wrappers from the stub era (volume_natural_param_domain,volume_evaluate,volume_map,volume_map_point,volume_transform,volume_eq). It now callsVolumedirectly; only thesurface.volume_p2andintersect.curve_class_*stubs remain. It also usescurve::curve::p2::at, and the stale "unportedSmVolume::operator==" docs were fixed.- Type codes:
surface::volume::p1's*_VOLUME_TYPEconstants re-exportgeom::assert_array::labels.curve.lineandcurve.ellipse_p2takeSURF_OF_*_TYPE/POLAR_CONVERSION_TYPEfromlabels, andnurbs.b_spline_surface_p2takesCRV_ON_SURF_TYPEfromcurve::curve::p2. No privateCURV_BASE_TYPEremains. Solution::new/zeroed/Defaultnow need onlyO: Copy, socurve.lineandcurve.ellipse_p2no longer keep their ownnew_solution.- Smaller fixes:
Line::join_with'sother_is_lineno longer makes a deadis_line()call, andVolume::copy_fromusesignoredinstead of a three-arm match. - Not changed (judged faithful or out of scope):
Volume::trim_param_bounding_box_simpleandhas_discontinuities_simplekeep upstream's in/out argument, because a disjoint box leaves the caller's value andTransformshares that signature. The per-fileOP_*operation-path constants and theErr(SolidError::Unported { .. })struct literals in other units still work and were left alone. The many other privatefn invalid(op, reason)copies undernurbs/can switch toSolidError::invalid_inputin a later batch.
Style review batch #15 (2026-10-13)
Workflow batch #7: reviewed curve.curve_p4, surface.twist_volume,
surface.b_spline_volume, curve.curve_p3, curve.curve_p1 and curve.curve_p6
against AGENTS.md (commits 90d5180, 38d50f3). Numerics and operation order were not
changed. difftest counts and status are unchanged: p1 3083 (590 deferred), p3 3076 (742),
p4 3259 (74), p6 3136 (88), twist_volume 1572 (3), volume_p1 2886 (39) partial;
b_spline_volume 2333 green; all max_rel_err 0. fmt, clippy, tests and wasm32 pass.
- Shared helpers:
curve::curve::{p1, p6}dropped theirkeepaliases oferror::ignored;p1/p3dropped privateinvalidforSolidError::invalid_input;p3/p4dropped privateis_unportedfor the method;p4'spoint_or_undef,eval_intoandreverse_ignoreduseignored/ignore_statusinstead of hand-written three-arm matches. - One
eval_n:curve::curve::p3::eval_n(pub(super)) replacesp1'seval_pvandp4's owneval_n(which wasp3's withfrom_left = true). - Type codes:
p4takesEDGE_TYPE,EDGEUSE_TYPE,FACE_TYPE,SURF_OF_*_TYPEfromgeom::assert_array::labels(its local copies are gone). - Lints: removed
#[allow(clippy::too_many_arguments / too_many_lines)]inp4/p6; both are allowed workspace-wide.p4'sCurvePropertyEfo::evaluatetakes_signal_errorsinstead oflet _ =. surface.twist_volume:members_eqno longer computes the dead period comparison (upstream overwrites it; documented);read_from_dbtakes_version.- Judged fine and left alone:
surface.b_spline_volume(theUSE_VOLUMES#elsebranches are ported as always-SM_ERRstubs with documented signatures),twist_volume'sOption<&mut _>box outputs (documented deviation), the per-fileOP_*constants.curve::curve::p2still has localEDGE_TYPE/EDGEUSE_TYPEcopies (outside this batch), and the privatefn invalidhelpers undernurbs/remain.
Style review batch #16 (2026-10-13)
Workflow batch #8: reviewed curve.curve_p7, curve.crv_on_surf, curve.ellipse_p1,
curve.curve_p8, primitive.api_curves and curve.composite_curve_p1 against AGENTS.md
(commits bff8a69, e06dbd7, merged in 1d80f47). Numerics and operation order were
not changed. difftest counts and status are unchanged: curve_p7 3027 (1311 deferred),
curve_p8 3058 (1090), ellipse_p1 2028 (132), composite_curve_p1 3093 (312), crv_on_surf
2094 (541) partial; api_curves 1629, parabola 3044, geom.tol 3440, geom_utility_p1 4185,
geom_utility_p2 3270 green; all max_rel_err 0. fmt, clippy, tests and wasm32 pass.
- Shared
SmMath.hhelpers:math::rad_to_deg(SM_RAD2DEG) andmath::min4(smos_4Min) replace private copies incurve::curve::{p7, p8},geom::tolandgeom::geom_utility::{p1, p2}. - Shared crate helpers:
curve::curve::p3::set_param,curve::curve::p4::point_or_undefandgeom::vector3d::is_valid_doublearepub(crate); the copies inp7,p8andprimitive::api_curvesare gone.curve::curve::p2::atreplaces.get(i).copied().unwrap_or(Vec3::UNDEF)inp7,p8andcomposite_curve::p1;ellipse::p1reads its polar tables withnurbs::index::at(privatetablegone). - Type codes:
curve::curve::p2::CURV_BASE_TYPEispub(crate);composite_curve::p1andparaboladropped their private copies (Parabola::TYPEisPARABOLA_TYPE). - Unported propagation:
ellipse::p1::read_from_dbandp7's line/line guess point useerror::ignoredinstead of hand-written three-arm matches. - Dead code:
Ellipse::convert_to_2dno longer computes the unusedtheta_y_rad(upstream computes it and uses ThetaX for both vectors; noted in a comment);join_withpicks the common/end points directly instead of swapping and discarding. - Lints: removed
#[allow(clippy::too_many_arguments / too_many_lines)]inp7,p8andcomposite_curve::p1(allowed workspace-wide). - Judged fine and left alone:
crv_on_surf(back-edge stubs carryUNPORTEDtags, lift formulas keep upstream order),api_curves'#[allow(clippy::neg_cmp_op_on_partial_ord)](the negated test rejects NaN, as upstream), the remaining three-arm matches whose failure arm returns orcontinues, andlet _ = extent.set_min_max(..)(neverUnported).curve::hyperbolaandellipse::p1still each have a privateextract(is >> double); a sharedInputStreammethod would remove both.
Resume note (2026-10-13, after style review batch #16)
mainis pushed toorigin(github.com/bherbruck/solidcore). Counts: 71 green, 35 partial, 3 claimed (curve.curve_p5,curve.composite_curve_p2,surface.curve_bounded_surface), 152 todo, 0 blocked. Ready set:surface.surface_cache_p1.- Worktrees branched before batch #16: private
rad_to_deg,min4,set_param,point_or_undef,is_valid_doubleandCURV_BASE_TYPEcopies are gone; usecrate::math::{rad_to_deg, min4},curve::curve::p3::set_param,curve::curve::p4::point_or_undef,geom::vector3d::is_valid_doubleandcurve::curve::p2::CURV_BASE_TYPE. - Next style review batch:
surface.surface_p2andcurve.parabola; alsocurve::curve::p2's localEDGE_TYPE/EDGEUSE_TYPE, thenurbs/fn invalidcopies and the two privateextracthelpers.
Known problems
- Long dependency chain. There are 65 layers, a 116,850-line critical path, and one
220-unit cycle broken by 800 dropped back-edges. A unit with back-edges can only be fully
difftested once their targets are green (
plan/order.md). - Nondeterministic upstream paths.
srand(time(NULL))inSmFace::GetPointsInFace(more than one point) and inSmShell.cpp. Pointer-ordered maps at 4 iteration sites. Process-wide tolerances, and statics set by the first call. Seeplan/closure.mdandplan/findings.md. - Large debug library.
libSMLib.ais about 296 MB because it is built with-g. Usebuild.sh --no-tests, or drop-g, if disk gets tight. - prog_test granularity. prog_test reports pass/fail per suite only. Its log names and timing lines depend on the clock, so strip them before diffing.
- Approximate call graph. Override matching (by signature) and template matching (by
name) are approximate, so unit closures may over- or under-include a few functions.
query.polyandquery.tessare included only through over-broad virtual-call resolution; they are low priority. - NaN/inf in difftest inputs. JSON can't represent NaN or inf, so non-finite inputs are covered by Rust unit tests only.
- Misleading
seedsfield. The difftest report'sseedsfield shows the CLI value, not the number of cases a runner actually generated. - Self-tagged iso curve stubs.
surface/surface_cache/p1.rskeepsUNPORTED(surface.surface_cache_p1)stubs forSmPlane::CreateIsoParametricCurveandSmSurfOfRevolution::CreateIsoParametricCurve, although that unit and the units that ported both overrides (surface.plane_p1,surface.surf_of_revolution_p1) are partial. The cache stores iso curves asBSplineCurve, so theSmLine/SmCircleresults cannot be stored. Whoever changesSurfaceCacheiso curve storage toCurvemust wire them (grep the tag). No difftest case reaches them today.
Wave 0 verification (independent check, 2026-10-12)
| Check | Result |
|---|---|
| Licence | PASS. LICENSE is byte-identical to upstream; NOTICE and ATTRIBUTION.md are present and correct. No tracked file comes from OCCT, USD, Hydra, SmPy or proposals. The CMake build and the call-graph extractor read only source/SMLib, source/SM_API and tests/*. Fixtures cite only tests/prog_test. Crate names carry no NVIDIA or SMLib branding. The README now says "a Rust port of NVIDIA's SMLib". |
| Gate | PASS. A clean --green run in a fresh target dir gave nlib.knots 2953/2953, max_rel_err 0. The C++ side really is upstream: N_BasisEval and the other upstream symbols are linked into the binary. REL_TOL is 1e-9 and there are no abs floors. Seeds are fixed (fnv1a(unit)+i). Two runs produced byte-identical reports, also identical to the committed report. --perturb caught 861/861 perturbed cases. |
| Rules | PASS. fmt, clippy -D warnings (with and without cpp), the wasm32 check and the tests are all clean. The library code has no unsafe (forbid), and its only unwraps are in tests. It has no statics, clock or HashMap. Kernel: Send is asserted. |
| Plan | PASS. 260 unique ids, all fields present, no dangling or self deps, no cycles. Layers equal the longest path, and every upstream path exists under SMLib/SM_API. The ready set (extent3d, vector2d, nlib.math, database_io) matches the table above. |
Open issues
geom.vector2d/geom.vector3dvsmath.rs. The plan puts these units atgeom::vector2d/geom::vector3d, butcrates/solidcore/src/math.rsalready definesVec2/Vec3, mapped toSmVector2d/SmVector3d. The agent porting those units should extendmath.rs, or re-export from it, rather than create duplicate types, and updaterust_moduleto match. Resolved forVec2:geom.vector2dmovedVec2/Point2intogeom::vector2dandmathre-exports them;geom.vector3dcan do the same forVec3.
Surface tessellation and iso boundaries (2026-10-14, PR #6 review)
surface.surface_p9 is partial: 778 cases (178 fixtures and 600 generated),
727 bit-identical, zero failed and 51 deferred (intersect.curve_class_p4: 50;
surface.surface_p6: 1), with no absolute floors. All 96 review regressions
and all 16 explicit analytic-plane cases without diagonals pass. The regression
corpus reproduced 32 failures before the degeneracy/max-side fixes.
The solver uses the existing surface_p1 CsiGlobalSolver and constructor;
surface/node/point helpers call the ported p1 implementations. The disjoint CSI
fixture now passes; crossing reaches surface.surface_p6. End-to-end CSI is
still unverified. FindTessellationSplits is wired into block subdivision:
cache p2 compares 808/905 cases (97 deferred), cache p3 450/600 (150 deferred).
The latest-main revolution surface also has 18 direct C++ comparisons (canonical
cylinder, virtual iso curves), all bit-identical. The committed reports give the
current deferral owners; earlier tables above
are historical snapshots.