f4d937d8a298f9bd930d8a430d82de2ec00e02bb
907
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f4d937d8a2 |
parts: a standard part's sketches are fully defined (#6376)
A standard part (a nut, washer, bolt, screw, pin or bearing) drew its sketches without constraints, so each was left with free degrees: 3 for a hex, 8 to 12 for a revolved outline. A check of a design that holds parts listed every one of them as not fully defined. The part builder now fixes each sketch's points and curves before finishing it: the outline is the part's size, and a resize builds it again the same way. Claude-Session: https://claude.ai/code/session_01XhgPofrmFDeLvMhar6Wsqw Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
91549d29fb |
Inspect: bodies measured on all cores, and once while they stay the same (#6412)
document.inspect with measure (what inspect_design and the solidcraft://design resource ask for) measured every body one after the other, and again on every call: on a 1,280-body design each call took 25 to 60 s. It now measures them on all cores, as inspect.measure does, and each body keeps its measurement in a cell shared by its clones (with the body it was measured on, as the outline box is kept), so a design inspected again measures only the bodies that changed. Placements taken as before keep their placed body's cell too. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b6a5a9166a |
perf: a pass over the joints evaluates the design's parameters once (#6418)
Every joint whose offset, angle or values are expressions is worked out again on each solve, twice per solve (re-expressing the joints, then the check that leaves out one whose expression no longer works out), and each expression that names a parameter evaluated every parameter of the design anew (`Document::eval`). The cost grows with the joints with expressions times the design's parameters: on 40 parts jointed to a plate, each joint lifted by an expression and each part carrying 13 parameters (522 in all), a joint solve took 0.55 s on main and takes 0.41 s now, a change of the lifts' parameter 1.06 s and 0.81 s (CLI, medians of 5 runs). `joints::Params` keeps the design's parameter values once the first expression names one, and `joints::expressed` takes it, so one pass evaluates the parameters once. Values and error messages are those of `Document::eval`. Test: `kept_parameters_evaluate_as_the_design_does` compares values and errors with `Document::eval`. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0063954286 |
Sketch: a sketch whose links moved far is walked there and keeps the stored sides and turns (#6420)
* Test: a label on a path follows its source far
A rectangle of construction lines placed on three points projected from another sketch, with a
centred text on its far side: moved 270 away by the parameters that place the source, its solve
from the stored sketch failed ("the sketch constraints conflict").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsiqyXWBFGTCX9J51bhysc
* Sketch: a sketch whose links moved far is walked there and keeps the stored sides and turns
Problem: a change upstream moves what a sketch projects (or the points its parameters place)
while its own values stay. The solve from the stored state then either failed ("the sketch
constraints conflict") or landed on the mirror image of a side or turn and reported it solved,
because a solve after a link move checks no orientation when no value changed: a line tangent to
a moving circle hopped a diameter to the circle's other side. And a sketch moved so was stored
only when its own values changed, so every later evaluation solved it from its first stored state
again.
Solution:
- flips_from_stored: the orientations of the stored sketch (unsigned offsets, distance
triangles, and a line's side of a circle it is tangent to) that a solved sketch turned over,
each read where the stored sketch has its points.
- solve_after_link_move: the new values solved where the links were stored, then the moved
points walked to where they are now (predictor-corrector: each step starts from the last two
solutions carried along their secant; steps halve on a failure or a flip and grow while they
solve).
- Document::solve_sketch: when a linked or fixed point moved and the jump fails or flips, the
walk; a flip the walk cannot avoid either is reported as one.
- Model::edited_sketches: a sketch whose linked points a value edit moved is stored as solved, so
the next evaluation starts where this one ended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsiqyXWBFGTCX9J51bhysc
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
6f6b64d484 |
Export Current View as DXF downloads in the browser (#3431) (#3601)
* claim: #3431 * fix(engine): use browser clock for DXF view export (#3431) |
||
|
|
52e496d017 |
Sketch Symmetry: centre a line between two fixed lines (#1796) (#3895)
* claim: #1796 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27 * Sketch Symmetry: centre a line between a face's edges (#1796) Symmetry of two edges of a face (its projected outline, whose sides run in opposite directions) about a construction line between them was refused with "neither entity can move". The pairing of the line ends by mirror image landed on main in #3029; this keeps the tool side and the tests for #1796: - The Symmetry tool and a three-line preselection take the only construction line or centerline as the symmetry line, whatever the pick order (picking it first used to mirror it out past the other line). Two points and a line take the line. Otherwise the last pick, as before. - Tests: fixed lines drawn the opposite way, pairs by mirror image (a V, end to end along the axis), every pick order, a three-line preselection, and a scenario clicking a face's edges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
16286249ed |
Extrude: a picked region expands across the reference geometry that cuts the drawn profile (#5302) (#5530)
* claim: #5302 * Extrude: a picked profile takes the whole drawn profile (#5302) A sketch on a solid's side face holds the face's outline as projected reference geometry, and that outline cuts the drawn profile into pieces of the sketch's graph. A pick by index or by point resolved against those pieces, so extruding a rectangle that straddles the face's edge took only the piece under the cursor. The pick is now expanded to the drawn profile it belongs to, so it takes every region of it. The drawn profiles are the disjoint faces of the drawn graph, so an explicit pick of a drawn hole still fills only that hole (#5176). * Extrude: a pick expands across the reference geometry that cuts the drawn profile (#5302) The expansion crossed every region of the drawn profile, so a projected curve that bounds a region inside the profile (the rim of a counterbore) was crossed too and the cut took more material than intended; now the expansion stops at that boundary. Fixes the regression in cmd::features::prism_local_tests::a_ring_cut_round_a_counterbored_hole_in_a_chamfered_disc, and keeps the behaviour of #5302 and of the #5176 invariant. |
||
|
|
19e2800472 |
Sketch on the sides of a thin extrude (#2722) (#5781)
* claim: #2722 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Sketch on the sides of a thin extrude (#2722) A thin wall's sides lie off the sketch curves (half the thickness either side, say), so no curve was under them: its side faces had no role, no exact history name, and Create Sketch refused them as "can't keep an attachment". The roles of a thin wall's sides are now `wall:<curve>:left|right` (the side of the curve it runs beside, in the direction the curve is drawn), and its open ends `wall:<curve>:start|end`. They follow the thickness and side settings and survive a save. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
a5f7f5a921 |
Batch: consecutive parameter edits can rebuild the design once (#5822) (#5839)
* claim: #5822 * Batch: consecutive parameter edits can rebuild the design once (#5822) An agent's batch of parameter edits, one parameter per step, evaluated the design once per edit: an edit early in the timeline rebuilds nearly all of it, and 21 such edits ran for minutes. engine.batch and the MCP batch tool take coalesce_parameters (default false). With it, each run of consecutive plain parameter edits (parameters.change with only a name and an expression, or with changes) is set as one parameters.change {changes}: one rebuild for the run. Any other step ends a run, so later steps see the design up to date. Every step still gets its own result (coalesced: the run's length); the run is one undo step. A run whose combined edit is refused (a new parameter, which changes can't make; a bad expression) runs step by step, so a refused edit is still named by its own step. The app's off-thread evaluation computes the run the same way. The grouping is solidcraft_engine::batch_params, shared by both runners. 21 parameter edits on a plate with 21 extruded bosses (in-process MCP, ci profile): 40.1 s step by step, 3.5 s coalesced, the same volume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Batch coalescing: say a run skips the states between its edits; test it against step by step (#5822) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a402c5e502 |
Snap points: the centre, middle and ends an edge or face offers, in the engine (#5881) (#5882)
* claim: #5881 * Snap points: the centre, middle and ends an edge or face offers (#5881) Phase 1 of #5881. `solidcraft_engine::snap::snap_points` gives the points a picked item offers as picks, from Measure's exact shapes: a round edge its centre (an arc also its ends and exact middle, joined across pieces the kernel split it into), a straight or other edge its ends and middle, a flat face the centre of its area, a cylinder or cone the centres of its ends, a sphere or torus its centre, a vertex itself. Nothing uses them yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cdde7d62b2 |
Persistent names read by one parser; four misreadings fixed (#5814) (#5966)
* doc: persistent names read as their structure; joint.inspect splits an edge with a blend face first
`persistent_name::{FaceName, EdgeName}` parse a face or edge name once (blends, inner walls,
copies, pieces, numbered pieces, `~k`) and write it back as the same text, bounded against
hostile input. A suffix after a blend's edge naming a later feature belongs to the blend face.
joint.inspect split an edge at its first `|`, so an edge whose first face is a blend
(`F4:blend:F3:+x|F3:+z#[…]|F5:-y`) came back as a cut-off blend name and a string that is no
face. It now uses `EdgeName::parse`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* doc: persistent names as typed values: features, roles, suffixes, a lexer and errors that say where
`FaceName` is a base (made by a feature in a role, a blend and the edge it rounds, an inner
wall, a body's face by index, other text) and its suffixes (copy, piece, numbered piece).
Roles are typed where the vocabulary is known (top, side:<curve>, +x, face:<k>, …) and kept as
written otherwise. The text form is lexed into tokens and read by the grammar in the module's
doc; a name that doesn't read says where and what was expected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* names: read what made a face, its kind and its feature ids through persistent_name
The places that took a name apart by hand to find the feature that made a face, whether it is
a blend, a copy, a numbered piece or a wall, or to remap feature ids on copy, now ask the typed
name (`made_by`, `split_feature`, `is_blend`, `is_copy`, `is_numbered_piece`, `is_wall`,
`features`, `map`). A name that doesn't read falls back to the old text check.
- An offset of a face that only names a blend inside it (`F2:top#[F5:blend:…]`) is no longer
taken for a fillet's.
- Copying a component remaps feature ids inside a piece's `#[…]` too.
- A shell's inner wall split by a later cut keeps the qualifier on the wall.
- joint.inspect finds the sketch curve of a copied side (`side:l1@F7.2`).
Finding a face by name (strip_piece, parent_piece) is unchanged: it reads a trailing
qualifier as the whole name's and moves once the text form says where it belongs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* engine: regression tests for press-pull on a wall that only names a fillet and joint.inspect on a copied side
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* names: an edge holds one face or more; made_by reads only the leading token
An open boundary's edge has one face and an edge several faces meet at has more, so an edge name
is a list of faces (a blend's own edge still takes two at most, as before). `made_by` reads just
the leading `F<n>:`, which every name a feature made starts with, so asking it of every face
each frame stays cheap. Names are read up to 1 MiB: a face engraved with text has piece names
past 4 KiB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* persistent_name: split into lexer, parser, write, text and tests
No code changed; callers' paths stay the same (`pub use text::*`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
3f6b9fe89f |
Timeline: reorder anyway (allow_errors) and report the features the move breaks (#5962) (#5988)
* claim: #5962 * Timeline: reorder anyway (allow_errors) and report the features the move breaks (#5962) timeline.reorder and timeline.reorder_many refuse a move after which a feature that worked would fail. Sometimes breaking a later feature is the point: an agent restructuring a design wants the move made, then the list of what broke so it can repair those features next. Both take allow_errors (default false: refused as before). With it the move is made and the reply lists broken: [{feature, error}] (empty when nothing broke); one undo puts the order back. timeline.reorder_many also takes active, as timeline.reorder does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f7233c4418 |
Render: a preview setup with exposure renders and names what it leaves out (#5089) (#6073)
render.settings.set stores exposure_ev and the environment controls, but render.preflight and render.start refused any setup with them because the CPU image executor doesn't apply them, so such a setup could never render. The CPU executor now renders it and the replies list the left-out controls in ignored_controls. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5573ae06fc |
Prevent body renames from reusing removed body names (#6042) (#6048)
* claim: #6042 * fix: reject body names retained by Remove (#6042) |
||
|
|
9d226b3535 |
Sheet metal DXF: options to leave out the bend lines and to use one layer (#4314) (#6130)
sheet.export_dxf always wrote the dashed bend lines (BEND_UP, BEND_DOWN) and split the flat pattern over a layer for each role. A cutter that cuts every line it is given cut the bend lines too. bend_lines: false now leaves them out, and layers: single writes every entity on layer 0. The defaults keep today's output. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b88673f366 |
Model cache: each sheet body is written once, not with every state (#6152)
* Test: a saved model keeps each sheet body once A sheet-metal design wrote its sheet bodies again with every later feature's state in the saved model: each feature that leaves the sheet alone (a box beside it) adds more than the whole sheet to the records. A design of many features after its sheet metal passed the cache's 32 MiB manifest limit that way and kept only some of its states (#3220). The test also opens the saved file and wants the sheets taken from it as evaluated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PsiqyXWBFGTCX9J51bhysc * Model cache: each sheet body is written once, not with every state Problem: a saved model writes every feature's state, and each state wrote its sheet bodies in full; most features leave the sheets as they were, so a sheet-metal design of many features repeated them over and over (a 280-feature part: 30.5 MB of manifest, 29.6 MB of it 69 distinct sheet records written 243 times). Past the manifest's 32 MiB limit the save thinned the cache (#3220) and opening evaluated the left-out features again, although the distinct data was small. Solution: the records keep a list of sheet bodies, each written once; a state names its sheets by their place in it, as it already does for bodies and sketches. A thinned model keeps the whole list and counts it once against its budget; the reader counts sheets with the bodies and sketches a state holds; the cache format is now /4 (an older cache is a miss). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PsiqyXWBFGTCX9J51bhysc --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c0640cb95e |
inspect.parameter_sweep: check a design across a parameter's range (#2273) (#2276)
* inspect.parameter_sweep: check a design across a parameter's range Rebuilds a copy of the design at each value of a parameter and reports what no longer holds compared with the design as it is: features that fail or warn, joint origins that are lost or turn over on their own part, sketch planes that turn over, and sketch solves that fail or fold. The design is not changed; each value's rebuild time is reported too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * inspect.parameter_sweep: params doc matches what it takes and returns `steps` takes at most 199 (200 values), as the check says; failed and warned entries carry the feature's name; a lost origin says which one; a value the parameter refuses gives {value, ms, ok: false, error}. The refusal test covers steps 200. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * inspect.parameter_sweep: Japanese and Chinese labels Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Aac7mP9rthK3WsPpPRvCsF --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
bba1953623 |
Fillet/chamfer: find each edge on its own body (#6378)
* Fillet/chamfer: find each edge on its own body A blend picked over edges of several bodies was evaluated on one body, the one nearest the first pick, and every edge was named on that body when the feature was made. Picks on the other bodies were named after that body's nearest edge and rounded it instead (or moved to it by position once an upstream edit moved them). With two stacked boxes, a height change put the upper box's picks on the lower box's rounded corners: the upper box's fillet resolved there and its own edges lost their rounds. Now each edge is named on the body it was picked on, and evaluation finds each edge's body by its stored name (a gone name stays on the feature's other bodies), groups the edges by body and blends each body. A name an older build recorded on the wrong body (the pick lies outside the box its reference recorded) is not used; the pick finds the edge. A pick an upstream edit left at an end of its named edge goes to the edge's middle, so the kernel cannot take the neighbouring edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fillet/chamfer: skip the per-body lookup when there is one body; name each body's picks in one pass Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6224cbf526 |
Thread: the refusal for a fine-only size says what to type (#4971) (#4977)
* claim: #4971 * Thread: the refusal for a fine-only size says what to type (#4971) M28 and other sizes without an ISO coarse pitch were refused as an unknown thread, with examples of other sizes. Thread and threaded Hole now say that the size has no coarse pitch and suggest two to give, such as M28x2 or M28x1.5. The coarse table is unchanged. * Thread: automatic sizing past M56 says what to type (#5055) Automatic thread sizing only knows the ISO coarse sizes up to M56, so a 75 mm shaft was refused with "no metric thread fits a 75.00 mm cylinder", although M75x2 and M75x3 work when typed. Past the end of the coarse range, the refusal now says so and suggests two sizes with their pitch, scaled to the diameter (a hole's size adds one pitch to its bore). The coarse table is unchanged. |
||
|
|
e89a8f56c1 |
MidPoint: the point goes to the line's middle, whichever is picked first (#5011)
MidPoint on a point and a line keeps the first pick still and moves the second. With the point picked first (an end of one line, then the line it should sit in the middle of), the line turned about its far end to bring its middle to the point: a line from (0,0) to (20,0) went to (0,0)-(10,10) for a point at (5,5). A point and a line now put the line first, so the point moves and the line stays; the line still comes to the point when the point can't move. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dbdeeec2e9 |
Undo restores what a hide put away (#2219) (#4780)
* Undo: what is hidden lives in the snapshot, so hiding is a step (#2219) `Session::execute` records a step only when the document `Arc` changes, and what is hidden lives in `Session::visibility` — saved with the design, but outside the document. `browser.visibility` was also declared `.noundo()`, and `Snapshot` had no field for it, so even a recorded step could not put a hidden body back: undo went to the command before the hide and left it hidden (#2219). `Snapshot` and `Checkpoint` now carry the `Visibility`, `go_to` restores it, and `Session::execute` treats a visibility change as a change of its own. That change is display-only, so it refreshes the view with a revision bump (`refresh_display`) instead of evaluating a model a hide does not touch. The command also takes `folder: "all"` (every body, sketch, construction item, origin item and joint, plus the browser folders and occurrence hides) so Show All can be one step, and asking for the state an item already has no longer reorders the hidden lists: a redundant hide is not a step and leaves no unsaved changes. * Show/Hide: the menus run browser.visibility, so the history has it (#2219) The hide and show actions wrote `session.visibility` directly instead of running the command: the body and face menus (`ui.hide` / `ui.show`), the `V` key (`context_menu::toggle_visibility`), the browser rows' eyes, the Origin rows and `ui.originAll` / `ui.originPart`. The engine could record a step and there was nothing to record — which is why the release still answered "nothing to undo" after hiding from the Browser. They all run `browser.visibility` now (the repo rule that the UI acts through commands), and `ui.showAll` is that one `folder: "all"` call, so Show All is one undo step rather than three. `docs/occurrence-workflows.md` says what visibility does in the history. |
||
|
|
1e81e65e7e |
Browser tree hover stays fast on a nested STEP assembly (#3892) (#3894)
* claim: #3892 * Browser tree hover stays fast on a nested STEP assembly (#3892) Hovering a placement row lit its whole subtree by walking it again (Session::occurrence_bodies) on every frame, and opening a STEP file makes its top placement active, so every highlight rebuild walked the whole assembly again for the active placement's bodies. On a nested assembly each walk resolves every placement below it, which cost 0.5 s or more per walk. - Session::occurrence_bodies remembers its answers per placement until the document, the evaluated model or the pending moves change (occurrence_bodies_shared hands out the shared list). - The highlight looks hovered bodies and faces up by name instead of scanning the hovered list once per body. |
||
|
|
9227685483 |
Smooth sketch dragging on large imported faces (#3222) (#3480)
* claim: #3222 * Sketch drag: project a face sketch's links once per live gesture (#3222) Every live sketch.drag frame refreshed the sketch's links before solving the drag. For a sketch on a face that projects all of the face's edges again: 115-460 ms per frame on the top face of a 600 x 500 mm plate with 120 holes imported from STEP, against about 1 ms for the drag's own solve. The model the links come from cannot change during the gesture. The session now remembers the sketch, document and revision a live frame left. The gesture's next frame reuses the links while nothing else has run: any other command (undo included) or any other change to the design makes the next frame project them again. A test counts the projections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sketch drag test: drop an unread assignment (#3222) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
22190e703c |
Revolve, sweep, loft and hole from a nested STEP part's face join and drill it (#3223) (#3987)
With an opened STEP assembly's imported root active (it owns no body), a Join revolve, sweep or loft from a sketch on a nested part's face made a separate body, and a hole at the points of such a sketch found no body to cut. A Join with no targets now takes the body its profile sketch is on, through the same active scope as an extrude, and a hole counts as a cut for the rule that a cut from a component with no body works on every body. Refs #3223 |
||
|
|
f7b08ab8c4 |
Dimension values: #name = value defines a parameter too (#2371) (#4212)
* claim: #2371 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27 * Dimension values: `#name = value` defines a parameter too (#2371) `#width = 40` in a dimension's value now works like `width = 40` (#2916): it adds the user parameter `width` and dimensions with it. On `main` the `#` made it an ordinary expression that failed to parse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ae0b1edeac |
Coil: a section from a sketch profile (#4236) (#4239)
* claim: #4236 Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: store an optional sketch and profile selection Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: sweep a sketch profile along the helix Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: accept a sketch and profile selection Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil dialog: pick a sketch profile for the section Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: cargo fmt Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: refuse a loaded sketch coil that also sets the primitive section A saved design could add section, section_position or start_angle to a coil that already has a sketch; eval ignored them instead of refusing the contradiction as the command does. Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil: assert the kernel refusal and exercise hostile profile selections Co-Authored-By: Claude Code <noreply@anthropic.com> * Coil dialog: keep the sketch-profile check after rebase Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Format Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
e2b8bf5a05 |
fix: copy a pattern's features in timeline order, not in the order they were picked (#4873)
A circular or rectangular pattern of a hole and the chamfers on its edges failed with "no edge near ..." when a chamfer was picked before the hole: the chamfer was copied first, onto a body that had no hole there yet. Co-authored-by: Nathalie Schönwetter <nathalie@cfw-ag.ch> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
2fd7b6f08d |
Measure: a face or edge picked by its point alone (#4099) (#4114)
* claim: #4099 * Measure: a face or edge picked by its point alone (#4099) inspect.measure (and the MCP measure tool) refused a face or edge item without an index ("missing field `index`"). Face and edge items are now read as body + point with an optional index: the point finds the face (the same lookup face picking uses) or the nearest edge within the pick tolerance, before the selection is built. A missing body or a point on no face or edge is a clear error. Explicit indices still work. |
||
|
|
59795872f6 |
Extrude: document which way a positive taper goes (#4098) (#4115)
* claim: #4098 * Extrude: document which way a positive taper goes (#4098) A positive taper slopes the walls outward (the far end is larger along the extrusion direction), a negative one inward. Say so in the params doc, the JSON schema and the guide, and pin the sign with a test. |
||
|
|
37b7b0bd3e |
Offset free-form curves and mixed chains: trim folds, round corners (#2149, #2676, #2761, #3159, #3828) (#4156)
* Offset of free-form curves: trim folds, round chain corners Offsetting a spline by more than its tightest bend failed with "offset spline has no interior certificate correspondence": the plain offset folds over itself there and a single linked spline cannot follow it. A chain of Bézier curves (any imported SVG outline) was refused outright. Such an offset is now the parallel boundary: the plain offset of every piece, joined round each corner by an arc about it, trimmed where it folds or crosses itself (a sharp corner is left), each kept run fitted with an open spline and certified to the offset tolerance, and the whole proven free of crossings. Tiny kinks are joined directly; zero-length Bézier handles (infinite curvature at the end) are replaced by their chord within a quarter of the tolerance. A single curve whose plain offset is smooth still gets the linked offset; the trimmed or chained result is ordinary curves, not linked to the source, so the command adds no distance parameter for it. * Offset chains mixing lines or arcs with free-form curves A chain offset read every curve with Sketch::exact_segs, which only covers free-form curves, so a chain with straight or circular pieces (a text glyph, an SVG path with L commands) failed with "offset source changed shape". Read them with exact_pieces, which gives lines and arcs from their analytic form; the trimming already handles those pieces. |
||
|
|
7e24f0e35a |
Sketch: a group too large to solve says so, not "conflicts with" nothing (#5280) (#5347)
The solver takes at most 4000 unknowns in one connected group, and a pattern joins all its copies into one, so a hexagon patterned 17 × 20 (4000+ unknowns) failed as "that would over-constrain the sketch (conflicts with )", naming no constraint. The solve report now carries the size of the largest group it could not take, and the failure says how many unknowns are joined, the limit, and that fewer copies or several patterns or sketches stay within it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e726f780d1 |
Sketch: the first dimension scales only a connected drawing (#3786) (#3815)
* claim: #3786 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Sketch: the first dimension scales only a connected drawing (#3786) The first length dimension scaled the whole sketch about its origin, so a second rectangle that nothing links to the first was resized and moved too. Now the sketch scales only when its curves form one chain, joined by shared points or constraints (the origin counts as a point); separate shapes keep the previous behaviour: the dimension sizes the shape it measures. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
b201c283c7 |
Model-cache tests trust an unknown source commit, so they pass without git (#4221)
Fixes #4045 |
||
|
|
a2ae3ffbe0 |
Sketch drag: keep the far end of a tangent line still (#4925)
Dragging one end of a line tangent to a circle or arc let the solver move the
line's other end too, sometimes far ("goes flying"): only the dragged point was
held, and the least-squares step spread the change over the free points. The
drag now first tries holding the line's far end as well, and falls back to the
old solve only when that can't be met. Dragging the end of a tangent line to
(5, 12) moved its other end 0.399 mm on 0.19.0; it now stays put.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
2a0096fe4a |
Release 0.22.0 (#6344)
* Release 0.22.0: version bump, design format 35 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Release 0.22.0: tests no longer expect the folded render and material changes to be pending Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cc9d76cd86 |
Tapered cut: run on less, then start on the face, when the run on closes a small corner (#4825) (#4858)
A cut starting on a face of its target runs its tool on behind the start plane with the tool's slope. A taper that widens the pocket narrows the tool there, and a corner arc smaller than the run on times tan(taper) closed: "the taper closes the profile", though the pocket itself only grows. The run on is halved, quartered, then dropped (the tool starts on the face with its straight foot) until the tool builds. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c61ffb54a8 |
Insert Part: M2, M2.5, M14, M24 and M30 screws, bolts, nuts and washers (#2091) (#5194)
The fastener tables stopped at M3 and M20. The socket head cap screw (ISO 4762), hex bolt (ISO 4014), hex nut (ISO 4032) and plain washer (ISO 7089) tables, and the coarse pitches they use, now have M2, M2.5, M14, M24 and M30 too, in size order. Set screws and dowel pins are unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ab889a8fff |
History: the sketch at the marker is shown while it's the current step (#5202) (#5204)
* claim: #5202 * History: show the sketch at the marker while it's the current step (#5202) A sketch a later feature uses is hidden, so moving the History marker onto it changed nothing in the view. The sketch immediately before the marker is now drawn even when consumed or hidden; its stored visibility is untouched, and earlier sketches, hidden components, groups and the Sketches folder still apply. |
||
|
|
4e6939b5cf |
Extend: extend a free arc in place and add the line the preview draws (#4615) (#4618)
* Slightly fix the extend tool * Extend: add the line the preview draws, and stop an arc on another curve sketch.extend reported "nothing to extend to" whenever nothing stood in the line's way, even though the viewport previewed a line past its end; with nothing in the way the extension now runs as far as the preview shows, on the same measurement, so the click adds what was drawn. An arc extension that ran into another circle or arc refused the target when its crossing already belonged to another curve's end (a shared crossing point of two circles): that point is now joined instead, so the extension closes onto the geometry that is there. --------- Co-authored-by: SolidCraft Dev <dev@solidcraft.local> Co-authored-by: n0tfr3shm1lk <n0tfr3shm1lk@ukr.net> |
||
|
|
8cbde001ef |
Front … Isometric View commands, Ctrl+1…7 by default (#5057) (#5058)
* Ctrl+1…7 turn the view to the standard views (#5057) Front, back, left, right, top, bottom and isometric, handled with the other view keys beside Ctrl+8: animated like the view cube, reserved so no command can take them, and ignored on the flat pattern page. * Front … Isometric View are commands, Ctrl+1…7 their default keys (#5057) The standard views become seven commands (view.front, view.back, view.left, view.right, view.top, view.bottom, view.isometric) whose default shortcuts are Ctrl+1…7, instead of keys the window handles itself. They show in Keyboard Shortcuts, can be moved to other keys or cleared, and the digits can be given to other commands. In the app they turn the view as the view cube does; headless they set the session camera, keeping target, distance and projection. |
||
|
|
ad8b2f91bb |
File: a saved model whose booleans retried is taken on opening (#5286) (#5334)
A design file keeps its evaluated model so that it opens without
evaluating the timeline. A feature whose boolean needed a retry records
it, and the kernel's BooleanDiagnostics leaves `rejected` out when it is
empty but has no default for it when read, so reading those records
failed ("saved model: diagnostics"), the saved model was refused, and
the design was evaluated on every open (17.6 s for the reporter's
17-feature design, 1.2 ms with this).
The reading side now takes an absent list as an empty one, without a
change to the kernel crate.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
9919825ea3 |
Expand All and Collapse All in the Browser (#5190) (#5335)
Open or close every Browser folder from the design or a component menu, including folders that start closed. Display only: not saved, not an undo step. |
||
|
|
97e0ff9050 |
Sketch: a face seen edge-on projects as one line end to end (#5375)
A face orthogonal to the sketch plane projected every edge onto the same
line: coincident and overlapping pieces, one per notch, that the face
boundary check then refused on the next evaluation ("boundaries cross,
touch or have ambiguous topology") and marked lost.
A link layout whose straight pieces all fall on one line is now one line
between its two farthest points; vertices along it can be projected one
by one. The face-boundary and loop-identity checks skip such a silhouette,
which encloses nothing. Coincident lines are laid out once.
|
||
|
|
d61dff69a6 |
Exploded views export as an animation (#3713) (#3744)
* claim: #3713 * Exploded views export as an animation (#3713) motion.export_animation takes an exploded view (view: id|name) as well as a study or a joint: the parts come apart at a steady pace from the assembled placement to where the view shows them, and back together with back: true (exploded at the middle frame). 41 frames by default, 81 with back. The home view frames the parts both assembled and exploded. As with explode.show, the design keeps its placements, its history and its saved state. explode.show and the export share the moves; moves a design file gives with no finite translation are left out. * Mechanisms skill: an exploded view's default frame count --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
8c853fff3e |
Press Pull: route curved faces to Offset Face (#2873) (#3463)
* claim: #2873 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Press Pull: route curved faces to Offset Face, with an arrow (#2873) Q sent every non-edge selection to Extrude, which takes only profiles and planar faces, so a hole's wall or a boss's side was refused. The engine's solid.press_pull already offsets curved faces; the window now does too: - Q on a curved face opens Offset Face; its Faces input takes curved faces. - Q with nothing selected opens Extrude as before, and a first pick of a curved face or an edge reopens Press Pull on it (Offset Face or Fillet). - Offset Face's arrow follows a curved face's outward normal where it was picked, so it can be dragged like an extrude's. Fixes #2873 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0520910bfd |
Control: name the token file and spot a stale one when a call is unauthorized (#4664) (#4670)
* claim: #4664 * Control: name the token file and spot a stale one when a call is unauthorized (#4664) When the app refuses a token that the MCP client read from a token file, the error now names that file, when it was written and how long ago, without printing the token. The app also writes `<port>.pid` beside the token file (its process id and, on Linux, its start time) and removes both at exit; when that process has exited, the error says the token file is stale. The process check runs only after a refusal, and the token file itself stays one line, so existing clients keep working. Document force-quit leftovers and the MSIX AppData redirection. * Bench assistant: read the token out of the new ClientToken Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
46f24f2394 |
Save the browser's view with the design, and an option to hide the origin by default (#1849)
The Origin folder's eye, a finished sketch's Show Profile and Show Dimensions, and body locks move from UiState into the design's saved visibility (origin_hidden, hidden_profiles, shown_dimensions, locked_bodies), so each tab keeps its own and they survive closing the design. The Sketches folder uses the saved browser folder (c0/sketches in hidden_folders); ui.set/ui.inspect keep showOrigin, showSketches, hiddenProfiles, shownDims and lockedBodies. Preferences > Design > "Hide origin by default": a design that never toggled its Origin eye follows the preference. New fields are a pending format change (format/pending/browser-view-state.rs). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4f4b1fc2d2 | Sketch: select edge midpoints for horizontal and vertical constraints (#5159) | ||
|
|
9e6e0b8df1 |
Render workspace: complete experimental ribbon, resources and effects integration (#5493) (#5494)
* wip(render): reconcile completed material and image pipeline source with 0.20 (#5493) Source-only integration reference for staged review, not a merge-ready aggregate. Donor: 0xlillith/solidcraft@aea20b0115. Upstream: |
||
|
+33 |
b7b25f9e2b |
Render workspace: first basic delivery to main (#6050)
* Normalize UI container violation paths across platforms (#5599) Report repository-relative paths with forward slashes so diagnostics and the existing nested-package regression assertions agree on Windows and Unix. Keep the container detection rules unchanged. Validation: official ui-containers gate and all seven focused xtask tests passed; rustfmt and diff checks passed. Only xtask/dependencies were compiled; no application build or heavy tests. (cherry picked from commit |