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 <35337584+0xlillith@users.noreply.github.com>
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.
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 <noreply@anthropic.com>
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 <35337584+0xlillith@users.noreply.github.com>
When projecting a face's boundary loop onto its surface fails from an unhinted first point, the walk is retried once from the first point an unhinted search does find; a walk that succeeds is unchanged. A fillet strip on an extended surface now meshes, so the rim cut in the reported case builds in one boolean.
A primitive's `targets` live in the feature's participants beside its kind; renaming a body now renames them too, so a box or cylinder cutting the renamed body no longer fails with unknown body.
sheet.export_dxf writes an AutoCAD 2000 drawing with ELLIPSE and SPLINE entities for ellipses, splines and text outlines instead of chords, with curves: arcs for machines that read only lines and arcs. Perforation circles go through the same writer. Closes#1089.
Rectangular and circular sketch patterns stay linked to their source: editing the pattern's count, spacing or the source geometry updates the copies, and the copies keep their point and radius relations. Patterns are saved under format 20; older designs keep their independent copies.
Edges shorter than the model tolerance are dropped from the flat cells, and the refold sews touching corners, so a cut-out across a bend line no longer fails with a degenerate edge.
doc.open and the STEP/mesh imports run on the file loader's worker with the request's path scope, and a font named by file path in a design is checked against the scope before it is read.
A design built over MCP is many small commands, and each one did work over the
whole design, so building or showing a 1,300-body design took minutes:
- moved components were transformed and meshed again after every change; world_state
now reuses each placed body whose definition and placement are unchanged
- next_d_name searched the design once per candidate number
- Document::eval evaluated every parameter for every expression; now only when the
expression names one
- param_rows' used_by was quadratic; the desktop no longer gathers completions on every
change while no text box has focus
solidcraft-cli bench-agent builds a generated ~1,300-body bicycle command by command
and times it (241 s -> 16 s here). Eval output is unchanged apart from timings.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The engine's headless scene takes a palette; ui.render and the CPU viewport build it from the window's theme tokens, so a dark window renders a dark picture. CLI snapshots, MCP screenshots and Home thumbnails keep the default colours.
Moves the per-feature geometry handling (transforms, face points, profile selections, point expressions) into one geometry walk in feature_geometry.rs, with a golden record of what each feature kind moves. sheet.perforate holds no world geometry and joins the no-op arm.
Where an Import's stored labels pair one for one with its numbered built bodies (X, X (2), X (3)), component.paste_new stores the copy under the bodies' names, so the copied features after it find their bodies. No format change.
sketch.text and sketch.edit_text make the curve a text follows construction in the same undo step, so it no longer cuts the letters into pieces; the reply names the curve as guide.
timeline.paste keeps a cylinder Emboss's persistent face name, remapped onto the pasted features and bodies, so the copy embosses the pasted cylinder instead of failing (#1035).
Adds sheet.perforate: round holes on a staggered or square grid over the flat sheet, kept clear of edges, cut-outs and bend zones. The holes go to the flat pattern, the DXF and the measured volume rather than the B-rep, so a panel can carry many thousands of them.
Adds local scalar appearance and material libraries: a catalog in the io crate with safe writes, engine commands to list and apply library entries, and a library dialog in the UI, with docs.
* Refuse fillets and chamfers that run past their faces or cross each other (#1080)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Blend fit: chamfers keep their old acceptance of crossed loops; skip the volume bound along tangent chains
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Blend fit: one selection per check, a coarse volume pass first; record the refused oversize blends in the action matrix baseline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Thin Extrude builds walls of a set thickness along sketch curves, open chains and closed loops, with the wall centred, inside, outside or on both sides at two thicknesses. Saved under format 20 (thin on an Extrude); an extrude without thin saves as before. Closes#721.
Part 2 of #876: one walk over the references a feature holds (the sketches it's built from, the features it names, the bodies it works on) replaces three hand-written matches, with a golden record covering every variant.
Bodies imported with the same label come in as X, X (2), X (3). Renaming one of them no longer renumbers the others: body.rename now pins the current names of the feature's other bodies when it stores the new one, so later renames, materials, appearances and features still find them.
A tapered extrude now offsets spline and conic curves as well as lines and arcs, so sketch text and any other profile can take a taper, and the join of tapered letters onto a face finds its boolean result.
sketch.text and sketch.edit_text took at, height, angle and text with a silent default when a value had the wrong kind, so a height of "H" made 5 mm text and at: ["H", 25] put it at the origin. An input that is given (not null) now has to be what it names, or the command is refused with the input's name, and a null at no longer passes as the origin.
A planar union no longer leaves a sliver where an edge crosses its neighbour, so a sheet whose outline runs on past a flange's edge with a slight bend unfolds again.
timeline.export_script (and solidcraft-cli script) writes a design's history as a command script that solidcraft-cli eval replays into the same design, so a bug that only shows on one design becomes a one-file repro. timeline.add restores one stored feature with its id, names and references, through the same checks a design file gets when it opens.
Closes#886. SychPL's #1078 approached the same issue independently; thanks to both.
Co-authored-by: SychPL <SychPL@users.noreply.github.com>
On Windows the temporary folder often has an 8.3 short name, which the path scope refuses. vfs::long_names spells folders SolidCraft derives from the environment with long names, and the MCP scratch folder and the recovery folder use it, so screenshot without a path and the recovery commands work there again.
Pasting an Import together with later features that name its bodies now points those features at the pasted copies, including bodies whose names carry a number such as X (2).
* feat: validate physical material cards with sources and conditions
* feat: query directional elastic modulus from physical material cards
* fix: register physical material validation labels in complete locales
* fix: register directional elasticity labels in complete locales
* Add German and Korean labels for the material property commands
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* fix: a modelled thread that would cut nothing is refused (#995)
An M5 modelled thread in a 5 mm hole cut nothing into the nut: the
internal groove runs from the minor diameter out to the major one, so
with the hole as wide as the thread it lay entirely in the hole, and the
faceted cut only left the wall faceted with slivers ("the thread goes the
wrong way"). The fit check takes a 5 mm hole as an M5 hole.
- thread_groove measures how deep the groove cuts into the material
(hole wall out to the major radius, or shaft surface in to the root);
under a quarter of the basic depth 5H/8 the modelled thread is refused
with the measured depth, the tap drill (as solid.hole drills it) or
shaft size to use, and the size that fits the face, e.g. "this 5.00 mm
hole is too wide for a modelled M5 thread: it would cut 0.00 mm of the
thread's 0.43 mm depth; drill 4.2 mm for M5, or thread this hole M6".
- Cosmetic threads and their fit check are unchanged.
* test: pin the cut-nothing limit for coarse and fine pitch; show the hole size with show_mm
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Convert to Sheet Metal without bend edges refused every plate whose bounding-box diagonal reached a thousand times its thickness ("no outline on that face"), and just under that limit a hole smaller than the mesh's sag fell out of the outline and the DXF. The outline's tolerance is now at most a tenth of the thickness: a thin plate is meshed again at that tolerance. Nothing new is stored. Tests: long thin strips, sheets thousands of times longer than thick, and a strip keeping holes down to 1 mm.
* Fillets and chamfers meeting at corners on curved faces, on top of #935
What #935 and #926 leave of #851 and the matrix:
- Corner build for blends meeting on faces of any surface (curvecorner.rs):
the knife-edge tip's round with the arc's or the back edge's, edges
picked in any order, a round ending in an earlier round of its radius
(as a feature of its own), and chamfers meeting at the arc's corner.
Its end curves are fitted by chord length: a uniform parameter
through points clustered at the ends doubled back on itself there,
and the display planner refused the face.
- A straight edge ending at a curved face (the back edge into the
knife-edge tip): rolled, or cut by the face in the planar corner
build (blend_corner.rs).
- A rolling ball turning about a rounded corner of its own radius
(a pivot: a piece of sphere or cone).
- Seams that are an earlier blend's straight contact (fitted curves)
count as straight.
- No spline across a joint where the strip's curvature jumps (a line
into a small round): each strip ends on the joint's section.
- Sections at a chain's vertices on the faces of the edge from it
alone.
- Every new fillet path passes #949's check for openings.
Tests measure volumes through the display plan, as #935's do.
Action matrix: 51 more fillets and chamfers pass (sweep, tangent arc
chain, sheet-metal hem, bracket holes), none fewer. Robustness seed 33
recorded (its last fillet builds differently; display and measured
volumes now agree).
Co-Authored-By: bherbruck <brennen.herbruck@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bV2ntwLfUHV4JnCMPSyRi
* Corner blend tests: scale, seed 33's volumes, tighter engine bounds
From the review of #854:
- seed_33_display_plan_and_measure_agree: seed 33's bodies measure
the same through the display plan and the measure (2e-5), which the
golden's re-record rests on.
- the_arc_rounded_after_the_back_edge_matches_both_together: the order
that still leaves 0.05 mm³ at the tip, as an ignored test.
- corner_blends_hold_at_any_scale: the cases this branch adds on the
#851 part, at 0.01x and 100x, take off what they take at full size
times k³ (within 5e-6, checked to 1e-4).
- The engine tests on the part as drawn measure through the display
plan; bounds are about twice what each reads (2e-4 to 3e-3), and the
front corner's sets are checked against the kernel's CSG grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bV2ntwLfUHV4JnCMPSyRi
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
press_pull_on_the_enclosure_lid_is_prompt took 61-87 s of wall time on loaded runners against a 60 s bound, for about 9 s of CPU. It now checks the operation's CPU time (under 300 s) and how far it grows the peak memory (under 1 GB; the runaway it guards against passed 24 GB), from /proc/self where the system has it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the read-only material.properties.validate command: schema_version 1 engineering material cards in SI units are checked for numerical consistency and elastic stability, with derived isotropic moduli, missing conditions and sources needing review. Nothing is assigned or saved.
Thanks @0xlillith.
Fixes#971. At the node limit the offset certificate splits the fitted curve where it meets the exact offset midpoint, so curves with different parameter speeds certify within the same 0.002 mm tolerance. Associated offset fit nodes are hidden from display, picking, box selection and snapping until Break Association.
Thanks @Karl-VonRichter.