mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
ffb1e650df33d7dbbeaa6ed32c19fdaec347fffb
73 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5c45570c91 |
Kernel rig: rustfmt; measured values in results are decimals, within tolerance; the ladder records 06/023 passing
The single results form compares a whole number exactly (a count). The construction and inspect cases gave positions and volumes as whole numbers, which the form they were written for compared within tolerance; they are decimals now. The NPT groove fix makes ladder 06/023 pass: its golden entry is the passing run (51 faces, 8226.4042 mm3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
014a9eced5 |
Kernel rig: a flat_pattern field grades a sheet metal case's flat pattern
From the script's last sheet.flat_pattern result: the outline's size, its area less the cut-outs', and the bend lines, each matched to its own one of the result's either way round. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
70f695c6ba |
Kernel rig: grade construction geometry and the numbers a command replies with
A case's construction field names construction points, axes and planes with the point, line or plane each must be, and results reads values out of the commands' own replies by JSON pointer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bc3c9185cd |
Kernel rig: grade the kinds of a result's surfaces, its B-spline degrees and spans, and its free edges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
235645db50 |
Kernel rig: the ellipse length's Simpson weights as a named step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
45c9553262 |
Kernel rig: a case can check the files it writes and the commands' answers
A case's expected.json may name files written to $OUT (json, csv rows, or a DXF's entity counts and curve lengths per layer) and a command's answer by step, each matched as a part of what was made. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
39bf63b16a |
Kernel rig: designed continuity cases, G0 to G4, that prove the measurement
A new `continuity` area: seams whose order is certain by construction, each graded `exact` (reads its order and not the next) at picked edges with Continuity Analysis's default stations and tolerances: a box's crease (g0), a chamfer in place of a round (g0), an r5 round on planes (g1), and a profile closed by a g1, g2, g3 or g4 blend curve between lines, extruded (g1 … g4). `continuity.edges` may now be a list of points, picked as `inspect.continuity` picks them, and `continuity.exact` asks for the order and no more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c37b0d2f02 |
Kernel rig: a case's expected continuity order at its blend seams
expected.json may give `continuity: {order: g0…g4, edges?: "blend" | "corner"}`: the
seams of each round, chamfer face or corner patch with the faces either side of the edge it
replaced, and of each corner patch with its rounds, are measured as Continuity Analysis
measures them (seam_diagnostics at its default 33 stations and default tolerances), and the
case fails naming the worst seam, its faces, the order it reaches and the next order's worst
value. The seams come from the faces' persistent names (doc::naming::blend_seams). The older
`{near, within_mm, …}` form is kept.
Every fillet-area case with a body gets its order: constant rounds and rolling corners g1,
curvature rounds g2, chamfers g0, the corner: g2 patch g2 at its seams.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
97721ad05f |
Fillet setbacks not past where the rounds meet are refused naming the least; the rig grades a refusal by its reason
A corner setback must reach past where the corner's rounds' contact lines meet without one (smlib_blend_setback); short of it SMLib failed with its own status (1001). The kernel now refuses it before SMLib runs, for the rolling-ball corner (fillet_setback) and the curvature corner (fillet_g2_corners) alike, naming the least: on each face at a corner of planar faces and straight edges the two rails, r tan(psi/2) from their edges, cross (d2 + d1 cos alpha) / sin alpha along an edge (r on faces square to each other). Other corners stay SMLib's to refuse. The rig's `refused` cases now name the reason (`refusal`, a part of the message, required), so a failure for another reason no longer passes; a `solid_or_refused` case may name one. Fillets: the setback cases and 783, 3435 pass; 1390, 896, 4782 past-a-full-round and the no-tangent-chain case are refused for an unclear reason and fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2fed39c85c |
Continuity Analysis measures G3 and G4; the dialogs offer what each construction builds
Each seam now also reports the jump of the curvature's arc-length derivative across it (G3) and of its second derivative (G4), both from the faces' exact partials, and its order: the highest one every station meets within the tolerances (two new tolerances, defaults in the kernel). The verdict keeps its meaning (smooth is G2 or better). inspect.continuity replies with the new metrics, each seam's order and a count per order; the viewport widens G3 and G4 seams, the legend and the marker labels name every order in words. The Patch, Blend and Boundary rows offer the orders the kernel's Construction table says they build. G4's words in en, ar, de, he, ja, ko and zh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
77bd85cabf |
cli: snapshot --quality coarse|normal|fine|ultra meshes the bodies at that display quality
As the app's Display Quality setting, set before the design opens so every display plan is made at it; a level that isn't one is refused with the levels. For close-up reviews of imported faces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8fbb575a5e |
kernel-cases: a continuity check, and ladder 07/015 graded by it
expected.json `continuity`: over the tangent seams wholly within `within_mm` of `near`, the curvature jump across them (exact surfaces, 65 stations a seam) at most `curvature_jump_per_mm` clear (`clear_mm` along the seam) of the vertices where two of them turn, where a neighbour's own G1 seam ends. 07/015, the box corner's three r5 rounds with corner: g2: one valid body of 10 faces within the box, its patch's twelve seams jumping at most 1e-3 per mm clear of the hole's six corners by 1.2 mm (2.7e-4 measured; the ball corner of 009 jumps 0.2). Its golden entry is the passing run's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9cd655259d |
Profiler: commands, feature evaluation, io; diagnostics.profile; CLI --profile
- Session::execute is a command span; Model::evaluate and each feature computed (by its type) are spans; io's import, export and design read/write too; the STEP reader's topology, pcurves and solid record. - diagnostics.profile (contract + output schema): report, start/stop, reset, Chrome trace file. - A STEP/IGES import's reply carries its own profile while recording. - Every app compiles the profiler in by default (feature `profile`); SOLIDCRAFT_PROFILE=1 starts it. - solidcraft-cli run/eval/bench-open --profile print the table on stderr, --profile-out writes the trace; run --profile works on designs and STEP files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9a79588575 |
doc, engine, ui: placing the model returns its error; placements are checked where they are set
doc: world_state, world_state_reusing, state_in_frame_at, state_in_frame, face_point_state, face_point_names and construction_state return Result. A body that could not be placed, a frame that could not be inverted, a sketch plane or thread axis that did not survive the placement, or a failed placing worker used to leave things where they were without a word; now the call fails with what could not be placed. is_placement is the one test of an occurrence's placement (finite, a rotation that does not mirror, a translation within 1e9 mm). Opening a design names every occurrence that fails it; add_occurrence refuses one; a joint solution that would place an occurrence outside one is not applied and the joints placing it report it. engine: Session::world_state returns Result and keeps a failure in its cache as it keeps a placed model. bounds, home_camera, open_sketch_plane, the scene builders, the world previews, frames::to_local and auto_operation pass the error on; helpers answering "not found" take the placed model from their caller. Commands setting a placement check it (valid_position, exploded view, fastener at a point, STEP insert matrix). ui: panels, picks and hovers read the session's placed model through open_meshing::session_world; a model that cannot be placed shows no placed bodies, and the viewport says why across its bottom. Pictures and the control channel report the error. cli, mcp: snapshots, renders, benches and the oracle report the error; the kernel-case rig records it as the case's failure. Tests read the new Results with unwrap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
af4e97b648 |
cli: the rig and the oracle reach MeshTolerance through the engine
solidcraft-cli depends on the engine, not the kernel: solidcraft_kernel::MeshTolerance did not resolve; it is solidcraft_engine::kernel::MeshTolerance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
712caf8bf4 |
engine: no boolean retries to report; overlaps are SMLib's exact intersection
The kernel has no boolean retries now, nor their diagnostics (capture_boolean_diagnostics, BooleanDiagnostics, BooleanStrategy), nor the near-coaxial crescent (coaxial_overlap, coaxial_candidate, with_attempt_budget). What read them goes: - doc: FeatureResult and the evaluation cache carry no boolean diagnostics, and no "Boolean retry" warning; the saved model no longer writes them (an older file's are ignored); - engine: timeline.inspect / feature inspection drop `boolean_diagnostics`; fuzz runs and the CLI fuzz summary drop their retry counts; - inspect.interference: each pair is the bodies' Intersect, measured; the near-coaxial crescent, the by-piece intersection of bodies in several pieces and the unverified notes go (the command's doc no longer lists near_coaxial); - inspect.clearance: an overlap is the bodies' Intersect, measured near its region; the pin tries, the cut-down copies of large bodies (clipped, narrowed), the union cross-check (union_disagrees, contradicted) and the malformed-part check (plausible) go, and the measure near the origin passes its move's error on. Tests: the retry test and the unit tests of the deleted checks are removed; the barrel join test (tests/boolean_diagnostics.rs, now tests/barrel_join.rs) keeps its geometry checks; the pin-in-bushing test expects the filleted case measured; assertions on empty diagnostics and on near_coaxial are dropped; the corpus fixtures lose the diagnostics key and the one retry warning. (not built: base in progress) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
001c62ab16 |
kernel-cases: the rig runs SMLib only and keeps each run's SMLib dumps
SMLib is the only kernel now. cargo xtask kernel-cases ran every case once per kernel asked for (--kernel default|smlib|both), setting SOLIDCRAFT_KERNEL per run, and wrote a column per kernel; solidcraft-cli no longer reads that variable. The rig and the ladder run each case once, through solidcraft-cli built with --features smlib (cargo run passes it), and report one result per case: --kernel and the env var are gone. Each run has an output folder (--out, default target/kernel-cases, target/kernel-ladder for the ladder) holding results.json (every case's full result, which --results used to write on request) and dumps/<area>-<case>/: every case runs with solidcraft-cli kernel-cases --dumps=<out>/dumps, so a failed SMLib call leaves its inputs for replay. The ladder's golden holds one entry per case: golden.json is the last recording's smlib entries, which predate SMLib running every case (its reason says so); record it again after the first SMLib-only run. docs/kernel-cases.md (until the next run regenerates it), tests/kernel-cases/README.md, docs/kernel-smlib-sweeps-shell.md (its truck column and comparisons dropped), xtask's and solidcraft-cli's usage text: one kernel. Nothing was run. clippy -p xtask --all-targets: clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
beebf22032 |
kernel-cases: --dumps=DIR leaves each failing SMLib call's inputs for replay
A rig case whose SMLib call fails left nothing to replay it from: nothing called solidcraft_smlib::set_dump_dir. `solidcraft-cli kernel-cases --dumps=DIR` now points SMLib's dump folder at DIR/<area>-<case> before each case runs, so a failing call leaves its arguments and B-reps there (smlib_brep_read reads them back into a scratch C program), and turns it off after the run. Off without the flag. The kernel exposes it as smlib_dump_dir, beside smlib_built (the CLI reaches SMLib only through the kernel). (not built: base in progress) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
166ebe2ee9 |
The truck crates leave the workspace; the rig and threads run on SMLib alone
truck-modeling, -topology, -shapeops, -stepio and the vendored truck-geometry, -meshalgo, -shapeops, -stepio and matext4cgmath (with their attribution rows and vendor notes) are removed. The kernel-cases runner has one kernel, SMLib, and no build-mismatch check; a modelled thread is cut by SMLib's exact screw-swept groove (the faceted groove path is gone). (not built: base in progress) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
42997a62ed |
WIP kernel: Body::mesh(&MeshTolerance) as the one mesher (does not build yet)
Body::mesh / mesh_plan / mesh_of and MeshTolerance (chord, display, display_of, export_of) replace tessellate, tessellate_with, display_mesh*, display_plan*, reference_display_mesh_angular; every caller in kernel, doc, engine, io, ui-egui, gpu and the CLI renamed mechanically; the truck-mesher fallback, mesh_shells*, refined* removed; Body::new orients from the planner; truck-meshalgo dropped from Cargo.toml with the tests of its hooks. Still to fix: trait imports in tess/*, splitface/lumps/preview/step_in/boolean_record meshalgo uses, measure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
e1a2571696 |
kernel-cases: SOLIDCRAFT_KERNEL=truck grades as the default kernel
The kernel accepts `truck` (as well as unset) for its truck backend, and `boolean_kernel()` reports that backend as `default`. The rig compared the raw setting with that name, so a run with SOLIDCRAFT_KERNEL=truck reported every case as "this build runs booleans on the default kernel, not truck" instead of grading it. The rig now names unset, empty and `truck` alike as `default`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt |
||
|
|
d672cfa013 |
kernel: one switch, SOLIDCRAFT_KERNEL, picks the kernel for every operation
All operations move to SMLib at once, so the selector is no longer the booleans' own: `SOLIDCRAFT_KERNEL=smlib|truck` (read once, unset is truck, the same refusals for an unknown value or SMLib in a build without it) is answered by `solidcraft_kernel::backend()` (crates/kernel/src/backend.rs), which every operation's dispatch calls. The boolean's SMLib path moves to smlib_boolean.rs. The regression rig's kernel check (`boolean_kernel`) now answers from the switch, and the rig and CLI set SOLIDCRAFT_KERNEL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT |
||
|
|
8cb976b9b2 |
kernel-cases: watertight checks the display mesh, welding vertices by distance
Two faults made simple curved solids look open (#818's blind hole: 80 open edges, a stadium slot: 52): - It meshed with `refine`, the measuring mesh, which splits triangles across curved faces on their own and leaves T-junctions on the edges they share with neighbours. It now checks the display mesh, whose faces share each edge's points. - It matched vertices by rounding to a 1e-6 mm grid, which splits points a rounding error apart across a cell boundary. It now joins vertices closer than `tolerance.weld_mm` (default 1e-4 mm). Both cases now have 0 open edges, also with the weld at 1e-6 mm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
58ce14f45f |
kernel-cases: count ranges, a watertight mesh check, and per-case timeouts
For the history areas' cases (threads, tessellation): - A count may be an inclusive range `[min, max]`, where the geometry only bounds it (a modelled thread's faces). - `"watertight": true` meshes each body (chord tolerance `tolerance.mesh_mm`, default 0.01 mm) and requires no open and no over-shared edges, with vertices matched by position. - `"timeout_s"` gives a case longer than the run's timeout, at most 30 min. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
35ebc816ba |
kernel-cases: a missing capability's message is the case's own text
The report already says "not supported yet" for the status; the error repeated it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT |
||
|
|
bf0761f6b9 |
kernel-cases: base recipes, missing capabilities, and the commands' own time
What the complexity ladder needs from the rig's case runner, kept in the one runner: - A script may name a `base` case directory: that case's commands (and its own base's, up to 32 deep) run first, so a ladder case starts from the previous tier's finished part without copying its commands. `$CASE` is each recipe's own directory; `$OUT` is the running case's scratch directory, in its bases too. - `missing` in expected.json reports the case as not supported yet without running it: a capability with no command or parameter (multi-start threads, fillet setbacks) stays visible instead of being faked with a stand-in. - Each result carries `seconds` (the commands' own time, without process start or grading) and `measured` (body count, face/edge/vertex counts, volume, area), for the ladder's golden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT |
||
|
|
c24b066d25 |
kernel-cases: an outcome for geometry with no single manifold answer
`"outcome": "solid_or_refused"` takes a clean refusal, or else grades the result as usual. Two bodies that touch along a circle only (#168, #3841) have no one-solid union: a refusal or the two bodies left apart are both right; fusing them is wrong. The two cases said opposite things (two bodies, a refusal) and now agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
678a70da2c |
kernel-cases: shells as a graded count, no SMLib verdict where it can't take the record, $OUT
- `"shells": N` in expected.json grades the total number of shells: a body with a sealed void holds two, and some joins keep several lumps in one body. Those cases (#3175, #3455, #3526, #3730) are graded on validity again instead of being marked not valid. - When SMLib's record conversion says "not supported yet" (revolved surfaces, traced intersection curves), its check gives no verdict. That's shown but not graded, since it isn't a fault of the body. solidcraft_kernel::SmlibCheck carries the three outcomes. - `$OUT` in a recipe is a scratch directory outside the repository, for files a case writes. The two STEP-export crash cases write there instead of /tmp. - cargo xtask kernel-cases --results FILE keeps every result as JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7bb74d1c6b |
kernel-cases: an smlib feature for the CLI, and no grading of a kernel the build isn't running
- solidcraft-cli and solidcraft-engine forward an `smlib` feature to solidcraft-kernel/smlib, so `cargo build -p solidcraft-cli --features smlib` gives the rig SMLib's check. - solidcraft_kernel::boolean_kernel() says which kernel this process runs booleans on. `default` until the SMLib switch (SOLIDCRAFT_BOOLEANS=smlib) lands and answers there. An smlib run on a build without the switch reports "not supported yet" instead of grading the default kernel under SMLib's name. - An area may keep its skipped list in parts (skipped-<part>.json). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
732fd2ace2 |
kernel-cases: a regression rig that grades each kernel against the case's own answer
Every geometry case the old kernel got wrong becomes tests/kernel-cases/<area>/<id>/: an engine script or oracle recipe, and expected.json with the right answer, from analysis or a hand calculation, or else the invariants. Never a kernel's output. - solidcraft-cli kernel-cases <dir>...: runs a case and grades it. The checks are the outcome (succeeds, or refuses cleanly), the body count, and one closed shell per body that passes the record's structure and geometry checks and, with SMLib built, SMLib's check(All). Counts, volume, area, centroid and box are graded where the case gives them. Each run is pass, fail, not supported yet or crash. - cargo xtask kernel-cases [--kernel default|smlib|both]: one process per case, so a native crash or a hang (--timeout) is that case's result. It writes docs/kernel-cases.md: coverage per area and source, results per kernel, and the sources looked at and skipped (<area>/skipped.json). - solidcraft_kernel::validity and smlib_built, which the rig's checks use. tests/kernel-cases/README.md gives the format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a7c1909766 |
CLI: evaluate scripts on the input design (#6861) (#6872)
* claim: #6861 eval scripts on input designs * fix(cli): evaluate scripts on the input design (#6861) * fix(cli): validate eval options before opening inputs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
03bf992bc1 |
Keep linked components valid through Undo after Save As (#6860) (#6877)
* claim: #6860 * fix: preserve linked component targets across Save As history (#6860) |
||
|
|
b3cbbe7bd5 |
CLI: reject incomplete and unsupported run/eval options (#6918) (#6919)
* claim: #6918 validate run and eval options * fix(cli): reject invalid run and eval options (#6918) |
||
|
|
dfde1e032d |
mcp setup: add SolidCraft to the AI clients on this computer; plain mcp finds a late app (#6379)
solidcraft-cli mcp setup (alias install) and Help > Connect AI assistants edit each installed client's own configuration in place, with this program's absolute path: OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed and VS Code. Only the solidcraft entries change; a stale path is refreshed and an entry the user changed is left alone. Plain mcp keeps looking for the app while its headless session has no work, and says on every tool result that it is headless when the app was not found. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0520910bfd |
Control: name the token file and spot a stale one when a call is unauthorized (#4664) (#4670)
* claim: #4664 * Control: name the token file and spot a stale one when a call is unauthorized (#4664) When the app refuses a token that the MCP client read from a token file, the error now names that file, when it was written and how long ago, without printing the token. The app also writes `<port>.pid` beside the token file (its process id and, on Linux, its start time) and removes both at exit; when that process has exited, the error says the token file is stale. The process check runs only after a refusal, and the token file itself stays one line, so existing clients keep working. Document force-quit leftovers and the MSIX AppData redirection. * Bench assistant: read the token out of the new ClientToken Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
9e6e0b8df1 |
Render workspace: complete experimental ribbon, resources and effects integration (#5493) (#5494)
* wip(render): reconcile completed material and image pipeline source with 0.20 (#5493) Source-only integration reference for staged review, not a merge-ready aggregate. Donor: 0xlillith/solidcraft@aea20b0115. Upstream: |
||
|
|
b7b25f9e2b |
Render workspace: first basic delivery to main (#6050)
* Normalize UI container violation paths across platforms (#5599) Report repository-relative paths with forward slashes so diagnostics and the existing nested-package regression assertions agree on Windows and Unix. Keep the container detection rules unchanged. Validation: official ui-containers gate and all seven focused xtask tests passed; rustfmt and diff checks passed. Only xtask/dependencies were compiled; no application build or heavy tests. (cherry picked from commit |
||
|
|
5e0e7a4c6a |
CLI: --help on each subcommand prints its usage and exits 0 (#4538) (#4545)
* claim: #4538 * CLI: --help on each subcommand prints its usage and exits 0 (#4538) Every command now answers `--help` and `-h` with its own usage on stdout and exit 0, the way the top level has since #1828. Before, each subcommand answered differently: `fuzz --help` started a fuzz run with the default seeds and ran until it was stopped, `recipe --help` took the flag for a file name, and `snapshot`, `render`, `oracle`, `run`, `eval`, `script` and `crashes` answered with an error on stderr and exit 1. The help is read before the command runs, so `--help` never starts work: no fuzz run, no MCP server, no file read. `fuzz` also refuses an argument it doesn't take, as `snapshot` already did: a flag it doesn't know and a stray word are errors, not arguments to ignore while a hundred seeds run. The usage lives in one place now: each command's lines are a constant, and the top-level usage is composed from them, so the two cannot drift apart. The top-level usage is unchanged, byte for byte. # Conflicts: # apps/solidcraft-cli/src/main.rs |
||
|
|
0dffb206b1 |
Assembly: ground the first part; drag free joint chains (#2804) (#2805)
* assembly(feat): ground first parts and drag free joint chains Ground the first top-level part when it is created and let face dragging reposition free occurrences or any member of a floating joint chain in one undo step. Preserve exact repeated-placement frames, joint values, modifier selection and Escape cancellation. Keep the accumulated fixture, viewport layout and documentation integration fixes from the existing PR. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(fix): update movable fixtures for first-part grounding Accept the documented grounded override in component creation, explicitly free movement fixtures, and keep linked clipboard pastes movable even in an empty document. Cover first and reused linked pastes, default insertion grounding, and single-step undo in the existing link tests. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): keep the motion-cost crank free Unground the first component in the document-level joint-face measurement fixture so its motion samples can rotate after first-part grounding. Preserve the rotation and shared-measurement assertions that caught the Linux CI failure. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): free the frame placement fixture Create the frame-member placement fixture ungrounded before moving it above its root sketch. Keep its world-space member assertions while respecting first-part grounding in the frame tests added on main. Signed-off-by: Karl Von Richter<karl@embeint.com> --------- Signed-off-by: Karl Von Richter<karl@embeint.com> |
||
|
|
98c4f335e8 |
Control channel and MCP on by default, with a setting to turn it off (#5561)
The app opens its localhost control channel on port 27878 (or the next free one) unless Preferences > Assistant turns it off; --control and SOLIDCRAFT_CONTROL_PORT still win. A current.port discovery file lets solidcraft-cli mcp find the running app with no arguments. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
e33c371e99 |
Model cache: thin the checkpoints of a design too big for its file instead of leaving the cache out (#3220) (#4882)
* claim: #3220 * Model cache: keep fewer states of a design too big for its file instead of none (#3220) A model cache over the container's budget keeps the final state, then the slowest features' states while the bodies they add fit (a body several states hold counts once), and is left out only when not even the final state fits. Opening evaluates just the features whose state the file left out, each from the state before it, so the model is whole afterwards. * Model cache: keep each encoded body apart, copy only what a cache stores Encoding a model too big for its file no longer copies every distinct body into one buffer before thinning: the payload is copied from the encodings once its size fits, so saving holds no more than before. * Model cache tests: canonical runs in the timeline fixture; rustfmt * Model cache: count the records a thinned cache keeps; always try the final state Thinning counted only the bodies, so a model of many copies of one part (records, not bodies, filling the room) shrank its budget to nothing and saved no cache although its final state fitted. The choice now counts the records of what it keeps, always keeps the final state, and says what it counted, so each retry keeps less and the final state alone is always tried. * docs: a thinned model cache counts its records and always tries the final state * Model cache test: the slowest feature's state holds bodies 16 to 23 * Model cache: thinned opens with fingerprints that read the state before a feature Since #3397 a feature's fingerprint reads where the bodies before it lie. After a state the file left out, adopt takes the saved fingerprint and the evaluation that works the state out checks it. A result evaluated ahead is never taken as one the file left out. The thinned round trips include the placed components' design, whose cut reads those placements. --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
dad28abecf |
solidcraft-cli bench-assistant: the standard Assistant model test (#4851) (#4930)
* claim: #4851 * solidcraft-cli bench-assistant: the standard Assistant model test Runs the eleven v1 tasks of #4851 against a desktop app that is already running with a control port. Each task starts a new design and a new chat with All permissions, runs only the prescribed setup, sends the prompt once and waits for the turn. The result is checked on the design (document.inspect with measurements): body count, volume within 1% (0.3% for the fillet), bounding-box size in any orientation, the lowest corner, the body name, live feature kinds, and changed or unchanged content; the volume question is checked on the reply. Failures get the issue's categories from the conversation's items. Prints a table, and writes the issue's comment template (--markdown) and the raw results with each conversation (--json). |
||
|
|
c54b711345 |
CLI: --help prints usage to stdout and exits 0 (#1818) (#1828)
* claim: #1818 * claim: #1818 * fix(cli): --help, -h and help print usage to stdout and exit 0 (#1818) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
93a2922c5e |
Display plans and the model cache for large STEP assemblies (#2814) (#4021)
* STEP import: mesh band faces that wrap around a closed surface correctly Fixes two related bugs that caused SolidCraft to fail to mesh a face wrapping all the way around a closed, non-periodic surface (a revolved ridge band, or a tube-shaped B-spline surface): a missing face, a folded/self-overlapping triangulation, or a hole in the rendered body. 1. Ring edges that run slightly off their surface. STEP files write a surface and the curves bounding it to the file's own declared uncertainty, so a ring edge can legitimately sit a few micrometres off the surface it bounds. The code finding where a ring edge crosses the planned split line compared against the edge's raw (off-surface) point using a hardcoded tolerance far stricter than the file's own precision, rejecting a valid cut. Now matches against the point on the surface directly below the ring crossing, respecting the file's declared tolerance. 2. Closed B-spline surfaces, their slits and their slivers. truck only treats the rotation of a revolved surface as periodic; a face on a B-spline surface whose control net closes on itself (a tube) wasn't recognized as wrapping around at all. This produced a folded face, a hole (a boundary loop landing a full period away from where it should cut), and one near-zero-width sliver face costing the entire body its tessellation plan. Adds periodicity detection for closed B-spline surfaces, period- aware hole-loop alignment, dropping of zero-width slit boundaries, and isolating a too-thin-to-triangulate face so it no longer takes the rest of the body down with it. The GPU shader's periodic wrap mirrors the CPU math so the two don't drift apart. Regression tests: band_faces_split_when_ring_edges_run_off_their_surface, band_face_with_a_zero_width_slit_is_still_meshed, a_sliver_face_too_thin_to_triangulate_does_not_cost_the_body_its_plan. Ablation-tested: disabling either mechanism in (2) reproduces its target test's original failure message while leaving the other test unaffected. * STEP import: mesh the band's split faces, not just split them jyviko asked whether a band wall missing from the interactive app's render was a shading issue or a genuinely missing face. It was missing: the ring- edge split fix made the band's topology correct, but plan_face still produced zero triangles for the two new sub-faces. Two causes, both in plan_face (crates/kernel/src/tess/mod.rs): 1. The tolerance for how far an imported boundary may lie off its surface was tied to the requested mesh's chord tolerance instead of to the body's own size. A finer display tolerance could then reject a boundary a coarser one had just accepted, so the face would drop out exactly when zooming in on it. 2. Near a sharp corner of this flat-panelled band, several boundary points that are legitimately a few micrometres apart (within the STEP file's own declared precision) all projected onto the same surface point. The boundary loop then repeated a point in (u, v), which a triangulator can't follow, so the face's plan failed outright. One face without a plan costs the whole body its plan, and the fallback mesher can't handle a closed B-spline surface either, leaving both halves of the split band with no triangles at all: present in the topology, invisible in every renderer, and absent from the body's volume. Fix: scale the off-surface tolerance to the body's own extent (new Rules::extent), and collapse a run of boundary nodes that land on the same surface point to one (drop_collapsed) before triangulating. Regression test additions to band_faces_split_when_ring_edges_run_off_their_surface: the body's volume now matches the analytic pentagon prism (was a third of it - only the top cap - before this fix); display_plan leaves no face without triangles from coarse to fine tolerance; and the actual tolerance and facet angle the viewport draws with gives the split band its full lateral area on both halves. Confirmed on review_band_big.step, the fixture behind the review screenshots: volume's relative error against the analytic pentagon prism drops from 1.4e-4 to 4.1e-6, and the band wall now occludes the floor grid behind it in a snapshot the same way the top and bottom caps always did. Full kernel suite: 739 tests, 0 failed. * STEP import: hold an imported boundary to the file's own tolerance, not the part's size jyviko: the off-surface acceptance added in the last commit (rules.extent * OFF_SURFACE) scaled with the part, so a 2 m part silently accepted a boundary 2 mm off its surface while a 20 mm copy of the same part accepted only 0.02 mm. A STEP file's own declared uncertainty does not grow with the part, so the bound shouldn't either. Body gains boundary_tol: Option<f64>, Some(file's declared tolerance) for a body read from STEP (crate::step_in::Units::tol, already used by the ring-split fix in split.rs), None for anything this kernel built. It's set before the import's own unmeshed-face check runs, and carried over by the two face-split operations that re-cut an imported surface (splitface.rs, outline.rs), since the file's uncertainty still describes those boundaries afterward. plan_face's accept now reads: the file's own tolerance when the body declares one, or the old chord-tolerance/extent bound otherwise. The old bound's tol * 10.0 term had to go too, not just the extent term: at a 2 m part's typical display tolerance that term alone is ~20 mm, nearly ten times the extent bound jyviko objected to, so keeping it would have left the exact 2 m/2 mm example still silently accepted. Two new tests in step_in/tests.rs: - band_faces_split_when_a_scaled_copy_runs_off_its_surface: the same band fixture at a realistic 2 m scale with the same absolute slop and declared uncertainty as the small one, meshes the same way at every chord tolerance from coarse to fine. - off_surface_budget_follows_the_file_not_the_part_size: the actual regression. The same off-surface error is accepted or refused the same way whether the part is 20 mm or 2 m; a 2 mm error against a 1 µm declared uncertainty is refused at both sizes now (accepted at 2 m under the old bound, refused at 20 mm - that asymmetry was the bug). Checked at both a coarse and a fine display tolerance. Known gap, not fixed here: a STEP-imported body saved into a .solidcraft design and reopened loses boundary_tol (native.rs's decoder has no field to read it from) and falls back to the old extent-based bound on reload. Today's design format has no concept of this value at all; persisting it is a real format change, out of scope for this fix. Full kernel suite: 776 tests, 0 failed. clippy -D warnings clean. * Display plans for STEP threads and pointed ends (#3847) Three faces cost a threaded or pointed STEP part its whole display plan, so it was drawn from the reference mesh: cracked, faceted, and never refined up close. - A loop through a pole (a cone's apex on the boundary) seemed to wind around the surface: the jump at the apex, unwrapped to the nearest copy, was arbitrary. The loop is turned to end where it leaves the pole, so the step that closes it is an ordinary one (`seam::close_through_pole`). - Projection onto a spline with hundreds of spans (a helical flank) restarted from a 16 x 16 grid and settled on another turn; it now starts from two points per span, capped. - Sag refinement no longer inserts a point exactly on a boundary side, which split the side and failed the face. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tess: a refused face's edges are divided finer and it is planned again (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * tess: sliver faces plan empty; a face no method plans is filled flat from its nodes (#2814) A face whose boundary runs out and back along itself (two edges between the same vertices, a rounding apart, as STEP files write slivers) encloses nothing and is planned with no triangles. A face still refused after its edges are divided finer (a cusp, a sliver whose sides sag across each other, a small cap at a pole) is filled flat from its shared boundary nodes, so the rest of the body keeps its display plan instead of falling back to the reference mesh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * display mesh tests: a rim the chord tolerance leaves a line is divided again (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * portable B-rep: up to 8192 control points along a spline direction (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * solidcraft-cli: --debug logs what the engine reports to stderr Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * STEP import: a spline written over a tiny parameter range is read over one (#2814) Some writers parameterize splines over a few millionths (a humanoid robot's edges span 5.4e-6). The kernel tells parameters apart only to 1e-6, so over such a range it evaluated the curve's end wrongly, and trimming an edge to its vertices dropped a control point: the bodies could not be stored exactly and the whole model cache was left out of the design. The knots are now stretched to span one; the curve is the same and edges are trimmed by their vertices, so nothing read from the file refers to the old parameters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * model cache: a body keeps its file's declared accuracy (#2814) A body restored from the cache dropped the accuracy the file it was read from declares, so the planner held its boundaries to the kernel's own bound and a reopened design drew differently from the import. It is stored with the body's runtime values, and only when there is one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * tess: a boundary within a hundredth of its body lies on its surface, at every quality (#2814) #1721 held a body read from a file to the accuracy the file declares. A writer declares what it aims for, not a bound its edges keep: a 251-body robot assembly declaring 0.01 µm has boundaries 10 to 30 µm off their surfaces, and 95 of its bodies lost their display plans. The bound is a hundredth of the body again, what Normal quality always allowed, and now the same at every quality, so zooming in still never drops a face (#1721's point). A file declaring more raises it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * STEP import: keep faces whose edge vertices sit slightly off their curves Some exporters write an edge's vertex beside the end of its curve, further off than the accuracy the file declares (a surface export declares 0.01 mm and puts a vertex 0.19 mm off two spline ends). The reader refused such edges and left the face, and with single-face bodies the body, out. A vertex may now sit up to the file's tolerance or 1/1000 of the model's size (the diagonal around its VERTEX_POINTs), whichever is larger, never more than the existing 1 mm ceiling. Spline and open-conic edges have the end control point moved onto the vertex, so every such edge meets its vertices exactly. The file's declared accuracy still governs closed-curve, seam and circle checks. * tess, io: review clean-ups (#2814) - A sliver face is one whose loops are all slit: drop_slits finds it in the retry after a failed plan, so the pre-check on every face (folded) is gone. - The flat fill uses cdt::triangulate (a lenient fill that leaves out sides crossing one already kept) and its flood-fill inside test, and the plane from geom::Plane. - Refinement only builds its indexes when a face was refused, takes the faces to plan again from edge_faces, and re-samples edges on the pool; Rules::finer for the finer rules. - One PackageWriter::fits for the model cache, the preview and the thumbnail. - The band split's ring tolerance lives in split.rs; comments say what the off-surface bound is now. - The CLI logger prints Level::as_str(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM --------- Co-authored-by: David Lackovic <lackovicdavid1234@gmail.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Ray <raykholo@users.noreply.github.com> |
||
|
|
9e78e86b92 |
perf: renaming a body evaluates nothing (#3044) (#3095)
A body rename changes names only, but committing it went through the general "design changed" path: the whole timeline was evaluated again. On an imported assembly that means reading the STEP file again and meshing every body (about 100 s on the Prusa Core One, and the same again for undo). When the only change from the previous design is one body rename (the features' stored body names and the body-keyed maps the rename updates), the model now renames the body in its results instead: every body keeps its geometry and meshes, and the fingerprints are set to the renamed design's so the next evaluation reuses everything. Undo and redo of the rename take the same path. A rename the model can't follow that way (the body is changed or named by a later feature, sheet metal, threads) still evaluates the design as before. `bench-open --ops script.json` times commands and their undo on an opened design, with the view work each leaves. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e98e065584 |
Open saved designs from the model they were saved with (#3053) (#3102)
* WIP: adopt the saved model on open (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * WIP: tests * WIP: bench-open opens as Open does * docs: opening from the saved model * Tests: an edit after opening evaluates from the feature it changes * clippy * Read SOLIDCRAFT_NO_MODEL_CACHE through the env helper * Open: no picture from the model when the model itself opens; cancel test evaluates * Saved model: bodies passed through found without writing their record; states list runs of bodies * Adopted results go into the evaluation cache: an undone edit, or the marker rolled forward, takes them again * Model cache size guard: the saved model's writer --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
953fd7d5f8 |
Typed parameter contracts and JSON Schemas for solid.box and solid.extrude (#2829) (#2611)
* Spike: typed parameter contracts and JSON Schemas for solid.box and solid.extrude The schema RFC's spike (plan/schema-spike.md). solid.box and solid.extrude get typed contracts with generated JSON Schemas (2020-12), checked in front of the unchanged handlers by a per-session level: off (the default, which changes nothing), warn (the call runs and gains param_warnings) or strict (the call is refused and the design left alone). Also engine.describe and the MCP tool describe_command, `solidcraft-cli mcp --param-check off|warn|strict`, a FeatureResult type for feature replies, and an env-var audit recorder (SOLIDCRAFT_PARAM_AUDIT) used to check the contracts against every test. Local spike branch; not for upstream as is (see the results report). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Parameter contracts: put the audit recorder behind a feature; drop two ignored test keys The audit recorder is now on only in a build with the engine's `param-audit` feature and `SOLIDCRAFT_PARAM_AUDIT` set. Its code is still compiled, linted and tested without the feature. `scripts/param_audit.py` summarises a test run's audit logs, and the schema module's docs give the steps for converting a further command. Two tests passed keys their command ignores, which the audit found: `position` on `solid.box` (`hole_face_tests.rs`) and `symmetric` on `solid.extrude` (`face_tests.rs`). Both are removed, so the tests build what they always did. Comments no longer call the contracts a spike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
d0afc31632 |
Save the evaluated model with every design: exact, stored once, scaled caps (#3053) (#3093)
* Model cache written by default: exact bodies, stored once, scaled caps (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bench-open: say why bodies were not kept * Exact planes and revolutions; budgets for large bodies (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Native B-rep: rebuild shells as the kernel made them (closed and seam edges) (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the model cache and the exact native B-rep (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bench-open: time a save without the cache too * Model cache entries are Deflate-compressed (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Model cache: one that does not fit the design file is left out, never a failed save (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
26f42ae09b |
Snapshot: draw a see-through appearance see-through, and show a tool body inside its target (#2329) (#2330)
* Snapshot: draw a see-through appearance see-through, and give the CLI --body, --tool and --section A headless picture dropped a body's opacity: the scene carried only a colour, so a design with a translucent appearance was drawn opaque by `solidcraft-cli snapshot`, by MCP screenshots and in a design's thumbnail, although the window has always drawn it as glass. There was then no way to look at the body a cut would remove inside the material it meets: only a volume to measure and reason about, which hides exactly the errors a picture makes obvious (a tool that stops short, or lands in the wrong place). - `SceneMesh` carries the body's opacity; `view::build_scene` takes it from the design (`model_body_opacity`), and a selected body stays solid so the selection colour reads. - The GPU capture routes a see-through body into the scene's glass at its own alpha, as the viewport does; the CPU rasterizer draws the solid bodies first (they own the depth buffer), then the see-through ones blended over them, far sides before near sides and without writing depth, so what lies inside shows and a line between the two is not hidden. - `snapshot --body NAME:opacity=0.3,color=#rrggbb`, `--tool NAME` (that body solid in the tool colour, the rest see-through) and `--section z=27` run the commands the app itself runs (`appearance.assign`, `inspect.section`): the options are shortcuts, not a second renderer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Tests: measure the tool colour as it is drawn through a see-through target The tool-colour filter was derived on paper and demanded a red more than twice either other channel, which the target in front of it tints away (measured: (200, 108, 85) through opacity 0.3); and the colour check painted the tool, which a solid target hides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fmt * Snapshot options: strip the colour's # rather than slicing it; README Slicing `value[1..]` on an input-derived string is what the house rules ask us not to write, even where a length check has already made it safe. The error a failed assignment reports now names the body rather than claiming it came from --body, since --tool reaches it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * --tool: assign the bodies around the tool in groups, not one command at a time Each appearance.assign is an undo step with a document snapshot behind it, so one per body made --tool cost grow with the size of the design for no reason: the bodies around the tool all get the same opacity, and the ones that share a colour can be named in one call. A design whose bodies all wear the default grey now costs two commands whatever its size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Rasterizer: say where its glass and the GPU's differ Splitting each see-through body by facing orders its own two layers; it does not sort across bodies, which the GPU does. Worth stating rather than leaving a reader to find it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
f3505f0db6 |
World placement on all cores (#3053) (#3123)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |