Commit Graph
174 Commits
Author SHA1 Message Date
bherbruck 736730c398 Merge pull request #122 from CypherNaught-0x/fix/loft-section-order
A loft gives the same body whatever the order of its sections
2026-10-11 07:57:49 -04:00
CypherNaugh_0x 79753040d5 Merge upstream and update curved-cut regression for fallback diagnostics 2026-10-11 13:25:06 +02:00
bherbruck 7e4dd4fd66 Merge pull request #121 from CypherNaught-0x/fix/fillet-keeps-dimensions
Fillet and chamfer keep the dimensions of the corner they replace on a virtual sharp corner
2026-10-11 07:12:48 -04:00
bherbruck f9d61590f3 Merge pull request #120 from CypherNaught-0x/feat/timeline-inspect
timeline.inspect shows a feature's editable fields; the timeline.edit format is documented
2026-10-11 07:11:49 -04:00
CypherNaugh_0x 3dc0a6024e Merge remote-tracking branch 'origin/main' into fix/loft-section-order
# Conflicts:
#	crates/engine/src/cmd/features_more_tests.rs
2026-10-11 13:03:56 +02:00
CypherNaugh_0x adb1e5fec5 timeline.inspect returns a feature's definition in the shape timeline.edit's set takes, so agents no longer guess field names: timeline.edit had no documented format ({"extent": {"distance": …}} worked, but only by trying). The new read-only command (feature: id or name) returns type, definition (the editable fields and their current values; bookkeeping and large payloads such as sketch geometry and imported STEP text are left out and listed under omitted), inputs (label, parameter name, expression, unit) and the feature's error and warning. timeline.edit's params doc points to it, and the book's Solid features chapter documents the edit format with the fields of the common features. Tests: inspect, edit by the field names it shows, inspect again (extrude, fillet, sketch); no undo step; hostile feature arguments are refused 2026-10-11 12:23:30 +02:00
CypherNaugh_0x c67ad22129 A sketch fillet keeps the dimensions of the corner it rounds: filleting the corner of a spout (a vertical riser and a horizontal top, the top's length dimensioned to the corner and the corner's height from the origin) dropped the height dimension with the corner point and left the length on the shortened straight part, so the top came out a fillet radius too long. Like Fusion, a corner that dimensions or constraints measure to now stays as a virtual sharp corner, a free point held on both curves' extensions (two point-on-curve constraints), with those dimensions kept and a line length to the corner turned into a distance from the line's far end to it; chamfers do the same. sketch.fillet and the chamfers report virtual_corners and moved_to_virtual_corner, and the viewport draws a virtual corner as an open square with dashed extension lines from the curves. Tests: the spout keeps its 80 x 100 size and both dimensions still drive it, the virtual corner survives save/load and undo/redo, a fillet with nothing on its corner leaves no point behind, and nearly straight or folded corners with dimensions never panic 2026-10-11 12:23:22 +02:00
CypherNaugh_0x 2406957039 A loft gives the same body whatever the order of its sections: the ruled side faces were parameterised from the first section to the last, and a loft cut that touches the body along an edge (a fall lofted from a shelled basin's whole outline down to a drain pad) worked for one order and failed for the other, with an Option::unwrap() panic inside the kernel's intersection or "shell not oriented and closed" from the planar fallback. The loft now builds its sections in one canonical order (first-to-last step with a positive largest component), so either order gives the same, working body. Tests: the basin fall cut, joined and as a new body in both orders and with the pad in every corner (engine), and the kernel loft's volume and cut in both orders, including large-below and planes facing down 2026-10-11 12:23:22 +02:00
bherbruck a6bb8fc924 feat: stabilize face attachments and closed spline workflows 2026-10-11 05:32:46 -04:00
bherbruck c1d819f235 feat: preserve body history and placed modelling workflows
Record ownership transfers in the timeline so earlier Join/Cut membership,
world placement, machining data and styles survive component moves, edits,
independent copying, undo and reopening. Preserve pending positions in history
snapshots and keep constructor transfers ordered during feature redefinition.

Retain shallow sheet pockets and exact planar intersection islands through
unfold/refold, with depth-aware flat previews and DXF export. Store explicit
3D wire coordinate spaces and migrate known legacy provenance without moving
saved points or analytic fits.

Infer unique eligible cylindrical Emboss targets, align Revolve manipulators
with actual extent geometry, and discard obsolete preview errors after input
correction. Certify constructed and immutable imported face styles using exact
source metadata while preserving legacy face names. Upgrade designs to format10
with bounded validation and geometry-preserving legacy migration.
2026-10-11 03:59:12 -04:00
bherbruck 0398a813c8 feat: strengthen sketches, feature editing, and independent assemblies 2026-10-11 02:44:31 -04:00
bherbruck 07932d0f42 feat: preserve modeling intent across sketches, assemblies, and fabrication
Persist typed sketch dimensions and bounded ordered configuration rules in format 8. Use exact occurrence paths and source identity for editing, movement, appearance, and mass. Preserve sheet material regions through cutting, folding, and exact DXF export; retain nested cavities and material islands.

Validation: cargo xtask ci passed, including 368 engine tests, 178 UI tests, 68 UI scenarios, 136 kernel tests, and the 10-crate wasm check. Actual preview workflows and legacy format-7 origin-corner reopening were inspected.
2026-10-11 01:28:01 -04:00
bherbruck d71bdb78ec feat: strengthen modeling, configuration, and assembly workflows
Preserve linked sketch identities through deterministic face projection and resizing. Fix origin-corner picking, persistent inline navigation, appearance editing, configuration styles, component properties, contact sweeps, sheet exports, analytic STEP offsets, and bounded rolling-ball fit accuracy.

Validated with full cargo xtask ci, native workflow screenshots, repeated enclosure replays, and analytic geometry regressions.
2026-10-11 00:37:26 -04:00
bherbruck c1b6478ab5 fix: join additive extrusions and merge coplanar faces 2026-10-10 21:31:19 -04:00
CypherNaugh_0x e575bded0e Fix construction-plane references in mirror, section, and sketches (#99)
Accept construction planes by name or feature ID, preserve parametric mirror references, and reject invalid plane inputs. Retain both mirror-plane and primitive-point regression test modules after resolving the conflict with main.

Validation: cargo xtask ci passed, including 282 engine tests, 138 UI tests, 62 UI scenarios, and WebAssembly.
2026-10-10 19:20:51 -04:00
bherbruck feaed2e3f6 Merge pull request #97 from CypherNaught-0x/fix/dimension-measured-after-solve
sketch.dimension reports the measured value after solving
2026-10-10 18:49:25 -04:00
CypherNaugh_0x c61b77613a solid.cylinder, box, sphere, torus and coil place by expressions: base, corner and center take numbers or length expressions per coordinate ("basin_width/2", "10 mm") and follow parameter changes, renames and component frames; before, a point that was not three plain numbers (or an unreadable axis) was silently replaced by the origin (or Z). The features now store the point as expressions (plain numbers still saved as numbers, so existing designs read and write the same) and evaluate it on every recompute; an unreadable or out-of-range point or axis is refused with an error. Tests: primitive points take expressions, follow parameters, renames and a moved component, and bad points are refused 2026-10-10 18:37:22 -04:00
CypherNaugh_0x ade30e9c4e Merge origin/main (with #86): sketch.dimension reports the solved value in the dimension's unit; the spout test expects the solved 30 and 60 degrees 2026-10-11 00:13:51 +02:00
CypherNaugh_0x 9c09e82cff sketch.dimension reports measured on the solved sketch: it reported the value the geometry had before the new dimension was solved, so dimensioning a 99.84 mm trough line to 100 answered measured: 99.84 and looked like the dimension had not taken. The command now measures the new dimension again once the sketch is solved (driven dimensions too), through a new solvecraft_sketch::measure_dimension that shares the solver's own measuring. Tests: lengths, a driven distance and a diameter report their solved values 2026-10-11 00:01:19 +02:00
CypherNaugh_0xandClaude Opus 5.5 1227fab860 Extrude's start_offset says which way it goes: along the sketch normal, also with direction: negative (where a positive offset starts on the far side of the sketch from the extrusion)
The params doc listed start_offset? with no description, and an extrude in direction negative with a positive start offset surprised a user. The behaviour is unchanged (an offset start plane, as in Fusion); the params doc of solid.extrude and the Extent field doc say so.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:57:34 -04:00
bherbruck 6f16b31771 Fix viewport input leaking through dialogs
Use egui pointer ownership for viewport navigation and picking, view-cube
interaction, and sketch-dimension hover. Cover scrolling, model selection,
and cube interaction under floating dialogs, including after they close.
Shield constraint glyph and move-gizmo highlights from overlapping UI too.

Clear strict Clippy warnings and use the same saved path in the recent-file
scenario on Windows and Unix.

Fixes #87
2026-10-10 17:34:14 -04:00
CypherNaugh_0x ab23fc9906 A failed MCP batch goes back to where it started however long the undo history is: rollback undid as many steps as the undo history had grown, but the history is capped at 200 steps, so once it was full a failed batch reported undone: 0 and left its sketches behind (an empty Sketch1, two sketches named TapHole). New commands edit.checkpoint and edit.restore_checkpoint (forget: true drops one) remember and restore the design, the active sketch and the undo history; batch takes a checkpoint before its first step, restores it on a failure and forgets it on success. Against an app without checkpoints batch falls back to counting undo steps 2026-10-10 17:21:25 -04:00
bherbruck acf4669f73 fix: select complete circular rims and curved surfaces in 3D
Group kernel pieces across circular rims and hidden surface seams for hovering, selection, dialogs, and measurement. Preserve physical references and cover full-item toggling and picking with regression tests.

Fixes #88
2026-10-10 17:13:36 -04:00
bherbruck d6cdec40da Fix modelling regressions and reconcile WIP UI work 2026-10-10 15:37:00 -04:00
CypherNaugh_0x 49bd89de13 Parameter and feature edits report the features they restored from the evaluation cache: setting a parameter back (or a timeline.edit back) reused the earlier results for the same inputs, so the geometry changed back but recomputed said 0, which read as "nothing rebuilt". The evaluator now counts cache hits as last_restored, and parameters.change, timeline.edit, timeline.redefine and solid.press_pull on a fillet or chamfer face report it as restored next to recomputed (params docs, the MCP set_parameter description and docs/mcp.md say what each counts). Tests: setting a parameter back and a feature edit back report restored, in the evaluator and through the commands 2026-10-10 15:36:42 -04:00
CypherNaugh_0x 90ce5261b3 An angle dimension between two lines takes the reading nearest its value and reports degrees: without text_at the angle was measured between the lines' directions, so a riser pointing up and an outlet pointing down-left (meeting at 30°, directions 150° apart) asked for 30° swung the outlet round by 120°, which the fillets between them could not follow, and the dimension was refused as over-constraining with a degree of freedom left. Two lines make two angles, θ and 180° − θ; the dimension now takes the one nearer the value (the acute one without a value), and text_at still picks a sector. sketch.dimension reports measured in the dimension's unit (deg for angles, mm for lengths) with a unit field, instead of radians for angles 2026-10-10 14:57:56 -04:00
CypherNaugh_0x 81be4e74b8 browser.visibility hides and shows sketches, bodies, construction geometry and origin items (items: ids or names, or a whole folder; visible: bool), so agents can hide helper sketches over MCP. What is hidden moved from the egui UI state into the engine session (view state: not saved, not an undo step); the desktop eye toggles and menus use it, headless renders and fit leave hidden items out, and document.inspect, sketch.inspect and ui.inspect report visibility. ui.set still accepts hiddenBodies, hiddenOrigin, hiddenSketches and shownSketches. Tests: helper sketches hide and show in the engine (hostile input changes nothing) and through MCP only 2026-10-10 14:33:56 -04:00
CypherNaugh_0x 9a22d9df92 Section Analysis takes a world coordinate: inspect.section {plane, at} places the cut at that coordinate on the named plane's axis (XY: Z, XZ: Y, YZ: X), so XZ's -Y normal no longer flips the sign; the params doc gives each named plane's normal; the result reports axis and at for named planes. at with offset, or with an {origin, normal} plane, is an error 2026-10-10 12:29:46 -04:00
CypherNaugh_0x 5d5ff44db2 Drawing connects to existing points: a new point landing exactly on an existing point (the origin too) gets a coincident constraint in the engine, for every drawing command and every caller (MCP, scripts, clicks), in the same undo step; connect: false opts out. Re-tying an existing coincident returns it ("existing": true) instead of failing; the UI's after-the-fact tie_snaps is gone 2026-10-10 12:08:19 -04:00
CypherNaugh_0x 49d6682fe7 Rectangles name their sides and corners: sketch.rectangle.two_point and .center return sides {bottom, right, top, left} (line ids) and corners {bottom_left, bottom_right, top_right, top_left} (point ids), so they can be dimensioned without a sketch.inspect round trip 2026-10-10 11:14:03 -04:00
bherbruckandClaude Opus 5.5 9d3cbb4acc Browser fold arrows: each row is one widget with a stable id, and where the click lands decides what it does (fold arrow with padding, eye, radio, or the row), so an arrow click toggles without also selecting or picking the row or starting a drag; Collapse/Expand tips; ui.at {browser|chevron} and ui.browser for automation; scenario browser_chevrons clicks every folder's arrow (document settings, named views, origin, groups, sketches, construction, canvases, components, bodies, the design) and checks the children; the inch-width units test runs now that params reads unit-less lengths in design units
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-10 07:19:58 +00:00
bherbruckandClaude Opus 5.5 fd4d38d31e Test (ignored until params reads unit-less length parameters in design units): a bare inch width stays 50.8 mm when the design goes metric
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-10 07:19:58 +00:00
bherbruckandClaude Opus 5.5 be18bd6573 Variable-radius fillets through middle points: radii at positions along the edge, straight or smooth between
kernel::fillet_variable_points(body, edge, start, [(position, radius)...], smooth): the radius law is a B-spline of the edge parameter (straight pieces, or C1 cubic pieces with Catmull-Rom slopes), and since every section's arc is affine in its radius the blend is exactly a rational B-spline (degree 2 across, the law's degree along) with B-spline contact curves on the faces. The largest radius along the edge decides whether the blend fits. Fillet type variable takes mid: [[position, radius expr], ...] and smooth; middle radii are parameters of the feature (Mid radius) and follow user parameters. Tests: straight pieces against (1 - pi/4) L (a^2 + ab + b^2) / 3 per piece, smooth against the integrated law, either end as start, refusals; engine test with a parameter-driven middle radius.

Refs #9

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 06:06:33 +00:00
bherbruckandClaude Opus 5.5 132acc9863 Perpendicular Plane (construct.plane.perpendicular {plane, line}): the plane through a straight edge, sketch line or axis, square to a plane or planar face; a line square to the plane is refused (no single answer)
Fixes #20

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 05:00:55 +00:00
bherbruck 721c8b96a4 Merge pull request #55 from bherbruck/ci/agent-managed-releases
Use agent-managed draft releases from the release branch
2026-10-09 21:17:14 -04:00
bherbruck 88b4c5b883 fix: preserve behavior under new Clippy lints
Keep the fixed ray direction explicit and simplify a single-element loop.
2026-10-09 20:18:05 -04:00
bherbruckandClaude Opus 5.5 a3a1d15d02 Test: Project and Include from other bodies (an edge, a face's loop, a silhouette, a 3D circle) follow a parameter edit of that body and are reported lost, not failed, when it is deleted
Part of #47 (components' occurrences wait for the frames fix, 62d22e2)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 00:14:00 +00:00
bherbruckandClaude Opus 5.5 61f86d073d Sketch Fillet: several corners at once (points: one radius dimension, the other arcs equal to it); a radius too large for a line corner is a clear error naming the length it needs, and nothing changes; tests for a constrained rectangle's corners (areas exact), line-arc and arc-arc corners, too-large fillets and chamfers, chamfer areas
Fixes #46

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 00:14:00 +00:00
bherbruckandClaude Opus 5.5 307ef11c0a Sketch Trim/Extend/Offset robustness: Extend reaches splines, ellipses and conics (their polyline, then pinned on the curve); Offset refuses a distance its shape can't take (a straight piece overrun by its neighbours) instead of drawing a turned-inside-out copy; adversarial tests for trims (chords, tangent touches, lone lines, collinear overlaps, splines, ellipses, hostile points), extends and offsets (rectangles in and out, slots, splines, a spike, hostile distances), each leaving the sketch sound
Fixes #45

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 00:14:00 +00:00
bherbruckandClaude Opus 5.5 ccde7a4837 Robustness runs and the kernel fixes they found
- solvecraft-cli fuzz / fuzz-one: seeded random modelling sessions (boxes, extruded profiles with arcs, revolves; joins and cuts of boxes, cylinders and spheres, fillets, chamfers, shells, holes, patterns) on a grid that makes coincident faces and tangencies common, every step checked (command ok, every body a sound solid via the new Body::validity, volume moving the right way), plus the boolean volume identity on random primitives; one process per seed with a time limit; failures minimised to scripts
- shell/offset: corners where a curved face meets another at an angle are refined onto the moved surfaces (Newton); edges that are neither lines nor arcs are rebuilt through their moved points; one shell per piece; faces that would cross say so (not supported yet) instead of leaving a cavity inside out; flat revolved faces count as planes; the unhealed body is tried when the healed one fails
- cylinders start their seams at an odd angle (grid-aligned seams broke booleans along them)
- partial revolves of profiles touching the axis build no zero-area face on the axis
- booleans on bodies in several pieces retry a piece at a time
- regression tests for each, and a fixed set of seeds in cargo test

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-10 00:04:37 +00:00
bherbruckandClaude Opus 5.5 3a416ccb7d Profiles picked by index are kept by the curves around them: add_feature turns profile indices into the curve ids of each profile's outer loop (Extrude, Revolve, Sweep, Loft, Emboss, Sheet Base, Patch), so a face sketch whose projected outline is rebuilt still extrudes the picked region; a profile that is gone is an error on the feature, never another pick
Fixes #30

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-09 22:13:01 +00:00
bherbruckandClaude Opus 5.5 334cb5f498 face.delete help text without the word fillet (MCP list_commands filter test)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 846bb112e2 Construction axes, points and planes from model geometry (SOLID ▸ CONSTRUCT): axes through a cylinder/cone/torus, perpendicular to a face at a point, through two planes, two points or an edge; points at a vertex, through two edges, three planes, at the centre of a circle/sphere/torus, at an edge and a plane, along a path; midplane, planes through two edges or three points, tangent to a face, along a path. References (doc::construct::GeoRef) keep picked faces and edges by persistent name and resolve on each evaluation (they follow the model); results live in ModelState.construct, in the component's frame; view::construction_axes/points/planes for the viewport; kernel::face_surface for a face's analytic surface
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 68271137a5 Split Face (FusionSplitFaceCommand): a timeline feature dividing the picked faces where a plane, another body's face (its surface extended) or a sketch's curves meet them, through kernel::split_faces; the solid is unchanged and the pieces take the face's name with #k. Rebase fixes: duplicate Chamfer and Combine specs; the Delete selection's face test expects Delete Face's refusal
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 3db6119cc6 Delete Face (face.delete, and faces in the Delete selection): a timeline feature that removes faces and heals the gap from the neighbouring planes through kernel::delete_faces; a picked face brings the rest of its surface (both halves of a hole's wall); face picks are stored by persistent name; a face its neighbours can't close is refused with the kernel's reason
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 9219eb5dc1 Rebase fixes: one helix re-export, a closing brace lost in tests.rs
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 141a5fea8a Modelled threads (Thread with modeled: true): the ISO 68-1 profile cut along a helix, M3 to M20, on shafts and in bores; running out past a free end of the face, stopping square at a set length. The groove is a watertight screw-sweep mesh (kernel::sweep_helix_mesh) cut by kernel::faceted_boolean (BSP on the faceted body, tolerance ladder checked by A−B + A∩B = A); the polygon rebuild welds vertices across grid cells, splits pinched loops, finds T-junctions through a grid, and takes the group's plane for a loop a hair off flat
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 1e515547d8 Boundary Fill (SurfaceSculpt): solids from the cells tool bodies enclose (inside some tools, outside the rest), each picked by a point, as new bodies or joined/cut/intersected, tools optionally removed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 0daeb0c9fc Align with snap points: from/to as {snap: vertex|edge_mid|face_center|circle_center, at}
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00
bherbruckandClaude Opus 5.5 3ee03ac597 Combine: new_component puts the result into a new component (keep_tools already kept the tools)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
2026-10-09 12:47:56 +00:00