New command inspect.compare {a, b, tolerance?}: measures of both and their differences, two-way surface deviation from a bounding-volume tree, regions beyond the tolerance clustered by side (only_in_a / only_in_b) with integrated volumes and faces, a verdict, optional face colouring as one undo step, and warnings for open meshes. Geometry in solidcraft-geom (compare.rs), command in the engine. Closes#1363.
"2,5 mm" is 2.5 mm; inside a function's parentheses the comma still separates the arguments. Noted under design format 22: an expression typed with a decimal comma is saved as typed, so 0.12.0 asks for an update instead of failing to evaluate it.
* Imports become components: a STEP assembly's tree as nested components (#1386)
Every imported file (STEP, IGES, 3MF, STL, OBJ: inserted, dropped, opened as a
design, or file.insert_step / file.insert_mesh / doc.open from scripts, the CLI
and MCP) now comes in as a new component named after the file, with its Import
feature inside it. A STEP assembly's products become nested components; a part
placed more than once is one component with an occurrence per placement, and
the Import feature skips the file's later copies (`skip`, format 21 note). The
contents are held as one rigid group (rigid joints, glyphs hidden); moving an
inner part is refused until component.unlock ("Unlock Contents") releases it.
Y up turns the import's component. Replies report component, component_name
and occurrence. Undo is one step; designs with loose imports are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep an import's rigid group hidden when the window loads the file in the background
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Import instances' skip list is format 22: format 21 ships in 0.12.0
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Catalog entries for Unlock Contents
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
component.insert_design (a saved part as a linked component) had no button, so an
assembly of separately saved parts looked impossible. It is now on the ASSEMBLE panel
of every tab and in the File menu; it asks for the file, and a STEP or mesh file picked
there is inserted as Insert STEP or Insert Mesh would.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Measure now reads the picked item's exact shape (cylindrical faces, circular and arc edges,
sketch circles, arcs and lines, profiles, planes, points):
- A circle, hole or arc shows Ø, R and its length (circumference, or arc length and angle).
- A slot (a sketch profile, or a half-cylinder end of a slot in a solid) shows its width,
centre-to-centre length and overall length.
- Two items where one is round measure from the centre or axis by default (axis to axis,
axis to plane, edge or point), with the closest and farthest distances listed too; the
panel switches between centre, min and max, and the canvas line follows.
- inspect.measure and MCP `measure` (new `items` argument) add diameter, radius,
center_distance, min and max; existing fields are unchanged.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Preferences ▸ Navigation ▸ Orbit around now offers View centre (the default, as before), Origin and Point under the cursor. A saved orbit_cursor preference migrates to the cursor option, and the control channel still accepts the old key.
Thanks to @kuraneko1 for this.
Co-authored-by: kuraneko1 <[email protected]>
The Joint and As-built Joint dialogs explain why picks make no joint and offer Create Components from Bodies when every pick is on one component; joint.create and joint.as_built suggest component.from_bodies. Labels in all catalogs. Fixes#1239.
Fixes#1187. New parameters.move {name, before|after} reorders user parameters (the design's own params order, so it is saved with no format change, and undoable); the Parameters dialog's user rows get up/down buttons. Label in every catalog. I merged main in. Checked on main + this branch: cargo check --workspace, the engine parameters tests, the UI parameter_order test, and docs/features.md regenerates unchanged.
Closes#1284. sheet.export_dxf writes layers by role (OUTLINE, CUT, TEXT or ETCH, dashed BEND_UP/BEND_DOWN, optional BEND_NOTES, POCKET_<side>_Z…_MM and PERFORATION), inner contours before the outline; text: cut|etch, bend_notes and note_height, per_body files. Letters are derived from the timeline, nothing new is saved (format unchanged). I merged main in (the export's parameter list keeps main's pocket layer names). Checked on main + this branch: cargo check --workspace, the engine cmd::sheet tests (160) and the io DXF tests.
Fixes#1167. While a sketch is edited, a body that a rolled-off, unsuppressed Remove deletes is not shown (unless the sketch is built on it); the history stays rolled back, only the view changes. Drawing, picking, screenshots, thumbnails and the browser's Bodies folder agree; document.inspect reports removed_later. Checked on main + this branch: cargo check --workspace, the engine sketch_edit_removed/occurrences tests and the UI viewport/browser tests.
One docked Save as mesh dialog replaces Export STL / 3MF / 3MF with face tags, with an optional Send to slicer that lists the slicers installed on this machine. Engine: file.save_mesh and file.slicers; the Convert Mesh dialog goes (mesh.convert and the MeshConvert feature stay, so existing designs open). Strings in all catalogs; screenshots in the PR. Checked on main + this branch: cargo check --workspace, the engine slicer/mesh/file tests, the UI catalog and menu tests and scenario s14 (the one local colour-token failure is epaint's TexturesDelta panic, which main shows on this machine too).
solidcraft-cli snapshot takes --camera JSON and --view with a saved view name (through view.apply, as the MCP screenshot does), plus --no-grid and --no-axes; the MCP screenshot and ui.render take grid/axes, the headless render skips a hidden origin axis, and the app's ui.render follows the Grid and Origin switches. Closes#1245.
A Convert to Sheet Metal bend point can now lie on a bend modelled with its radius (a cylinder tangent to two flat faces) or in the middle of a chamfer; either becomes one bend at the faces' virtual sharp, with the rule's radius. Holes and engravings carry over. Bend points on edges convert as before. Recorded under format 21. Tests in cmd::sheet::convert_tests; the sheet-metal guide is updated.
Drive Joints opens on the joint picked in the browser, else the first joint that moves, and won't start on a rigid joint. ui.start takes params: joint.drive opens on a joint (id or name) at given values, driving nothing until OK; a command or param it doesn't take is refused.
file.insert_step and doc.open on a STEP file take `up` (turn a Y-up file so its up is +Z) and `names` (names for the imported bodies). The MCP tool and the app's control requests pass them through; an import without them works as before. Tests in the kernel's STEP reader, the engine, MCP and the file-load UI; docs/step-import.md and docs/mcp.md describe both.
A solidcraft-mechanisms agent skill with a worked example that a test replays through the MCP server (the actuator follows the hinge with the stated stroke and the drive stops at the rod's limit); the skills index and docs/mcp.md list it.
The bridged MCP server starts when no app is listening, says so in its instructions and in each tool error, and connects on the first call after the app starts. A batch that could not be sent says the app is not running. docs/mcp.md describes it.
snapshot starts from --in (a design or STEP file) with the script on top, as run does, and refuses options it doesn't take; an unknown command id names the closest registered ids.
A control-channel method that reads the orbit camera, sets any of target/yaw/pitch/roll/distance, and fits listed occurrences (or everything) keeping the direction. Its reply passed back sets the same view again; a refused field changes nothing.
joint.drive answers moved, and a reason (with unconnected) when the joint can move nothing; motion.play answers moved and unmoved; joint.list answers floating chains. joint.drive values come back in joint.list's shape (deg/mm). quiet: true trims joint answers and motion.play poses. A single value keeps a joint's other values. Drive Joints shows the floating case (drive.unconnected, all locales).
A symmetric solid extrude of 10 mm is now 10 mm thick, 5 mm each side of the sketch plane, and switching from One Side to Symmetric keeps the thickness entered. Under format 21 the extent records its convention; designs saved without it keep the per-side reading, so their geometry and parameters don't change. Fixes#1236.
Co-authored-by: Karl-VonRichter <[email protected]>
* Title bar: inactive tabs without outlines, File menu, Help on the right
Inactive tabs go back to the plain bar colour (the Home tab's readable
hover stays). The separate Home button and theme toggle are gone: Home is
a tab and the theme is in Preferences. The folder icon becomes a File text
menu, Save moves into it (Ctrl+S unchanged), and Help sits on the right
next to Report a bug. Tabs start right after File, Undo and Redo.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Title bar: File button hover uses the app bar hover token
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
An opt-in Touchpad navigation preset: Shift + pointer motion pans, Alt/Option + motion orbits, vertical scroll zooms, all without a mouse button. Existing presets are unchanged; view.navigate gains continuous motion for the control channel and scenarios.
A design note for SolidCraft in the browser: tiered storage, a SharedWorker job broker, GPU dispatch and in-browser agent control through the same engine commands, with the generic pieces in craft-libs. Linked from the MCP and web getting-started docs.
Ruled Surface: strips off an edge chain in normal, tangent, angle or direction mode, exact planes/cylinders/cones where possible, mitred or round corners, stitched into one surface body. SurfaceRuled is noted under format 21. Closes#762.
* Action matrix: nightly shards cut by measured cost, heavy designs in slices, and a determinism check
The full matrix ran as four fixed quarters of the corpus and two of them hit the 60-minute
job limit. Now action_matrix_shard runs shard k of n (ACTION_MATRIX_SHARD=k/n), planned from
tests/action_matrix/costs.json: designs that take more than five minutes are split into slices,
and the pieces go largest first to the least loaded shard. The nightly runs 12 shards and uploads
each one's outcomes and timings; cargo xtask action-matrix --from DIR --bless records them in the
baseline and costs.json without running the matrix again. action_matrix_determinism runs a sample
twice and lists any action whose outcome changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Action matrix: costs measured on runners, 10 shards, determinism on the lighter designs
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Action matrix: baseline and costs from a full run on main, 16 shards, timeouts listed
The baseline is regenerated from the 10 shards' results of a full run on 01b1b705
(cargo xtask action-matrix --from DIR --bless): 8 new designs, 302 actions that pass now,
names that changed, and 40 actions that passed before and fail now, each listed in #1255
(22 fillets refused since #949, 4 hinge blends since #1101, 9 on the loft cut, 5 single).
costs.json now holds every design's measured time, and the nightly runs 16 shards of about
17 minutes. An action that passes in the baseline and times out is listed but not judged,
and stays passing when blessed: two runs of the same build differed only on 10 actions that
took 50-84 s against the 90 s limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Action matrix: slice heavy designs by a hash of each action's name
Slicing by index gave some slices only fillets on oracle 41 (28 slices, a multiple of the
actions per edge), three times the planned time; two shards ran past their limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Lands the Render workspace (R3), bounded preview-image jobs with cancellation and atomic PNG export (R4a), and opaque PBR looks as one change.
- Render workspace: an overlay-free product preview on the installed GPU device. Returning to Design restores its camera. render.scene and render.settings.apply are added.
- Image jobs: render.preflight/start/status/cancel/export run bounded CPU preview jobs. Export captures the job, overwrite choice and document, and checks the caller's file scope before writing.
- Opaque PBR: looks can carry metal fraction, roughness and dielectric IOR. The product viewport and preview PNG use them; geometry and mass are unchanged. The v1 catalog export refuses PBR rather than dropping it.
- Render setups and PBR looks are saved under design format 21. Labels are in en/de/ja/ko/zh/ru.
Supersedes #706 and #800. Refs #620, #673, #756, #1004.
Contributed by @0xlillith.
Co-authored-by: lillith <[email protected]>
Adds a French catalog as a partial locale (missing strings fall back to English, like Russian), registered in the language list, with tests and attribution.
A design carries metadata (format 21): project, part number, revision, author, organization, description and license, plus other tools' data under reverse-DNS keys, kept as written. Edited in the Design Properties window (Document Settings), set by document.metadata.set, reported by inspect_design, and written into STEP, IGES and 3MF where the format has a place for them. Translated, with tests.
The AMD FirePro D300/D500/D700 Metal driver hangs on the tessellation compute shader and macOS resets the GPU, freezing the desktop. Bodies are built on the CPU on that device, and SOLIDCRAFT_CPU_TESS=1 forces the CPU path on any GPU. The check is a tested predicate, and the troubleshooting guide documents the variable.
A crash inside a graphics driver left only "an unclean exit between
commands": no backtrace, no module, no hint that it was at startup.
- The crash journal now keeps the startup step the app is in (window,
graphics instance, surface, adapter request, adapter chosen, device
creation, renderer and viewport shader compiles, surface configure,
first frame and its present, running) and the renderer as soon as the
adapter is picked. egui-wgpu tells the app about its steps through a
small hook. An unclean exit before the first frame names the step.
- Windows: an unhandled-exception filter (src/native_crash.rs, the one
Windows-only module allowed unsafe) writes the exception code, the
faulting module and offset, and a minidump next to the journal; the
next start puts them into the report.
- The Windows CI job runs a child that faults on purpose and checks the
report.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Colour vision modes (Protan, Deutan, Tritan) in Preferences > Graphics, with palettes, shader and icon colours adapted per mode, translations and tests.
render.settings.set saves validated named render setups (camera, output size, exposure, environment) as one undo step, and render.settings.get reads them back without applying a camera. When validated render settings are the only change, the engine keeps the evaluated model instead of rebuilding it. Setups are saved under design format 21; format-20 designs read as having none. Fixes#658; refs #620.
Contributed by @0xlillith.
Co-authored-by: lillith <[email protected]>
Publishing a non-prerelease release runs winget.yml, which opens the
winget-pkgs pull request for bherbruck.SolidCraft with Komac (pinned
2.16.0 binary, SHA-256 checked) from the three versioned MSIs. It skips
cleanly without the WINGET_TOKEN secret or before the package's first
version is in winget-pkgs. docs/releasing.md has the one-time token
setup; the README's Download section gains the winget install line.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Windows: start Direct3D 12 first, and Vulkan only without a D3D12 GPU
wgpu started Vulkan, D3D12 and OpenGL together and preferred Vulkan, so
every Windows start loaded all installed Vulkan drivers. An old Intel
Vulkan driver (igvk64.dll) crashed 0.10.1 at startup inside the driver.
The app now probes D3D12, then Vulkan, then OpenGL, one backend at a
time, and starts only the first with a hardware adapter (WARP when none
has one). WGPU_BACKEND still wins; graphics.json's "backend" sets the
order by hand; the software renderer (safe mode, the retry) uses D3D12
alone. A new Windows CI job starts the real app under several settings.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* CI: a test filter YAML parses
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* CI: the startup smoke test writes graphics.json where the app reads it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* CI: run the WARP shader tests one at a time
WARP's shader JIT (d3d10warp.dll) crashed twice in 124 runs of the gpu
tests with four test threads, with a null read inside its own optimizer
while several devices compiled compute pipelines at once. Serial runs
did not crash.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Bounded in-memory PNG/JPEG texture decoding with explicit base-colour/data-map roles; RGBA8 pixels are published only after a complete decode. Input only: no material assignment, GPU shading or persistence. Refs #900.
Contributed by @0xlillith.
Co-authored-by: lillith <[email protected]>