mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
develop
51 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
81e55b5443 |
New kernel cases go to solidcore's corpus; the rig's case list only shrinks (#11646)
* New kernel cases go to solidcore's corpus; tests/kernel-cases/ only shrinks tests/kernel-cases/CASES lists every rig and ladder case there is today. `cargo xtask lists` fails on a case directory it does not list, saying where a kernel case goes instead (solidcore's crates/solidcore/tests/kernel_cases/, exported with `cargo xtask kernel-cases --export` or written by hand), and on a listed case that is gone. AGENTS.md, CONTRIBUTING.md, the tests skill, the rig README and EXEMPT say the same; a new command gets an EXEMPT line naming where it is tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ * CONTRIBUTING: the kernel-cases paragraph ends before the naming corpus one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ea772a0438 |
Merge develop into main: SolidCraft on the SMLib kernel (v1.1.0-alpha) (#11351)
* UI: rim and wall picking tests for a circle of arcs that is one wall A circle drawn as two half arcs now extrudes to one full-turn wall with its seam edge, and each rim is one closed edge (tangency-labs/smlib#67). The tests keep their intent on that body: a rim clicked once is one item, measured and highlighted whole and picked from either side; a wall picked either side of its seam is the same surface, lit whole, and toggles off from the other side; every face is its own patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fillets: a later round at a corner where earlier rounds meet blends them again with it The owner's ruling of 2026-10-30: a later fillet on an edge at a vertex where earlier rounds already meet in a mitre makes the corner rounding all of them at once makes, one smooth patch, the mitre gone. A body made by constant-radius rounds keeps what they were made on and which edges they rounded at which radius; a later round reaching such a corner blends the earlier rounds' edges (from the earliest of those meeting there on) with the new edges in one SMLib blend of the body before them. The result's history is put over the body the later round was given: an earlier round's face is that face trimmed anew (its name kept), the corner patch and the new rounds are the later fillet's. A picked mitre seam has no round of its own: it is refused unless a new edge of its radius meets it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: a later r3 round at an r2 mitre, through the Fillet feature, makes the all-at-once corner Two box edges rounded r2 in one fillet, then the upright edge at their mitre r3 in a second fillet (tangent chain on, so the seam comes with it): 10 faces, 7928.743041 mm³ as fillet_radii of the box gives, the r2 rounds' names kept, the r3 round and the corner patch the second fillet's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Kernel: a body's record of rounds goes with its history, and can be read back The rounds that made a body are over the body its history is over: a new history (an operation carrying a step's result back to its own operands) leaves the record out, so a later round never reads one history against another body. Body::rounds gives the record and Body::with_rounds_of takes one back, checked (edges of the body before them, a fillet's radii, the body's history over that body), for a saved model to keep it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Model cache: a body made by rounds keeps them, so a later round blends them the same after opening A design gave another body when opened from its model cache: the rounds a body was made of lived only in memory, so a later round at their mitre was built sequentially (or refused, as too large) after opening and blended with them when evaluated anew. The saved model now records, per kernel body made by rounds, the body they were made on (a place in the bodies, stored once though no state holds it) and the edges with their radii; reading it attaches them back down each chain, and a thinned model keeps those bodies with the bodies made from them. Model cache format 7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: a corner patch of rounds 2/3/4 takes no more than the strips leave in its block three_rounds_at_a_corner bounded the patch's extra cut at 3 mm³ beyond the strips, a figure with no derivation; SMLib's all-at-once body at radii 2, 3, 4 takes 3.099 (V 7878.579574 against strips 7881.678). That body is the strips' outside the block its patch lies in: its rounds are the strips' cylinders ending at a's section y = rc, b's x = 20 − rc and c's rails where they meet a's and b's (the patch's four corners, now asserted), so the patch takes at most what the strips leave in that block (11.86 at 2/2/3, 32.69 at 2/3/4). The lower bound is that block; the upper one, at most the strips' volume, stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: the io crate's hand-built saved models list their rounds, as format 7 records do Model cache format 7 ( |
||
|
|
b818d7043d |
docs: Nommage des copies de motifs et de miroirs dans la référence de modélisation (#11160)
- Les motifs rectangulaire et circulaire prennent bodies et nomment leurs copies source (2), (3)…, en sautant les noms pris - Motif de features : nouveau BodyN, comme le miroir ; noms lus dans created_bodies - Miroir d'une copie : suffixe ajouté ; body.rename pour des noms stables ; renvoi à #8194 Co-authored-by: P'tite Tête <asso@ptitetete.com> |
||
|
|
dadefec702 |
Joint dialog: Find Limits fills the limits from where parts collide (#5968) (#7047)
* claim: #5968 * Joint dialog: Find Limits fills the limits from where parts collide (#5968) joint.find_limits sweeps each value of a joint both ways from where it is, on a copy and with its own limits left out, and answers where the parts it moves first touch another part, where a closed loop stops closing, or that nothing is met within a turn (angles) or the parts' reach (distances). Edit Joint's Limits section gets a Find Limits button that runs it on the joint as the dialog has it, off the window thread, and fills the rows. joint.sweep now tells touching bodies apart from the other bodies of the same two parts: on the Hinge Assembly sample the pins touch where it starts, and the plates used to pass through each other unnoticed. * find_limits: read the session only * find_limits: a joint with nothing to meet, edits during a search, tiny ranges * find_limits: keep touching at the start per pair of parts Per body, the Hinge Assembly sample's plates, which only come flush in the end plane the two leaves share, stopped the search. The leaves never pass into each other: the sample's answer is a whole turn. * find_limits: touching boxes cost no collision work; stale results and rounding * find_limits: each pair of bodies of resting parts held to its own depth; loops without bodies * find_limits: a 32-bit budget it can pass, a repaint with the result, a bad form says so first * find_limits: each pair of resting bodies gets its own margin * find_limits: Arabic, Hebrew and Vietnamese strings (complete catalogs on main) * find_limits: search the joint with Edit Joint's side picks Edit Joint now keeps the joint's sides as picks in its inputs, and its OK commands compare them with what it opened on. Built without them, the commands Find Limits applies first asked for the sides again, so every search failed. --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
927a5c3733 |
PR rules: a title has to make sense to someone who never saw the chat (#11202)
* PR rules: a title has to make sense to someone who never saw the chat Agents write PR titles from inside their own context, or copy the issue's title, so a maintainer reading the list can't tell what a PR does. AGENTS.md, the contributing skill, CONTRIBUTING.md and the PR template now say who a title is for and show an unclear and a clear one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K14jA88xLsBfMkWo4Da5uh * PR rules: write PR text in plain, literal English, and use a clearer example title The first example still said "that window", the kind of pointing word the rule bans. The new rule takes the useful parts of Simplified Technical English without its approved-word list, which doesn't fit CAD terms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K14jA88xLsBfMkWo4Da5uh --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a735620aa8 |
Circular pattern: the axis origin takes length expressions and follows them (#10852) (#10855)
* claim: #10852 * Circular pattern: the axis origin takes length expressions and follows them (#10852) solid.pattern.circular with axis {origin, dir} kept its origin as plain numbers, so a pattern about a parameter-driven point stayed where the point was when the pattern was made: the seed moved with the parameter and the copies did not. Expressions were refused (#248). The pattern's origin is now a point of expressions, as a Mirror plane's origin is: numbers or length expressions, evaluated on each rebuild, and renamed with their parameters. Plain numbers are saved as numbers, as before. timeline.edit sets pattern.origin as expressions, and editing the pattern in its dialog keeps them. One pending format change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
e16647a63f |
MCP: measure summary and body_topology faces: cylinders for small verification replies (refs #3380) (#9853)
* MCP: measure summary and body_topology faces: cylinders for small verification replies (refs #3380) `measure {"summary": true}` replies one short object per body (name, volume_mm3, bbox, faces, closed) with body_count and total.volume_mm3, as a tool and as a batch step. `body_topology {"faces": "cylinders"}` replies only the cylindrical faces, the kernel's split halves merged by radius and axis line into {radius, origin, axis, extent, names}, and no edges. Both defaults are unchanged. The merge uses only what model.faces and model.edges already reply. Measured on a 13-feature enclosure, body_topology is 74 KB and measure 1.1 KB per body; agents filter them with scripts, and seven of 29 benchmark runs asked for exactly a cylinders-only listing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * MCP: keep body_topology's cylinder merge next to check_parameters' without a name clash Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: bherbruck <tenbee91@gmail.com> |
||
|
|
c528964363 |
MCP: check_parameters tries parameter values and puts the design back (refs #3380) (#9910)
A new tool that checkpoints the design, sets every value in one parameters.change, reports measure, errors and cylinder axes, and restores the checkpoint, so the loop set_parameter / measure / body_topology / set back costs one call and one rebuild. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
9d8ab72da7 |
Ribbon tooltips: help for every remaining toolbar command (#9060) (#9868)
The last 106 commands on the backlog get the two-sentence help under their tooltip, in English, German, Japanese, Chinese, Arabic, Hebrew and Vietnamese: Surface, Sheet Metal, Plastic, Inspect, Construct, Configure, Parameters, Library, View and the few single ones (Bill of Materials, Frame Member, Pattern on Path, Convert Mesh, checkpoints). With that the backlog is empty, so it goes: the test is now every_toolbar_command_has_help, and the docs that mentioned the backlog say so. The help tests use sketch.drag, which is not on the ribbon, as the command without help. |
||
|
|
3b77567932 |
Docs: joint flip needs x for point origins; checkpoints are single use, 16 kept (#9746) (#9844)
joint.create's flip text and the assemblies guide said two origins at one point with the same z stay as they are; a point origin without x takes its x from z, so a part turned about z turns back. Say so, and point to giving x on both origins or joint.as_built. edit.checkpoint and edit.restore_checkpoint now say that restoring or forgetting uses the checkpoint (and later ones) up and that only the last 16 are kept, and the "no checkpoint N" error says why. The editing and mechanisms skills no longer read as if a checkpoint can be restored twice. Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
60b9e9d188 |
Edit Joint: show the two sides and pick them again (#9686) (#9692)
* claim: #9686 Co-Authored-By: Claude <noreply@anthropic.com> * Edit Joint: joint.edit takes the two sides joint.edit accepts a and b in the form joint.create takes them (read by the same origin_param), so a joint can move its attachment without being deleted. A side left out keeps its place. The two sides must stay on different occurrences, checked as joint.create does. Limits, values, expressions and the name are kept; a relation's values start again where its parts are. joint.edit gets an input contract and reply type (schema/joints.rs), and leaves schema/unconverted.txt. Co-Authored-By: Claude <noreply@anthropic.com> * Edit Joint shows both sides and picks them again The Edit Joint dialog now shows the two sides in the same selection inputs as Joint, each pre-filled with the face or edge of its own component under the origin. OK sends joint.edit a or b only for a side picked again, so an untouched side keeps its origin exactly. Co-Authored-By: Claude <noreply@anthropic.com> * Edit Joint: a moved side turns the part as joint.create would joint.edit kept the joint's old angle when a side changed, so the part could turn by a half turn about its axis when the new origin's x lay the other way. When a side moves and no angle is given, the angle is worked out again as joint.create does (the least turn from where the part is now); a relation's angle is its tilt and stays. The mechanisms reference says joint.edit takes a and b. Co-Authored-By: Claude <noreply@anthropic.com> * Edit Joint: pre-fill sides by name, send the angle only when changed A side shows the face or edge its snap names, the piece nearest its stored pick when the name has several, else the one nearest that pick; it no longer searches outward from the frame origin, which could show the plate beside a hole for the hole's wall. A point or frame side shows nothing. OK sends the angle only when the user changed it, so a side moved without it turns the part as joint.create would. Co-Authored-By: Claude <noreply@anthropic.com> * Edit Joint: a moved side leaves an angle expression alone (#9686) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
bd1525dcfb |
Expressions: hyperbolic functions sinh, cosh, tanh and their inverses (#9332) (#9334)
* claim: #9332 * Expressions: hyperbolic functions sinh, cosh, tanh and their inverses (#9332) `sinh`, `cosh`, `tanh` and `asinh` take a unit-less number and give one, as `exp` does; `acosh` refuses x < 1 and `atanh` refuses |x| >= 1, worded as `asin` and `ln` refuse what lies outside their domain. They are in the expression help (`FUNCTIONS`, the parameter autocomplete), the hostile-expression fuzz tokens, the parameters and equation-curve guides, and the checking skill's list of functions. Horn flares (Salmon's hypex `cosh h + T sinh h`), catenaries and the tractrix can now be written as they are stated instead of expanded into `exp`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5e0e168ae9 |
Properties: moment of inertia, principal axes and first moments of a part or assembly (#8095) (#8103)
* claim: #8095 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Properties: moment of inertia, principal axes and first moments of a part or assembly (#8095) component.properties and occurrence.properties report each closed body's inertia tensor about its centre of mass, and in total the tensor about the total centre of mass (world axes, g·mm²), its principal moments and axes, the first moments, and with `about: [x, y, z]` the tensor about any point. The tensor comes from each body's welded export mesh, tessellated once per body, scaled to the measured mass and turned by each placement; bodies add by parallel axes. The maths is a new crates/geom inertia module. The Properties window shows the first moments, the tensor, the principal moments and axes, and the view marks the centre of mass with the principal axes while it is open. Inspect → Measure → Properties now opens that window (it ran the command and showed nothing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Properties: Copy matrix, and the whole design's card titled just Properties (#8095) Copy matrix, under the tensor, puts it on the clipboard as three lines of tab-separated values at full precision, in the units shown. The card for the whole design is titled Properties; one opened on a component, placement or bodies keeps its name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Properties: measure beside the window, so a large assembly doesn't freeze it (#8095) Properties ran on the window thread: 18 s on a 144-body design, with the window answering nothing meanwhile (#5203). In the desktop app the card now measures on a copy of the session on a worker, the way #4614 made agents' measures work, says "Measuring…" until the reply is in, and Close drops it. A quick measure still shows at once; headless, tests and the web measure in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9bcb2ffe68 |
Drawings: first landing from the drawings branch (#8249)
* Drawing sheets: sizes, the ISO 5457 border and its marks (#3875) (#5108) * Drawing sheets: sizes and the ISO 5457 border (#3875) A sheet is a paper size and an orientation: ISO A0 to A5, Letter, Legal, ANSI A to F, architectural A to E1, or a custom size (finite, above 0 and at most 5,000 mm a side), landscape by default and either way for every size. A border profile is the rule for its border, and layout() works it out for a sheet: the frame inside the margins, and the reference fields with their labels. The ISO profile has a 20 mm filing margin on the left and 10 mm elsewhere, a 0.7 mm frame, fields of 50 mm out from the centre lines with the corner fields taking what's left, rows lettered from the top without I and O (AA, AB and on past Z), columns numbered from the left, and an A4 sheet labelled on its top and right only. A profile can divide the frame equally instead, or have no grid. The records sit in geom, next to the section records, so saving, rendering and export can all use them. Nothing is saved or drawn yet: there is no command and no format change. Margins that leave no drawing space, values that aren't finite, fields outside 1 to 1000 mm, more than 200 equal fields and unknown JSON fields are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing sheets: ISO 5457's centring and trimming marks (#3875) A drawing border profile now also holds the grid reference border and the centring and trimming marks, either of which can be left out. The ISO profile follows ISO 5457:1999 (read in its identical adoption, IS 10711:2001): - the grid reference border 5 mm outside the drawing border on every side, with the field labels between the two; - four centring marks on the sheet's centre lines, from the grid reference border to 10 mm inside the drawing border; - at each corner a trimming mark of two overlapping 10 x 5 mm rectangles, in the margin outside the grid reference border. The labelled sides are now a rule worked out for each sheet: ISO 5457's (the top and right of A4, all four sides of the others) or fixed sides. So one profile labels every size correctly. validate() checks what doesn't depend on the sheet, for when a profile is read; layout() calls it, then checks the rest against the sheet. Refused: - a grid reference border that isn't finite, is negative or is wider than a margin; - a centring mark that isn't above 0, would reach past the sheet's centre lines, or is on a drawing border those lines miss; - a trimming mark that isn't above 0, is wider than it is long, is wider than the margin left outside the grid reference border, or is longer than half the sheet's shorter side. The grid rule's JSON is now externally tagged ("none", {"from_centre": {"field_mm": 50}}), as the sheet size is, so an unknown field is refused in every form; the internally tagged "none" ignored one. Nothing is saved yet, so the shape change costs nothing. The names follow the glossary, and ISO 5457's "drawing sheets": the module is drawing_sheet (was sheet, which means sheet metal elsewhere here), DrawingBorderProfile and SheetLayout (were BorderProfile and BorderLayout), sheet_border and drawing_border (were paper and frame), and Sheet::size_mm (was paper_mm). The first part of S1 is still a draft, so the renames are made here rather than in a commit of their own. The extra centring marks ISO 5457 asks for on sheets larger than A0 aren't here yet. Still nothing is saved or drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing sheets: a lighter built-in border, the filing margin a choice (#3875) Cameron's decisions after seeing the sheet in the app, 2026-10-23: - No trimming marks in the built-in profile: the filled corner blocks take a lot of ink. trimming_mark_mm stays, so a profile can still ask for them. - Centring marks stop at the drawing border: from the grid reference border to the drawing border, never into the drawing area (on A4 they crossed the tables). centring_mark_mm keeps its meaning, how far a mark reaches inside the drawing border, and now takes 0, the built-in value, as grid_border_mm already takes 0 for "on the drawing border". A mark of no length (a reach of 0 with no grid reference border) is refused. - Margins of 10 mm all round. ISO 5457's 20 mm filing margin on the left, for drawings that get bound, is a named choice: filing_margin, off when not given. On, it stands in for margins.left_mm, which is kept, and still checked, for when it's off again. The 20 mm is FILING_MARGIN_MM, and validate() and layout() both take the margins from one helper, laid_out_margins(). A3 landscape is now 400 x 277 at (10, 10), with eight 50 mm columns. With the filing margin it is 390 x 277 at (20, 10) again, the same as a 20 mm left margin set by hand. A profile with all three choices on (ISO 5457's full border) lays out exactly as the built-in profile did before, and the tests keep the old hand values for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing W23a workshop plate oracle fixtures (#4950) (#4962) * test(oracle): reviewer-aligned parameterized workshop plate fixtures (#4950) Independent analytic W23 expected values and seven verified models, including four R10 corners and the 2 mm tangent top chamfer. Document accepted Drawing states and commit regenerated docs/oracle.md from successful workflow run #29 (90/90 pass). * fix(oracle): correct W23 chamfer centroid and clarify drawing session ownership * Paper scene: plain records of what is drawn on a sheet (#4890 A1) (#5513) geom::paper holds what a sheet shows as plain data: groups of strokes and fills, each group tagged with the object that drew it (an opaque id) and its kind, with one validation pass over a bounded record count. Seg2's checks and the coordinate rule move into profile.rs so section and paper share them, and drawing_sheet takes its side and line limits from paper, so each limit is written once. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing sheets: centring marks across the whole margin (#3875) (#5554) The built-in sheet's centring marks now run from the trimmed sheet's edge to the drawing border. From the grid reference border, as before, a mark was the same length as a grid field mark, and on A3 it sat on one (the sheet's centre falls on a field edge), so it no longer read as a mark once the grid reference border's outline is left out (Cameron, after the prototype on #3875). - DrawingBorderProfile.centring_mark_start: sheet_edge (the built-in's, and when not given) or grid_border (ISO 5457's figure 5); ISO's full border in the tests asks for grid_border. - A mark still needs a length: one that stops on the drawing border needs room outside it on the side it starts from, every margin for sheet_edge, the grid reference border for grid_border. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing sheets: no centring or trimming mark longer than the largest sheet's side (#3875) (#6410) A border profile capped every length at 5000 mm (the largest sheet's side) but two: a centring mark's reach inside the drawing border and a trimming mark's length. A file could give either a huge whole number, such as 18446744073709551615. The read's number check passes whole numbers, so the profile took it as 1.8e19 mm, and saving wrote it as a decimal the next read refused: a drawing that saved but didn't open again. K2's fuzz test of drawing files (#3890) found it. Both now take the cap the margins have, `MAX_SIDE_MM`, in `validate`, so the file is refused when it's read. Layout's own checks against the sheet are unchanged, so a 5000 mm mark still validates and is refused only on a sheet too small for it. Tests: the hostile-profile test refuses 5000.000001 mm and 1.8e19 mm for each mark, and takes 5000 mm on its own; 4 planted faults (each cap removed or made one short) are all caught. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Bill of materials types: phantom, reference, purchased and inseparable (#3874) (#5107) * Bill of materials types: phantom, reference, purchased and inseparable (#3874) A component's bill of materials type is a part property (`bom_type` in its metadata, set with component.metadata.set and read by component.metadata, as part_number is), so older releases keep it and no format change is needed. It isn't a design property: the Design Properties dialog doesn't list it and document.metadata.set refuses it. A word this build doesn't know reads as normal and is kept. assembly.bom counts by type: a phantom has no row and what it holds counts where it is placed, in the same rows as the same parts there; a reference counts nowhere, and nothing inside it does; purchased and inseparable components are one row, and in parts only an inseparable component's purchased parts are listed too. Rows of one part number but different types are different rows. Rows and children carry bom_type. With no types set the rows are as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Glossary: bill of materials and bill of materials type (#3874) The bill of materials types are a new idea, so the glossary gets their term, with the five types, and "normal" gets its fourth meaning in the table of words with more than one meaning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bill of materials types: a plain line for each (#3874) The guide's table and the glossary entry say what each type is for in plain words: something you make, something just to group, something to use as a reference, something you buy, something made as one piece. The names stay the five standard ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bill of materials: kits fold together only when their contents' types match (#3874) With group_children, two kits with one part number share a row when what they hold reads the same. That reading (kit_key) compared each child by part number and count, but not by its bill of materials type, though rows themselves are told apart by type. So a kit holding a made bolt and a kit holding a bought one, both KIT, folded into one KIT x2 row listing the first kit's bolt twice: the other type was lost, and which one depended on creation order. 0xlillith found it in review of #5107. kit_key now includes each child's type. Equal part number, type and contents still fold. Test: two KIT kits each holding a BOLT fold into one row of two; once one bolt is purchased they are two rows, each listing its own bolt. Planted fault (the type left out of the key): caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890) (#7098) * File format families: drawings share the design format's versioning (#3890) The format code works for a family of files: its own tag, its own numbers and its own folder of change files, with one reading of a file's format, one refusal of a file a newer build saved and one upgrade for every family. Designs (solidcraft/N) are one family and keep everything they had: FORMAT and FORMATS are as they were, and pending_names, format_string, version_of and read mean what they did. Drawings (solidcraft-drawing/N) are the other, with format 1 as their first record and each later change one file in crates/doc/src/drawing/format/pending/, as design changes are. Nothing saved changes. A design tag never reads as a drawing's or the other way round, and a file that names no format is a design from before format strings, never a drawing. Every refusal names its family; a design's read word for word as before. A design listing a pending change this build doesn't have is now refused as a newer build's before its content is checked, as a newer number already was, so a newer build's fields never read as damage. The list reader is take_names(v, key, noun), the shape #4811 uses for its own list. For the maintainer, the release step changes: scripts/fold-format-pending.sh now folds every family that crates/doc/build.rs lists (FAMILIES), each into its own next number: designs into crates/doc/src/format/v<N>/ as before, drawings into crates/doc/src/drawing/format/v<N>/ from 2. With nothing pending in a family it says so and moves nothing. Its test folds a drawing change into v2 beside the design changes. docs/releasing.md, CONTRIBUTING.md and AGENTS.md say so. build.rs lists each family's changes from its own folder, each folder's versions contiguous from its own base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing record: format 1 of the drawing family (#3890) A drawing is now a record of its own (crates/doc/src/drawing.rs): its name, the designs it draws (sources: an id and the design file's path, relative to the drawing's folder as a linked design's is), its properties under the design metadata's rules, its units (each design's own, or one length unit of the expression unit table), its content language (a BCP 47 tag) and its sheets. A sheet holds its paper, a copy of its drawing border's profile, the resolution its pictures are rendered at (300 dpi unless it says, 25 to 2400), the component it documents and whether it is left out of the sheet count or of printing. This is format 1 of the drawing family; each later kind of record adds one file in crates/doc/src/drawing/format/pending/. The rules: one id counter for every object, raised above the largest id when a file is read and never rewound; every reference is an id that must resolve, or the file is damaged and refused, never patched; 1 to 1,000 sheets, at most 64 sources, names of 1 to 256 bytes, paths of 1 to 4,096 bytes without NUL. The same drawing always saves as the same bytes. Drawing::from_json reads at most 16 MiB of text, then runs the family's format check, the number check, the upgrade, the typed record (unknown fields refused everywhere) and Drawing::validate. The number check is now one function every family calls, Family::check_numbers, told which field holds other tools' data; a design's refusals read as before. Nothing saves or opens a drawing yet: the container is the next step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing ids: random and collision-free from format 1 (#3890) A drawing's objects now take random ids, the form jyviko chose on #3890 from #1017: no counter, so objects added on two branches never share an id and never edit one line. The allocator is one module for drawings now and designs later (#626 Phase 3), crates/doc/src/ids.rs: an IdSource gives nonzero u64s by splitmix64 from a seed, each checked against the ids in use and drawn again when it is one, or when it is 0. A run never draws a number twice, so at most one draw per id in use comes before a free one, and a seeded run over a file the same seed made still gets a new id. IdSource::seeded gives the same ids for tests and replays; IdSource::from_entropy seeds a session from the standard library's hash keys, the time and a count, with no new dependency. In a file an id is 13 characters of Crockford base32, lowercase (0000000000001 to fzzzzzzzzzzzz): ids::encode writes it and ids::decode reads only that spelling, case-sensitive, so an id has one form to search for. Anything else, 0 included, makes the drawing damaged. The drawing loses next_id (a file that holds one is refused), and Drawing::new and allocate_id take the IdSource. Messages name ids as the file writes them. The component a sheet documents stays its design's own number until designs move over. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing record: a drawing too large to reopen isn't saved (#3890) from_json reads at most MAX_DRAWING_BYTES (16 MiB), but to_json had no limit. Other tools' properties are kept as they came, and indenting them can grow a drawing read in at about 4 MB past 16 MiB (two million zeros in one array), so the next read refused what the save had just written. 0xlillith found it in review of #3891. to_json now returns a Result and refuses JSON longer than a read takes ("drawing too large to save (at most 16 MiB)"), so every drawing saved opens again. Nothing writes drawing files yet, so the error reaches the future file writer before it publishes anything. Test: that case is refused, and a smaller compact drawing saves and reopens unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890) K3 of the drawing storage plan (W11.2): the file a drawing is saved in. Nothing writes one from a command yet; opening and saving come with K5. - A drawing container is a design container holding `drawing.json` (Deflate, as `document.json`) in place of `document.json`, and `container.json` says so: {"content": "drawing", "format": "solidcraft-container/1"}. A design's `container.json` is as it was, with no `content`. The plain JSON form is the drawing's own text. Both give the same bytes every save, so a drawing can live in version control as text. - `io::file_kind` tells a design from a drawing by `container.json` or by the JSON's format. The design reader refuses a drawing ("this file is a SolidCraft drawing, not a design: open it as a drawing") and the drawing reader a design; plain JSON only asks when it isn't a design, so reading a design costs nothing more. A container's content this build doesn't know, or a drawing container that also holds a design's `document.json`, is refused. - `read_drawing` (bytes, either form), `read_drawing_file` (one seekable handle, the design reader's checks) and `write_drawing_container`; `DRAWING_EXT` (`scdwg`) and `is_drawing_path` beside the design's. - The container writer's set-up and finish, the file opening and the container sniffing move into one function each, which designs and drawings both use. The container's own checks (budgets, entries, the ZIP directory) are the design container's, word for word. Tests: a drawing round-trips through both forms byte for byte, and the container holds exactly `container.json` and `drawing.json`; `file_kind` on both forms of both kinds; each reader refuses the other's file; unknown content, a stray `document.json`, a missing drawing, an unknown container version and a damaged drawing refused; both forms from disk, and a directory refused; mutation fuzz of both forms (an error or a drawing, never a panic). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Paper scene: SVG of a sheet, its lines, arcs, cubics and fills (#4890 A3a) (#7032) The first export of a paper scene, as SVG first (jyviko and 0xlillith on #4890), from the research in the plan's §16. A3b adds ellipses, hyperbolas and Béziers above degree 3, which this refuses as "not supported yet". - `geom::paper::Prepared`: writers read a scene through `Scene::prepare`, which checks it first, so none reads one unchecked. Clipping goes there too, once (A8). - `geom::paper::arc_pieces`: an arc's ends in pieces of at most a quarter turn, the last its own end. A half turn drawn from its ends is ill-conditioned: rounded to the nanometre (six decimals of a millimetre) its centre moves up to 1e-2 mm, where a quarter turn's stays under 1e-6 mm. - `io::paper::paper_svg`: one user unit a millimetre, the size in mm on the root; y flipped by writing the height less y, so the file's numbers are page coordinates and an anticlockwise arc has sweep flag 0; lines `L`, arcs `A` (never large-arc), cubics `C`, parabolas `Q`; fills black and even-odd; one path per item; groups as `data-object` and `data-kind` (object ids can't be XML ids). No background, so cutters see only the marks. - Numbers are whole nanometres (six decimals of a millimetre): no exponent, no "-0", the same bytes every time. They are the writer's own state too, so a joint is written once and a closed path ends exactly on its first point. Numbers are written straight onto the document, with no text made per number. A width under half a nanometre is written as one, and a piece too short for the grid is merged into the next, both counted in the report; a path's last piece has no next, so it is kept however short (unless it has no length). Output over 64 MiB is refused. Tests: the A3 drawing border's whole document; quarter, 270°, clockwise and whole-turn arcs, and a circle of two halves giving the same four quarters; the quarter-turn boundary both sides; cubics and parabolas, and the conics refused; the number format; a closed path whose start and end round apart; a sliver merged; an open path's short last line and arc kept, and one of no length dropped; read back through our own SVG reader (lines exactly, the arc as an arc); the scene's JSON mutated into the writer (never a panic). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing W17a: view definitions as data (#4816) (#4817) * claim: #4816 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing views as data: named views, scales and the map onto paper (#4816, W17a P1) What a drawing view is, before anything draws it (geom::drawing_view): - NamedView: the six principal views and the isometric views from the four upper corners, each an exact basis (a Plane whose normal points back at the viewer), never taken from a camera, whose pitch clamp would tilt a top view, with no -0 to write out; `iso` reads as the front-right one. - basis_looking: any other view from its direction, an up and an origin (paper x = direction x up, paper y = x x direction), refusing a zero direction, an up along it, or an origin that isn't finite or within 1e12 mm. W07's {direction, up, origin} view reads through it. - Scale: paper : model as two positive numbers, read and written as "1:2", from 1:100000 to 1000:1; Scale::fit picks the largest ISO 5455 preset (50:1 to 1:10000) at which a view fits a space (W07's "fits the page" default), compared as size x paper <= room x model so an exact fit at a reduction isn't lost to rounding. - ViewFrame: a view's basis, scale and place on paper, the point of its plane at `centre` landing at `position_mm`. Its paper map moves the view's curves and their accuracy onto paper (paper error = scale x model error); `boundary` maps a box of its plane onto paper from its four corners. In geom, where Seg2 lives: - Similarity2 and Seg2::similar (new): scale, turn and shift a segment exactly, an arc staying an arc. Similarity2::new refuses a scale that isn't above 0 and works the turn out once, not per point; - Seg2::map_controls: a segment's control points mapped by any function. It was doc's sheet::map_controls; its four callers in doc use the method now, unchanged. CurveAccuracy::scaled gives a curve's accuracy at another scale. Tests: each named view's basis and viewer side, and the same bases from a direction and up; a 50 mm cube in every principal view at 1:1 and 1:2, and in isometric (edges 50 x sqrt(2/3), outline 50 x sqrt(2)); the view's centre on its position (W07's 100 x 20 front view on A3 at 2:1); scales read, written and refused; the fitted scale, an exact fit at 1:5 and ISO's smallest; no -0 in any named view's axes; an origin refused by its own message; hostile bases, centres and positions; every segment kind moved by a similarity, and the similarities refused; scaled accuracy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drawing views: the bottom view pinned, square near parallel, bounded scales and boxes (#4816) From DevonHope's review of #4817: - The tests pin all six principal views exactly. The direction-and-up test uses literal ups, so a bottom view turned half round now fails it. - basis_looking squares x to the direction before normalising. Near the 1e-9 threshold the normal was up to 8.8e-9 rad off; now it is about 1e-16. - Scale::fit fits nothing for a size or room that is negative or not finite. A size of 0 still sets no limit that way. - Each side of a scale is 1e-6 to 1e6, so a scale writes at most 49 characters. - ViewFrame::boundary returns a Result. It refuses a box that is inside out or not finite on paper, instead of dropping a corner. - paper_map, boundary and seg_to_paper say which coordinates they take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing W18 S2: title blocks as data (#3875) (#3877) * Title blocks: what a cell shows and where its text goes (#3875) geom::cell holds a title-block cell's content: parts of text and fields (component, drawing, sheet, revision or prompt, by key, with an optional unit and decimals kept as written for the resolver to check), at most 64 parts and 4096 bytes of text, with no control character but a line break. Beside it are a resolved value (live, overridden and drifted, or missing, with "-" for each part that couldn't be read and why), an override with the value it replaced, and the projection a drawing's views use (first or third angle), which the title block's symbol will read. A fit warns and never cuts: a count of characters, not bytes, with its own count for text in Chinese, Japanese, Korean or Yi characters. The cell text rule places a label and a value: the label's baseline 1 mm plus its cap height below the cell's top, the value box under it inset by 1 mm, one line centred in the box, and wrapped or several lines down from its top at the font's pitch, with a warning when the last falls below the box. Wrapping by width, width warnings and glyphs a font lacks need the text measured, so they wait for the paper scene's text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Title blocks: definitions and their place on a sheet (#3875) geom::title_block holds a title block's definition: its size (1 to 1000 mm a side), its outline and cell line widths, and up to 64 cells. Each cell has a snake-case name of its own, a rectangle measured from the block's lower-right corner, an optional one-line label with its cap height, and a value: text from cell content, with its cap height, alignment and fit, or the projection symbol, which reads the drawing's projection and is never fixed in the block. validate() checks a definition on its own, as when one is read: cells inside the block and not overlapping (touching is fine), label and value cap heights that fit, and the content's own limits. layout() puts the block on the drawing border's lower-right corner of a sheet, refusing one that doesn't fit, and gives each cell's rectangle, its label's place and its value box by the cell text rule. On A3 landscape a 180 x 54 mm block runs from (230, 10) to (410, 64); it fits A5 landscape and every larger sheet, but not A5 upright. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cell text: a value box lies within the paper's coordinates (#3875) place_lines took any finite, ordered value box, but centring one far out (min x 1e308, max x 1.1e308) overflows: the anchor came back infinite, with no warning. 0xlillith found it in review of #3877. The paper scene already bounds every coordinate it holds by MAX_COORDINATE_MM (twenty times the longest sheet side). That limit is now public, and place_lines keeps its box inside it, so its anchor is always finite: one rule for what a paper coordinate may be. Tests: the hostile-box test refuses both overflow cases from the review and a box just past the limit on each side, and places lines in a box the size of the whole paper limit. The fuzz test's numbers now include values past the limit (1.1e5, ±1e308). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Title blocks: padding from the lines' inner edges, one label rule, nothing unread (#3875) A cell's text is padded from the inner edge of its lines: each side of a cell moves in by half the line drawn on it (the outline's on the block's edges, a cell line's inside), so a wide outline never covers the text. Before, the padding ran from the line's centre. The label's one-line rule lives with the cell text rules (`cell::check_label`), where parts lists will find it. The cell types nothing read yet (`Override`, `Resolved`, `State`, `Problem`, `Reason` and `MISSING`) wait for the resolver that will read them. The tests check the exact thresholds inside the lines (a label that leaves no room, a value that just fits), pick each of the lines, alignment and wrapping on their own so every combination comes up, and lay fuzzed blocks out on every standard sheet, either way, with and without the filing margin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Title blocks: the drawing border's line, descenders, room where the block sits (#3875) From DevonHope's review of #3877: - Each cell's lines are clamped to the inner edge of the drawing border's line. That line runs along the block's right and bottom edges, and along any other edge it reaches, as when the block fills the border. Aabb2::intersection, the 2D twin of Aabb3's, does the clamping. - layout checks each cell's room again where the block sits (place_cells, which validate runs at the origin), so rounding at the real corner can't turn a cell inside out. - The cell text rule counts descenders, at 3/10 of the cap height (ISO 3098's lettering B). They may reach into the padding under a value box, never past it to a line. place_lines warns Height when they would, and validate asks it whether one line fits. - The module doc no longer links to a private function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455) (#2457) * Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455) crates/geom/src/hlr (L0): prepare once per body, then hidden_lines for a scene of placed bodies. Edges and face silhouettes are projected from their exact curves, split where projections cross, and each piece is classified by rays toward the viewer against the trimmed faces: visible, hidden or explicitly uncertain, never guessed. Planes, cylinders, cones, spheres and tori are supported whether stored as analytic records, revolutions or spline patches; freeform faces are reported and make what they could hide uncertain. Screen geometry is Seg2 with the W01a accuracy vocabulary: exact lines and conic arcs where the projection is exact, bounded chords otherwise. docs/drawing-hlr.md holds the contract and its limits. * geom/hlr: handle the brep records main gained in #3093 Main's exact model storage (#3093) added Curve::SurfaceIntersection, Surface::PlaneThrough and Surface::PlacedRevolution, and the HLR matches on the records no longer compiled. A plane through three points is a plane: it is trimmed and intersected as one, and a box stored that way draws the same bits as one with axis-stored planes. The procedural intersection curve and the placed revolution (whose affine placement may scale it unevenly) are refused by prepare as not supported yet, until the kernel adapter handles them. The records' positions move with the body's centre like the others. * geom/hlr: pad plane faces' boxes, so a plane record off by rounding still hides Plane faces took the box of their boundary chords unpadded, flat along the normal. When the plane record and its edges disagree by rounding (a unit cube's top plane one ulp above its edges, a 100 mm cube's 1e-11), the ray toward the viewer reached the plane just outside that box, the slab test culled the hit, and the back edges came out Visible with no doubt. Every face's box is now padded by 1e-6 of its size, as curved faces' boxes already were. Reported by DevonHope on #2457. * geom/hlr: limits from review: trim samples, scene extent, crossing pairs, surface groups From DevonHope's review of #2457: - Options::trim_samples must be 1 to MAX_TRIM_SAMPLES (4096). - Placed bodies must stay within MAX_COORDINATE (1e12 mm, as section and brep bound stored numbers). Placements 1e200 apart made the scene's size infinite and every tolerance taken from it with it, so everything came out Visible. - The crossing search counts its candidate pairs before it starts and meets each pair only in the first grid cell the two segments share, instead of keeping a set of the pairs met, which could outgrow wasm's 4 GB before the 500 million pair budget tripped. The same pairs in the same order. - prepare groups faces on one quadric or torus by comparing each face with the faces of its kind whose size agrees to the tolerance (sorted), not with every face; at most 50 million comparisons. prepare_cancellable polls a cancel callback between edges, faces and comparisons. * geom/hlr: boundary chords on a grid, and no fine pass over coincident curved chords The cost items of DevonHope's review of #2457: - Every ray hit asked how far it lies from its face's boundary by scanning all of the face's chords (4096 per circle; a plate's face with 256 holes has about a million). The chords are now bucketed on a uniform grid over the boundary's box, cells at least as long as the longest chord, and the test looks only at the cells around the point. It finds exactly the chords the scan finds. - Chords of two curved curves that lie on each other end to end (aligned holes seen along their axes) were cut into 16 by 16 pieces and intersected, to find crossings that are only noise within the curves' tolerance. They are now skipped; visibility changes where a coincident run starts or ends, and chords that only partly overlap still find those. - Fixtures: a plate with holes, coaxial cylinders seen along their axis, and 300 placements. * geom/hlr: pad boxes by the body's coordinates too; coincident chords only to rounding Review of the previous commits found two holes: - The padding grew with the face's size, but rounding grows with where the body was modelled: a 0.001 mm cube 1e7 mm out, its top plane one step (1.9e-9 mm) above its edges, still had its hit culled. The pad now adds 1e-12 of the body's offset. - Two chords were taken for coincident when their ends lay within each other's sag, which in a coarse scene can hide real crossings: an arc can bulge across a line 1 mm off its chord twice. Only chords that coincide to rounding are skipped now; identical projections, the costly case, still are. * geom/hlr: coincident curves meeting at shared sample points do not cross Timing the review's scenes showed that skipping coincident chords was not enough: chords of two coincident curves also touch end to end at every sample point they share, and each touch was a crossing to refine and split at. 64 coaxial cylinders seen along their axis gave 8.3 million crossings (19 s), 64 cylinders one behind another along the line of sight 4.2 million (214 s; before these commits the crossing budget refused it). A touch at a shared sample point is now dropped when the two curves also coincide, to rounding, on both sides of it. Where a coincident run starts or ends one side differs, and the touch is kept. * geom/hlr: the own-face margin allows for rounding where a body was modelled The far 0.001 mm cube of the previous commit showed a second effect of records rounded at large coordinates: its top plane, one ulp (1.9e-9 mm) above its edges, hid the top edges themselves, because a ray leaving an edge met its own face 3.2e-9 mm along, past the own-face margin of 1e-6 of the face (1.4e-9). The margin now also covers 1e-14 of the body's offset (about 45 ulps of its coordinates), which only matters for parts far smaller than their distance from the origin. * geom/hlr: no parity test for a hit on the boundary, where it is uncertain anyway * docs/drawing-hlr: the fixtures of the review's second round --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> --------- Co-authored-by: CamWight <91156642+CamWight@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Benny1496 <163034806+Benny1496@users.noreply.github.com> Co-authored-by: Mateusz Sych <151755914+SychPL@users.noreply.github.com> Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
1e3cb3e36e |
Motion interference: a check cut short is not clear, and budget_s gives it time (#7855) (#7934)
* claim: #7855 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ * Motion interference: a check cut short by its work budget is not clear (#7855) motion.interference stops when its work budget runs out (truncated) but answered clear: collisions.is_empty(), so a check that reached only the first steps read as a pass: a gear pair checked for 14 of 61 samples was "clear". clear is now true only when every sample was checked and none collide. The budget is an argument of interference_within, so a test can cut a check short: B slides up to touching A, a full check is clear, and the least work that checks a first step stops after 1 of 16 samples with clear: false (it was clear: true before). The guide and the mechanisms skill say the same. The issue's second request, a budget_s on motion.interference, is not here. Refs #7855. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ * Motion interference: budget_s gives a check the time it needs (#7855) The work budget is fixed, so a gear pair drawn in detail was cut short after a few samples, or refused before its first one. motion.interference now takes budget_s (seconds, above 0, at most 86400, as inspect.interference allows): the check then runs until that time instead of the work budget, looked at before each sample, and echoes budget_s in its answer. Without budget_s nothing changes. The refusal before a first step now names the budget that ran out and suggests budget_s. Tests: given a time, the smallest work budget (which checks no step) no longer stops the check, through motion.interference too; budget_s 0, -1, 1e9 and "soon" are refused. Fixes #7855. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
03b8f4138b |
New designs save as .sc; .solidcraft files still open (#7325)
* claim: new designs save as .sc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * New designs save as .sc; .solidcraft files still open and keep their name on Save DESIGN_EXT is now "sc"; DESIGN_EXTS lists sc, solidcraft and solvecraft and every open path (dialogs, drag-drop, recent files, CLI, MCP, links, the web picker) accepts all three. Save of an opened .solidcraft file writes that file; Save As, autosave, recovery and crash reports use .sc. Windows, macOS and Linux register .sc alongside the older extensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update UI tests for the .sc default extension Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c0a0da225a |
Ribbon tooltips: help for the Solid > Modify tools, and new toolbar commands must have it (#2758) (#7134)
* Ribbon tooltips: help for the Solid > Modify tools; new toolbar commands need it (#2758) Writes the two-sentence help for the 16 Modify tools that had none (en, fr, ja, zh), and adds a test that fails when a toolbar command has no help; the 190 older ones are on a backlog that only shrinks. AGENTS.md, CONTRIBUTING.md and the Adding a command guide say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ribbon tooltips: the help test reads the ribbon as the toolbar lays it out (#2758) Review: commands placed by a workspace section without .at(...) were not checked; 79 of them join the backlog. Physical Material's help no longer promises the centre of mass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ribbon tooltips: French help uses the glossary terms (#2758) Matériau, Apparence and Scinder, as rlartot asked in review; Delete Face says the hole stays open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ribbon tooltips: the help rule fits the commands skill's size limit (#2758) AGENTS.md became a router to skills on main, so the rule lives in skills/solidcraft-commands/SKILL.md, kept under its 3600-character limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ribbon tooltips: Arabic, Hebrew and Vietnamese help; the backlog follows main (#2758) Arabic, Hebrew and Vietnamese are complete catalogs now, so the 16 texts come in those too. Twelve toolbar commands that landed before the rule join the backlog; the Render commands, which left the ribbon, leave it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
98abb249c4 |
Cylinder: place by distances from reference edges, like Hole (#6380) (#6381)
* claim: #6380 * Cylinder: place by distances from reference edges, like Hole (#6380) A Cylinder standing on a flat face can take up to two straight edges of that face as references, each with the distance of its base centre from the edge's line, measured in the face's plane, as a Single hole does (#37). `solid.cylinder` takes them as `references: [{edge, distance}]` with a numeric `base`; the edges are found again by their persistent names, else their points, so the cylinder follows them when the part changes, and each distance is a parameter (an input of Inspect Feature). A cylinder without references stays at its base, as before. In the Cylinder window, started on a picked face, Reference 1 and 2 take the edges, each with its Distance measured from the pick, so it works with the mouse alone; the canvas shows each distance with a box to type it in, and Edit Feature picks the edges again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cylinder: Edit Feature shows each reference edge input once (#6380) A face selected when Edit Feature opened the window had already added the reference inputs; the edit added them a second time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cylinder references: the measured-once cost test's cylinder has none (#6380) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cylinder: pick the face it stands on in its window, then the edges (#6380) The Cylinder window took its place only from a face selected before it opened, so started first it stood at the origin and offered no reference edges. It now has a Face input before Reference 1 and 2, as the Hole window does: start Cylinder, click the face, click the edges, OK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Glossary: reference edge, and "Reference" among the words with more than one meaning (#6380) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cylinder edge references: the Cone dialog (#6250) gets the references field too The Cone primitive landed on main after this branch began; its dialog initialisers lacked the `refs` field the Primitive kind gained here. The dense form check for Cylinder fills only its fields: its face and reference edges place it, which the reference tests cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cylinder: declare references in its contract; reference-placed cylinders are not placement-independent Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
ebb6cc53ab |
Crash reports: capture native crashes on macOS (#2113) (#6593)
* claim: #2113 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Crash reports: capture native crashes on macOS (#2113) A signal handler for SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP and SIGABRT writes the same <journal>.native.json as the Windows exception filter: the signal, the faulting module and offset, the thread and a frame-pointer walk. The next start reports it instead of "an unclean exit". The signal then goes on to the previous handler or the default action, so macOS still writes its own crash report. native_crash_macos.rs joins the crash-safety skill's list of files that may use unsafe (no safe API can catch these signals). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e063a7778e |
Axes and points from circular edges and round faces (#3128) (#4610)
* Axes and points from circular edges and round faces
In 3D, a round edge or face stands for its circle wherever a command
takes an axis or a point.
Revolve, Surface Revolve, both Circular Pattern dialogs, Plane at Angle
and Move -> Rotate take a circular edge (through its centre, along its
normal) or a cylinder, cone or torus face (its axis). The features keep
the pick as a model reference, so they follow the model when a parameter
moves it. Straight edges work as before; flat faces are refused.
Every 3D point input takes a circle's centre: construction points (which
follow the circle), To Object, patch points, hole placement, and Move's
pivot and points. Hovering shows the axis as a dashed construction line
or the centre as a star, and the dialog's chip names what the pick
stands for ("centre of edge 10", "axis of face 3").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Format the round-pick hover check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Selection chips: name a round pick as a circle centre or axis
The chip has room for little more than the body and a word or two:
"Shaft · circle centre", "Shaft · circle axis", "Shaft · face 2 axis",
and "Shaft · point" for a point of the model.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move: a circle's centre in a placed component is where the component shows it
Move's pivot and points read a picked circle from the model in its
component's own frame, while the pick is in the world. They now read it
from the world state (and, editing a Move, from the state before it,
placed the same way), as the viewport and the other round picks do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Plane at Angle on a planar face about a model edge: test that it follows both
Upstream #1956 made a planar face a base for Plane at Angle; this branch makes a model edge an
axis. The rebase keeps both in one PlaneRef::AtAngle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
3a504f5347 |
Agents: AGENTS.md becomes a router to focused skills, with a staleness check (Refs #6439) (#6444)
AGENTS.md drops from 16.8k to 3.7k characters: the hard rules, each naming the check that enforces it, and a table of what to read for each kind of work. The rest moves into eleven new skills; the five existing ones stay. cargo xtask skills fails when AGENTS.md or a SKILL.md cites a path, an xtask command or a skill that does not exist, when the router table and skills/ disagree, or when the router or a new skill grows past its size cap. It runs in cargo xtask ci and the repository checks job. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
309eda87a2 |
Agent skill for reproducible bug reports (#3983) (#3984)
* claim: #3983 * Agent skill for reproducible bug reports (#3983) skills/solidcraft-reporting has an agent gather the version, steps and exact error, reproduce the problem in a fresh design (a command script or one batch with rollback off), shrink it while it still fails, check the numbers with measure and a screenshot, keep private designs out, search for duplicates, and write the report in one shape: Reproduced on, Steps with a minimal script, Expected, Actual, Attachments. Listed in skills/README.md and docs/mcp.md; the bug template's steps field points agents to it. The skill-name check now covers its files. * Reporting skill: GitHub takes a design only as a .zip |
||
|
|
157c0d68d0 |
Sketch Dimension: point/circle to circle or arc edge (#4064) (#4065)
* Sketch Dimension: point or circle to the edge of a circle or arc (#4064) Adds PointCircleDistance and CircleCircleDistance (edge-based, like #1863): solver, constructive seed, drawing, command wiring, tests, UI scenarios and docs. Saved designs gain two constraint kinds, in one pending format file. Fixes #4064. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Sketch Dimension scenario: pick the circle at its top edge (#4064) The bottom edge sits under the Dimension tool's status hint in the scenario window, so the second click never reached the tool. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
13c4a62a71 |
fix(skills): correct statements that no longer match 0.21.0 (#5899)
On 0.21.0 the facts test failed on rectangle symmetry, which now works on two sides. Other statements in the modeling and mechanisms skills also no longer match: edge picks have a distance limit, concentric circles extrude as a ring, a hole sketch below a part drills into it, slot ids count on from earlier curves, shell refuses curved blends and lofts, measure and inspect_design grow with the body count, and joint.sweep needs occurrences with against. Each correction has a test in test_facts.py. |
||
|
|
c48c1a09d3 | fix(skills): quote editing description for valid YAML (#4389) | ||
|
|
be685d064f |
Add a contribution skill for first-time contributors (#5851) (#5852)
* claim: #5851 * Add a contribution skill for first-time contributors (#5851) skills/solidcraft-contributing walks a new contributor from checking their tools through fork setup, choosing and claiming an issue, a focused change with checks and screenshots, and review. The skills index lists it and the install commands point at tangency-labs/solidcraft. * Skills install: copy the modelling skills only The contributing skill is read from its link when someone wants to contribute, so the install commands no longer put it in every project's global skills folder. |
||
|
|
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> |
||
|
|
807e367bdb |
Hole: place a Single hole by distances from reference edges (#37) (#2931)
A Single hole can take up to two straight edges of its face as references,
each with the distance of the hole's centre from that edge's line, measured
in the face's plane. `solid.hole` takes them as `references: [{edge, distance}]`;
the edges are found again by their persistent names, else their points, so
the hole follows them when the part changes, and each distance is a
parameter. A bad reference (no edge there, a curved edge, one square to the
face, parallel edges, a negative distance, a centre off the face) is refused.
In the Hole dialog, Reference 1 and 2 follow the face, each with its Distance
row; the canvas shows each distance as a dimension from its edge to the hole,
with a box just past the edge to type it in. Picking an edge measures its
distance (and its box takes the keyboard), typing one moves the hole, and
moving the hole measures them again. Edit Feature picks the edges again with
their distances.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
0dffb206b1 |
Assembly: ground the first part; drag free joint chains (#2804) (#2805)
* assembly(feat): ground first parts and drag free joint chains Ground the first top-level part when it is created and let face dragging reposition free occurrences or any member of a floating joint chain in one undo step. Preserve exact repeated-placement frames, joint values, modifier selection and Escape cancellation. Keep the accumulated fixture, viewport layout and documentation integration fixes from the existing PR. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(fix): update movable fixtures for first-part grounding Accept the documented grounded override in component creation, explicitly free movement fixtures, and keep linked clipboard pastes movable even in an empty document. Cover first and reused linked pastes, default insertion grounding, and single-step undo in the existing link tests. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): keep the motion-cost crank free Unground the first component in the document-level joint-face measurement fixture so its motion samples can rotate after first-part grounding. Preserve the rotation and shared-measurement assertions that caught the Linux CI failure. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): free the frame placement fixture Create the frame-member placement fixture ungrounded before moving it above its root sketch. Keep its world-space member assertions while respecting first-part grounding in the frame tests added on main. Signed-off-by: Karl Von Richter<karl@embeint.com> --------- Signed-off-by: Karl Von Richter<karl@embeint.com> |
||
|
|
36a354e3a7 |
Agent chat: serve the bundled skills' reference through an MCP tool (#4966) (#4970)
* claim: #4966 * Agent chat: serve the bundled skills' reference through an MCP tool (#4966) The bundled skills start with "read reference.md", but SolidCraft's own chat turns the agent's file tools off, so its agent could not follow them. The MCP server gains a read-only `skill_reference` tool that returns a skill's documents from copies built into the binary. It takes a skill name and a file name from a fixed list, never a path, and refuses an unknown skill or file with the available ones. The skills point to it, and docs/mcp.md and skills/README.md describe it. |
||
|
|
382680d8e8 |
fix(skills): refresh Boolean checking facts that fail on current release (#1835) (#2093)
* fix(skills): refresh Boolean checking facts that fail on current release (#1835) * fix(skills): restore warning coverage in test_run and use present tense in Boolean facts * fix(skills): the slot keeps its side at zero since the solver takes the shortest step The upstream solver now takes the shortest step that fits (#2060), so the slot that other constraints also held slides to the origin when the distance reaches 0 and keeps the side it was drawn on when the value comes back. It no longer comes back mirrored, so the trap and its test state the current behaviour. --------- Co-authored-by: jeremymiribung-blip <jeremymiribung-blip@users.noreply.github.com> |
||
|
|
e27d43553c |
Sketch Dimension: line to circle/arc edge (#1803) (#1863)
A line and a circle or arc can now be dimensioned to the circle's edge (ConstraintKind::LineCircleDistance, saved as line_circle_distance): the gap while the line misses the circle (0 is tangency), or how far the edge reaches past the line when it cuts through (stored as `inside`). Rebased onto main as one commit (the branch's own changes; its merges of main dropped). Conflicts resolved: the test mod lines in cmd/sketch.rs (both kept, sorted), and README.md, whose feature list main moved to docs/capabilities.md; the line-to-circle dimension is listed there now. Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT Co-authored-by: jyviko <jyviko@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
91753051a1 |
docs(skills): multi-body primitives, mirror body names, unit suffixes, set_parameter call (#2964)
Add body_name and targets to the solid.box and solid.cylinder rows, the bodies form of solid.mirror and the names its bodies get, unit-suffixed parameter values, and the set_parameter argument shape. |
||
|
|
539c17b425 |
Joint sweep: drive a joint until parts touch, and report where (#1981)
Joint sweeps can stop where parts touch and report the contact and remaining room. |
||
|
|
8fdf6a61a2 |
Motion analysis: plots of a study's joint values and a point's path, and the parts that collide along it
- motion.plot: joint values and a point's path sampled as the study plays (closed loops following), with velocities and accelerations by finite differences, per step or per second with a duration. - motion.interference: the pairs of parts that pass through each other along a study, with the first step, the step ranges, the depth and the shared volume; occurrences, against or contact sets choose the pairs; bounded samples and work. - Motion Study dialog: a Study box opens a saved study; a chart of a joint value or a picked point with a cursor at the step shown (click to move there); Check collisions with the steps shaded on the chart. ui.start motion.study takes study. |
||
|
|
6232fa33c2 |
Joints: cam and follower coupling (joint.cam) (#1734)
Thanks @SychPL. |
||
|
|
dd5278a402 |
solid.hole: position takes length expressions and follows them (#1678) (#1683)
Thanks @DuBearte. |
||
|
|
2ae21143ee |
Motion animation export: a study or a joint drive as an animated GIF (#1680)
Thanks @SychPL. |
||
|
|
571122c4ba |
Motion Trace: a point's path as a joint is driven or a study plays, kept as a 3D spline (#1529)
Thanks @SychPL. |
||
|
|
4d862cd76c |
Joints: joint.reach (inverse kinematics), and a jointed part follows a drag (#1540)
Thanks @SychPL. |
||
|
|
a422fb1a4b |
solid.mirror: a plane's {origin} takes length expressions and follows them (#1492) (#1501)
Thanks @erkexzcx. |
||
|
|
f7967b1ff6 |
Joints: gears, rack and pinion, and screws (#1524)
Motion links can carry a coupling (gear pair, rack and pinion, screw) whose ratio follows radii, picked round faces or pitch. Design format 23. Co-authored-by: SychPL <SychPL@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
02fe495d38 |
Joints: a link ratio between an angle and a distance is per degree (#1433)
A motion link's ratio is read in b's units per a's (mm per degree, degrees per mm) when an angle and a distance are linked; stored values stay per radian, so saved designs are unchanged. Follow-up to #1415. Co-authored-by: SychPL <SychPL@users.noreply.github.com> |
||
|
|
c2ba3657f3 |
Joints: link offsets in b's units, new joints start inside their limits, loop dof at dead points (#1415)
joint.motion_link offset is read in the linked value's units (degrees or mm, or an expression); links are still stored in radians, so saved designs open unchanged. A new joint whose values fall outside its limits starts at the nearer limit, stores it, and says so in warnings. Loop dof also counts at a nearby pose, so a part at a dead point of its loop keeps its mobility. The mechanisms skill is updated to match. |
||
|
|
4674f0a61c |
Joint dialog: say why picks on one component make no joint (#1367)
The Joint and As-built Joint dialogs explain why picks make no joint and offer Create Components from Bodies when every pick is on one component; joint.create and joint.as_built suggest component.from_bodies. Labels in all catalogs. Fixes #1239. |
||
|
|
81d9b7e04a |
Agent skill for mechanisms: joints by coordinates, loops, links, motion (#1315)
A solidcraft-mechanisms agent skill with a worked example that a test replays through the MCP server (the actuator follows the hinge with the stated stroke and the drive stops at the rod's limit); the skills index and docs/mcp.md list it. |
||
|
|
5c6f70cc00 | Faces by name in the face commands; model.faces gives a point on each face (#1243) | ||
|
|
f037c92370 | Add the solidcraft-checking skill (#880) | ||
|
|
17db109c5d |
URDF export for robot simulators (#719) (#942)
A design with components and joints exports as a URDF robot description (file.export to .urdf, the MCP export tool, solidcraft-cli run --out, File > Export URDF...). It writes one binary STL per component in metres, links with mass, centre of mass and inertia from material density, and maps rigid, revolute, slider, cylindrical, pin-slot, planar and ball joints to URDF joints or short chains. Loops, broken joints and loose parts are reported as warnings. Options: mesh_prefix, effort, velocity. Docs in docs/urdf.md. |
||
|
|
95d394dcab |
Modeling skill: rim fillets after shell, slots along Y, boolean retry caveats (#879)
Corrects the modeling skill: an edge of the face a shell removes is rounded after the shell, a slot along Y is dimensioned horizontally, long shallow arcs measure to about 0.1%, and a boolean retry warning can hide wrong geometry when the volume does not match. |