Commit Graph
1009 Commits
Author SHA1 Message Date
NOWN-DEV 37113c0c13 fix: sketch.text refuses inputs it doesn't understand (#1057)
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.
2026-10-14 09:24:30 -04:00
phanoob f2d7b220f7 Live preview: show a tool at once, and OK takes the preview's result (#1020)
The live preview of a join, cut or intersect now sends the tool body's scene as soon as the tool is built, then runs the feature for its error while the dialog keeps its spinner. OK pressed while the preview is still computing the same commands waits for it instead of computing the feature a second time on the UI thread; the session shares its evaluation cache with the preview's scratch copy.
2026-10-14 09:19:15 -04:00
Igor Churmeev 14d928b2c8 fix(macos): open files double-clicked in Finder (#1031)
macOS hands files opened from Finder, the Dock or open -a to the application delegate's application:openURLs:, which winit's delegate lacks, so AppKit showed "SolidCraft cannot open files in the SolidCraft design format". open_documents adds that method to the delegate's class before launch finishes and queues the paths for the UI, which opens them like files named on the command line.

This is the one place SolidCraft's own code uses unsafe: the app crate denies unsafe_code instead of forbidding it, and only the two macOS-only functions in open_documents.rs allow it, with the reason documented there and in AGENTS.md. Adapted from VectorCraft (see ATTRIBUTION.md).

Closes #997.
2026-10-14 09:19:08 -04:00
NOWN-DEV 435ad0fd8c fix: a planar union leaves no sliver where an edge crosses its neighbour by their shared corner (#1058)
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.
2026-10-14 09:14:41 -04:00
Thodoris Tsagkogeorgas 7879875d49 Command search asks the field for focus once (#724) (#1065)
The command search asked its text field for focus on every frame. On Wayland that restarted the text edit's held key, so the key kept repeating in other windows after the palette closed. Focus is now requested once, when the box opens, with a test that the field doesn't take focus back while the box stays open.

Fixes #724.
2026-10-14 09:14:03 -04:00
Lasse LianandSychPL ec8e739aad Export a design's timeline as a replayable command script (#1045)
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 <[email protected]>
2026-10-14 09:06:24 -04:00
NOWN-DEV 08b6a8fe0f fix: the server's own folders under a short-named temp path are expanded, not refused (#1055)
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.
2026-10-14 09:05:43 -04:00
NOWN-DEV cf93869c4d fix: pasted features act on the copies of an import's bodies when their names are numbered (#1059)
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).
2026-10-14 09:03:09 -04:00
bherbruckandClaude Opus 5.5 75f1cdd4c9 Hovered and selected faces light solidly again over GPU-drawn bodies (#1064)
Since the model's mesh went back to the reference mesher (#940), the GPU viewport drew
opaque bodies from their display plans while hover and selection lit faces with the
reference mesh's triangles. Two triangulations of the same face at equal depth fought, so
a hovered face shimmered in wedges instead of lighting whole.

Faces lit over a body drawn from its plan now use the plan's own triangles
(ModelBody::plan_mesh, evaluated once on the CPU), and the highlight pass gets a small
depth bias toward the eye so f32 rounding between the GPU and CPU evaluations of the same
triangles cannot speckle it either.

Test: every face of a box with a pocket and a round boss, hovered and rendered offscreen
through the viewport's real pipelines on the GPU path, lights the same pixels as on the
CPU path (skipped without a GPU adapter).

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 08:56:35 -04:00
bherbruckandClaude Opus 5.5 b5efde94c8 docs: make the release notes template version-neutral (#1074)
The template still carried the 0.9 notes, and nightlies publish it as is. Keep the introduction, the upgrade notes that hold for every version and the install instructions; the release-specific part is written for each release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 08:36:03 -04:00
bherbruck 0897ee3781 docs: clear the release notes carried into 0.10.0 (#1054) 2026-10-14 08:17:48 -04:00
seluneandbherbruck 52fb5ac0e6 feat: query directional elastic modulus from validated material cards (#998)
* 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 <[email protected]>
2026-10-14 08:17:00 -04:00
377e12a059 A modelled thread that would cut nothing is refused (#995) (#1018)
* 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 <[email protected]>
Co-authored-by: bherbruck <[email protected]>
2026-10-14 08:16:55 -04:00
3256fffe35 test(ui): find command dialogs by their published rect (#1011)
The dialog_docked scenario check and the ui_layout width test looked up
the dialog window by the id "sc_dialog", but dialogs are now registered
under ("sc_dialog", <title>). Every lookup failed with "no command
dialog", so the test had to stay ignored and could no longer catch an
oversized dialog.

Read the rect the dialog publishes each frame instead (viewport
dialog_rect in the check, "dialog:outer" in the test), and re-enable
every_dialog_opens_docked_within_its_width_limits. The exploded view
dialog it was ignored for now opens 365 px wide, inside its limits.


Claude-Session: https://claude.ai/code/session_01VbBYY5zfMXgq939AF9Vren

Co-authored-by: PhilippBischoff0 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: bherbruck <[email protected]>
2026-10-14 08:16:49 -04:00
NOWN-DEV 0f4cda0510 Convert to Sheet Metal takes a long thin plate (#1040)
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.
2026-10-14 08:12:41 -04:00
JIJINBEI 3341e2d60b One walk over a feature's expressions (1/3, #876) (#902)
inputs_mut, inputs and expressions are now built on one exhaustive walk (walk_exprs / walk_exprs_mut) instead of two hand-written matches over FeatureKind. inputs() no longer clones the feature, plane chains are walked with the same nesting cap for inputs as for expressions, and a new variant without a walk arm does not compile. A golden record of every variant's expressions guards the walk.
2026-10-14 08:11:03 -04:00
NOWN-DEV 35e58d18ed docs: the camera of screenshots and renders, and a close-up recipe (#1022)
docs/mcp.md gains a "Camera and close-up recipe" section: the orbit camera's conventions (radians, eye direction for yaw and pitch, orthographic half-height, which side inspect.section keeps) and a seven-step recipe for a close-up of one region of a large STEP model. Documentation only.
2026-10-14 07:43:57 -04:00
32d3cf6877 Fillets and chamfers meeting at corners on curved faces: what #935 leaves (#854)
* 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 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bV2ntwLfUHV4JnCMPSyRi

---------

Co-authored-by: bherbruck <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 07:40:23 -04:00
bherbruckandClaude Opus 5.5 27374ca289 test: hold the enclosure lid's press/pull to CPU and memory, not wall time (#1027)
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 <[email protected]>
2026-10-14 07:38:53 -04:00
bherbruckandClaude Opus 5.5 d506382aca ci: run the action-matrix sample on its own; name a hang in an edit round (#1037)
The sampled pi-case hole on Body1/F13:side:[email protected] became a real hole with #952 (7.5 s instead of 170 ms); beside the heavy tests on main's push runs it passed the matrix's 90 s action limit in its edit round, reported as the design not loading. The sample now takes every CPU in nextest so nothing runs beside it, and the edit round writes a start line so a hang there is reported as <action>+edit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 07:19:25 -04:00
bherbruck 626fe6fdee Release 0.10.0 (#1010) v0.10.0 2026-10-14 05:38:13 -04:00
selune 01f2c78cd1 Validate sourced physical material property cards and elastic stability (#934)
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.
2026-10-14 05:28:18 -04:00
Karl Von Richter 9ca7d307a4 Offset of an irregular closed spline no longer fails its error certificate; offset fit nodes stay private (#1009)
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.
2026-10-14 05:27:39 -04:00
bherbruck 2f91338d3d Large STEP imports: no more multi-minute hang, shared STEP text, cancel and progress (#925) v0.9.0-nightly.20261014.36.1 2026-10-14 05:01:12 -04:00
bherbruckandClaude Opus 5.5 dfbf7dd6a8 fix: a lenient parameter boundary found across a seam is not used; record seed 81 (#1003)
#970's lenient retry of a face's parameter boundary searches the whole surface as a last resort; on robustness seed 20 it found an edge's points across a cylinder's seam, half a turn apart, and the Cut failed. A lenient boundary whose points jump half the parameter range or more is refused, and the face is left undivided as before (#970's elbows step at most a fifth of the range). Seed 81's volume moved by 0.1 mm3 with #952's hole direction; recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 04:56:39 -04:00
Mateusz SychandSychPL cf9d7772f1 To / From Object finds a target in another, moved component (#993)
Fixes #928. Extrude To / From Object and Revolve To Object now resolve their target in the feature's own component frame, so a face in another component whose occurrence is moved is found where it is shown instead of the extrude stopping at a flat plane through the picked point. The point is named after the nearest face, a named point keeps its named face when another component touches it there, and such features re-evaluate when a placement changes, so they follow the target when it moves later.

Co-authored-by: SychPL <[email protected]>
2026-10-14 04:41:59 -04:00
midasdfandmidasdf 6e84c03946 Curvature-continuous (G2) fillets for straight planar edges (#862)
Fillet gets a Curvature (G2) radius type (type: "curvature", alias "g2"): a quintic blend whose size is the equivalent circular setback, for convex and concave straight edges between planar faces with a perpendicular planar end face at each end. Unsupported cases are refused rather than falling back to a circular fillet, and a blend that would cross a loop in its end face is refused. Persisted as a fillet style under format 19; labels in en/de/ja/ko/zh.

Co-authored-by: midasdf <[email protected]>
2026-10-14 04:26:53 -04:00
froglemonade2andfroglemonade2 3669d4b3d0 Cut a hole tangent inside a stepped boss (zero-thickness curved wall) (#986)
Touching sews now split 4-use straight edges on extruded walls (along their rulings), not only on planes. Faces round such an edge are ordered by the way they leave it and, where they leave it tangent, by their curvature. Sheets bound the material wedges as before, except where that would make a face touch itself (a hole touching the outline from inside); edges ending there pair the empty wedges instead, so each loop gets its own vertex. Coincident faces are still refused, plain sew still refuses vertex contact, and shell_groups is unchanged.

A Ø10 hole tangent inside a stepped Ø40/Ø20 boss now cuts exactly instead of failing with "wire is not simple". Robustness seeds 42 and 51 are recorded: their touching hole patterns in stepped bodies now cut exactly instead of by a tool nudge.

Co-authored-by: froglemonade2 <[email protected]>
2026-10-14 03:55:06 -04:00
NOWN-DEVandNOWN-DEV 345c0882cd Renders hide edges just behind a face; lines on steep faces draw whole (#983)
The CPU renderer's line depth test allowed a fixed share of the scene's depth range (2e-4), which on large designs or close views let edges a millimetre or more behind thin sheet show through it. Each pixel now keeps the screen depth slope of the surface it shows, and a line passes where it is no further behind than that slope spans across the line's width, plus f32 rounding. Section caps use their plane's slope.

Edges lying on steep faces now draw their full width; edges behind a face stay hidden. Only crates/render/src/raster.rs changes; the GPU viewport is unchanged.

Co-authored-by: NOWN-DEV <[email protected]>
2026-10-14 03:28:31 -04:00
bherbruckandClaude Opus 5.5 18347b3d0d Boolean nudge: no sliver where a tool only touches a round side (#827, seen in #897) (#941)
* Boolean nudge tried both ways: no sliver where a cut's end touches a round side (#897, #827)

Robustness seed 14 cuts a cylinder whose end lies exactly on the apex of the
plate's arc side. Since the display tessellation plans (#805) the generator sees
the arc's true extent, so the cut's base lands on -10 instead of -10.000000000000002
and the end is tangent to the arc. The direct boolean fails there and the
last-resort tool nudge pushed the end into the arc, leaving a planar sliver a
tenth of a millimetre wide; shell then picked that sliver and refused it. The
13 October merges were not the cause (the bisect lands on #805).

The nudge is now tried both ways and the simpler result (fewer faces) is kept,
so the opposite nudge parts the touching faces instead. The same change joins
the hinge barrel in #827, whose top lies tangent to the lid's underside where
the mounting leaf ends. Each result still passes the same checks as before.

Goldens: seeds 14, 26 and 85 now build to the end, 31 and 78 come out with
fewer faces. Seeds 6, 43 and 73 still stop at shell: their joins now succeed and
shell reaches corners it can't offset (a cusp where a box face touches an arc,
coplanar faces facing opposite ways), which is unchanged.

Fixes #897
Fixes #827

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Nudge the other way only when the first nudge fails or leaves a sliver

Trying both nudges and keeping the one with fewer faces could prefer a result
that keeps two pieces joined by a hair over one that separates them (the
centred coplanar revolve cut). The opposite nudge now runs only when the first
one failed or left a face narrower than 1/200 of the part, and it is taken
when it works with fewer of those.

Goldens re-recorded on current main: seeds 14, 26 and 85 build to the end,
31 and 78 with fewer faces. The other changed seeds are main's own since its
last full record, identical with and without this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Nudge the other way only for slivers the nudge made

On the sensor enclosure's lid the first nudge left thin faces the operands
already had (thin walls, small rounds), so the opposite nudge ran a second
boolean for nothing and the press/pull took 79 s against main's 55 s. The
opposite nudge now runs only when the first one failed or left more slivers
than the operands brought; the press/pull test takes 36 s against 42 s on main
(same machine, one after the other).

Goldens re-recorded on top of #940.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Goldens on current main: seeds 31, 43 and 78 with the two-way nudge

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Keep the hinge and tangent-cut tests beside the #871 fillet test

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Goldens: seeds 33 and 91 as main's fillet changes build them

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Goldens: keep main's #950 record

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 02:55:37 -04:00
bherbruckandClaude Opus 5.5 ac84491638 test: web-offline-test reads the worker's caches without opening them (#982)
The state probe opened every cache it listed; when a worker deleted one in between, the open made it again, empty, and the next wait timed out. The probe now reads each cache with caches.match over the builds' files and marks, which never makes a cache (3 failures in 20 runs before, 0 in 30 after). The worker only ever starts a complete build.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 02:54:45 -04:00
NOWN-DEV 6a67cc8f1e An edge flange folds beside a ledge as tall as its setback (#978)
Trimming the base at an edge flange's setback snaps corners within tolerance onto the trim line and merges away the zero-width run a ledge as tall as the setback left along it, so the ledge goes with the strip as a lower one does. Splitting outline edges at other cells' corners now skips points within the model's tolerance of an end, so a ledge a little taller keeps its sliver and the bend runs under it, also off the axes. Both used to fail with "sew: a degenerate edge". Nothing new is stored.
2026-10-14 02:51:29 -04:00
bherbruckandClaude Opus 5.5 6cb3025b72 Sheet metal: a hole drilled into a rim is a bore its panel keeps (#560) (#955)
A hole drilled along a panel, into its rim, has no place in the flat pattern
as a cut-out, so every such hole was refused. It is now kept as a bore of that
panel, in flat coordinates, and cut only from that panel's material when the
folded part and its flat pattern are built, so the sheet still unfolds and
folds back with it. A hole that would run into a bend, or cross a panel at an
angle, is still refused. The flat pattern lists the bores.

The action matrix worker takes ACTION_MATRIX_ONLY (just these actions) and
ACTION_MATRIX_UNFOLD (unfold and refold every sheet after each action) for
rechecking one group by hand.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 02:31:19 -04:00
bherbruckandClaude Opus 5.5 b34d45233d Holes on round faces from the app, and Flip for holes at sketch points (#980)
* Holes on round faces from the app, one hole per pick, and Flip for holes at sketch points (#967)

The Hole dialog takes any face: a hole on a round face drills against the face's normal where it
was picked, and its arrow shows that. A round face split at its seam is picked whole, but makes
one hole where it was clicked. Holes at sketch points get Flip (saved as flip in the hole's points,
under format 18), in the command and in the dialog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Holes at sketch points show their arrow: into the part, or away with Flip (#967)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 02:10:09 -04:00
Karl Von Richter 47a7f29485 Guide sketch patterns with live previews and editable offsets (#966)
Sketch Mirror, rectangular and circular Pattern, and Offset now show a green live preview that follows the selection and settings; OK commits, Cancel leaves the sketch unchanged. The shared dialogs show the current selection step and source counts, sources can be toggled with Ctrl/Cmd-click, cleared and reselected.

Offsets keep editable driving distance dimensions (One Side, Two Sides, Symmetric) that update the derived curves without moving the source, with undo/redo and save/reopen. Format 19: older files keep their fixed offsets.

New dialog labels in en, zh, ja, de and ko.

Fixes #696. Fixes #517.
2026-10-14 01:58:01 -04:00
Giovanni 9c51f9d433 Retry a face's parameter boundary leniently (#970)
Cutting one lofted round tube from another failed in truck's divide_face ("parameter boundary") for some section counts: the intersection points lie on the loft's seam, the previous point's parameter can sit a hair outside the range, and the Newton search from there fails.

The boundary is built as before; only when that returns None is it built again leniently, starting each search from the hint clamped into the parameter range and, as a last resort, searching the whole surface. Nothing that succeeds today changes. The boolean-retry guards are untouched.

Fixes 5 of the 6 failing hollow-elbow cases in #968 (new test crates/engine/src/tests/loft_cut.rs, volumes within 0.3% of exact). 90 degrees / 14 sections still fails in a later step and stays open in #968.
2026-10-14 01:50:00 -04:00
NOWN-DEV 4687e3b073 Convert to Sheet Metal keeps a plate's round holes round (#977)
The plate check compared the face outline's area (from the tessellation, so each hole was a polygon of chords) with the exact volume, and refused plates with a few small holes. Where the outline runs along a cylinder about the plate's normal it is now an arc of that circle; coaxial pieces of one radius are one wall, so a hole split into two, three or four faces is one circle. The check passes with its tolerance unchanged, and the flat pattern and its DXF carry one CIRCLE per hole (ARC for rounded corners and slot ends).

A round blank now converts too, so unfold_cut_refold_and_convert checks the refusal with a block thicker at one end instead of a rod.
2026-10-14 01:47:40 -04:00
bherbruckandClaude Opus 5.5 1ff16cc72b A hole on a face drills into the material, also in older designs (#950) (#952)
* Hole matrix: placement, types, extents, edits and 0.9.0 designs, with today's failures listed

Each case checks the drill goes into the material, the bore sits on its axis, a blind hole stops
where it should, the removed volume against its analytic value, the face count and that every
body is still one closed valid solid. Failing cases are listed in tests/hole_matrix/known_failures.txt
as a ratchet.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* A hole on a face drills into the material, also in older designs (#950)

Without a direction, solid.hole took the direction from any face whose infinite plane passed
within ten tolerances of the point, the last one found winning: the far side of a thin plate or a
face of another body at the same height could set it, and the wrong direction was saved. It now
uses the face nearest the point, against its outward normal (exact for planes, cylinders and
spheres, so holes on curved faces work without a direction). When a design is evaluated, a face
hole whose saved direction leaves the face it starts on is mirrored into it, so designs saved
with the wrong direction drill into the part.

Fixes #950.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Hole at a point on no face (a planar face's centroid outside it) keeps the plane it lies in

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Record the hole direction fix in the goldens and the action matrix baseline (#950)

Seeds 7, 21, 81 and 85 drill their holes straight into their faces now. In the action matrix,
holes on four faces that used to go the wrong way now build, and two whose correct direction
meets a cut the kernel refuses are marked failing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Goldens: seeds 7, 21 and 85 drill their holes straight in (#950)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:45:11 -04:00
bherbruckandClaude Opus 5.5 813bee8914 Fillet: refuse a result with openings in its surface (#871) (#949)
* Fillet: refuse a result with openings in its surface (#871)

A fillet on #871's part (radius 1 on a window's edges and on the sides of a
block 1.26 mm away) came back closed as topology with every face meshing, but
the rounds overlapped and the end faces' boundary loops crossed, so the model
showed holes. The constant-radius fillet now checks its result before taking it:
the body's mesh, welded within a millionth of its size, must have no edge
without as many triangles running the other way along it, except for cracks
within the mesh tolerance where fitted curves meet. A result that fails is
refused, so the corner build gets its try and the radius search treats it as
too large.

Body::open_mesh_edges is the check. Kernel tests cover the #871 part (refused
at radius 1, closed with a round on every edge at 0.5), and an engine test runs
the fillet from the report and leaves the part as it was. All 96 robustness
seeds build the same with and without it.

Fixes #871

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Check fillets for crossed face boundaries, not free mesh edges

Free edges in the mesh also come from cracks where fitted curves meet (corner
patches, rounds ending on curved edges), so the mesh test refused good fillets.
The check is now on the cause of #871: a planar face whose boundary loops cross
each other or themselves has no inside. Body::crossed_face_loops samples each
planar face's loops within 1/1000 of the size and counts proper crossings
(touching within a millionth doesn't count).

The pyramid apex blend and the loft fillets build again. Ten action-matrix fillets
stay refused, recorded in the baseline: r = 3 on the 2 mm rim of oracle 09's
shelled box (the round runs past the inner wall; the result was 240 mm3 lighter
than any round can make it), and the six seam-edge fillets on oracle 60's bolt
holes, whose boundary jumps across the hole.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:36:30 -04:00
Mateusz SychandKarl-VonRichter 482abb4562 test(mcp): compare the scratch folder by its canonical path (#965)
PathScope stores its roots canonicalised (\\?\ on Windows, /private/var on macOS), so the default-folders test now canonicalises scratch_dir() before looking it up, like the recovery folder on the line above.

The same fix came in from @Karl-VonRichter in #969, found on macOS.

Co-authored-by: Karl-VonRichter <[email protected]>
2026-10-14 01:26:51 -04:00
7bed28752e Sheet metal: fold a second tab on a line, and a tab on an L's inside edge (#638, #639) (#963)
- A fold's zone clipped across the root of a tab folded before on the same
  line kept a sliver of no width there, which made the zone look like it
  crossed a slanted edge. The one real piece is the zone now (#638).
- When the tabs a fold line spans can't take the side beyond them along,
  because it joins the side that stays through other material (a tab on the
  inside edge of an L, the fixed point on the far leg), the tabs' own ends fold
  instead, the same part as with the fixed point on the near leg (#639).

Co-authored-by: jonaswre <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:16:04 -04:00
bherbruckandClaude Opus 5.5 ea23401710 Sketch on every side wall of one sketch extruded up and down (#957)
* Name a side drawn on several sketch curves by all of them

An extrude named each side wall after the one sketch curve its profile
segment lies on. A wall along two coincident lines (squares drawn edge to
edge) or along collinear lines merged into one profile segment had no
single curve, so it was named from geometry and could not carry a sketch
attachment. One sketch extruded up and down and joined (the logo) refused
a sketch on every one of its side walls.

Such a side is now named by every curve it lies on, in sketch order: the
first is its name, the others aliases. The set is fixed by the sketch, so
the name is exact. The refusal message no longer blames merged faces.

Adds the reported logo design and a synthetic up-and-down join to the
action-matrix designs, and a regression test that sketches and extrudes on
every flat face, fresh and after save and reopen.

Fixes #954

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Action matrix: record the up-and-down join designs

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:00:04 -04:00
Nathan Dane 9f76cca8b4 Collect application resources for #433 (#833) 2026-10-14 00:49:32 -04:00
d5ff5ea1a5 Sheet metal: a thousand cut-outs fit the flat budget, ten times faster (#732) (#964)
- A new cut was clipped against the panel with every one of its cut-outs, so
  each hole of a perforation cost the square of all the holes before it and the
  second cut of 500 ran out of the flat budget. A tool is now clipped against
  only the cut-outs whose boxes meet its own (the same result).
- The planar booleans' point-in-loop test now skips loops whose box is clear of
  the point, before the costly on-the-loop test; with a thousand holes that test
  was most of the time.

The 1000-hole panel now takes about 15 s instead of failing.

Co-authored-by: jonaswre <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 00:40:08 -04:00
bherbruck ce902be66c Fillets and chamfers ending on a boss's pieced wall or beside it, on a face that runs along its seam twice, and loops the loop blend refuses (#926)
Fillets and chamfers ending on a boss's pieced wall or beside it, on a face that runs along its seam twice, and loops the loop blend refuses

- A straight edge whose end meets one curved surface in several faces (a cylinder a join left
  in pieces) ends on that surface: the section is cut where it crosses each seam, which is
  shortened there; where no seam is crossed (the wall missing inside the other body) the
  section lies on the bared surface and the rest of the side edge becomes a seam.
- Two end faces that aren't both planes meeting in lines: each face takes the section's points
  on it, cut where it crosses the edge between them. Not where an end face runs on smoothly
  from a side (an earlier round): the corner build takes those.
- Face boundaries close over several connector edges.
- A face on a periodic surface running along its seam twice (as a STEP file gives it) is
  rebuilt the same way (#691).
- A curved chain ending square on a face that runs on past its side extends that face's seam.
- A smooth chain stops before an edge between faces that meet smoothly.
- A loop the loop blend refuses for its shape is blended edge by edge; refusals about its size
  stand.
- Action matrix: ACTION_MATRIX_ACTIONS and ACTION_MATRIX_VALIDITY for quick local runs; the
  fillets and chamfers that pass now recorded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 00:28:09 -04:00
63095fedb3 Sheet metal: a fold's fixed point on the sheet's face counts as on it (#685) (#958)
On a large thin sheet the probes around the fixed point stepped clean through
the sheet (twice the mesh tolerance of a 465 mm part is more than its 0.8 mm
thickness), so a point exactly on the face touched no material and the fold
was refused. The probes now also step a quarter of the thickness.

Co-authored-by: jonaswre <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 00:14:49 -04:00
bherbruckandClaude Opus 5.5 00fdcf3e72 fix: grid lines for split ruling edges only in display and export meshes; record the moved seeds (#961)
#921's grid lines through the vertices that split a ruling edge also went into the meshes Boolean operations intersect, which changed seven robustness seeds (seed 2's sphere cut failed). Deflection::rulings now asks for them only in the kernel's own tessellation (display, export, measurement). The seeds #936 (rejected instead of unsupported), #946 (26 and 85 build, others fail differently) and #921's display side (about 1e-6) moved are recorded with their reasons.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 00:14:18 -04:00
bherbruckandClaude Opus 5.5 774a8b1d9a test: record the bracket example's action matrix after its fillet moved (#962)
#820 fixed examples/bracket.json's fillet pick, which lay far from every edge and rounded the outer corner instead of the inner one. This re-records examples/bracket only: its edges carry the fillet's new names, and three chamfers beside and across the inner fillet are refused (the blend is larger than the neighbouring faces).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 23:57:59 -04:00
bherbruckandClaude Opus 5.5 97ff7578c2 Rounded Rectangle sketch tool (#891) (#948)
* Rounded Rectangle sketch tool (#891)

A new SKETCH > CREATE command, sketch.rectangle.rounded, in the Rectangle
family: two opposite corners and a corner radius make a 2-point rectangle
whose four corners are filleted alike, as one undo step. The radius is one
parameter (one radius dimension, the other arcs equal to it), so changing it
changes all four corners. While drawing, Width, Height and Radius boxes take
typed values; typed width and height dimension the overall size between
opposite sides. Without a typed radius the corners get a fifth of the shorter
side.

Fixes #891

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Rounded Rectangle in the Simplified Chinese catalog

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-13 23:52:10 -04:00
HEOJUNFO 19bf854dc9 Loft: pair section vertices by best fit (#881) (#956)
Each section now starts at the rotation whose vertices line up best with the lead section's, vertex for vertex, instead of at the one vertex closest in direction to the lead's first. Fixes the twisted loft of matching non-convex sections; regression test loft_pairs_the_vertices_of_matching_non_convex_sections.
2026-10-13 23:50:45 -04:00