Skip to content

Sheet metal: a perforation field bounded by a sketch's outline (#732) - #10754

Merged
bherbruck merged 1 commit into
tangency-labs:mainfrom
LorbusChris:sheet-perforate-outline
Nov 2, 2026
Merged

bherbruck merged 1 commit into
tangency-labs:mainfrom
LorbusChris:sheet-perforate-outline

Conversation

@LorbusChris

Copy link
Copy Markdown
Contributor

Change

Part of #732. sheet.perforate (#1041) covers the whole flat sheet: a panel that is perforated in one area and solid elsewhere (behind a connector, around a mounting area) can't be modelled, and an extruded cut can't take the holes either (about 500 per sheet). #732 asks for "a perforation feature on a sheet face: field outline + hole shape + pitch/pattern"; this adds the outline.

  • The command: sheet.perforate takes an optional sketch (id or name) and profiles (as a base flange takes them; default: all of them). Holes go only inside the selected closed profiles and out of their inner loops, at least margin clear of the outline as of the sheet's edges, cut-outs and bends. The grid starts at the outline's lower-left corner (within the flat), so the pattern can be placed. profiles without sketch is refused.
  • Where the outline goes: on a folded sheet it maps through each panel whose material the sketch plane lies in (region_to_flat) and is clipped to that panel, so an outline drawn on one face never reaches a flange in the flat. On an unfolded sheet it is taken as drawn on the flat. A sketch on no face of the sheet is refused.
  • Stored: the feature saves sketch and profiles (walked like any profile selection, so deleting the sketch deletes the feature); the sheet's perforation keeps the outline in flat coordinates (SheetPerforation::outline), so it follows fold, unfold, refold and reopen. A mirrored copy mirrors the outline with the field (from_right).
  • Edits: the Perforate dialog has no outline input yet, so timeline.redefine keeps the outline when the new parameters name no sketch (as it keeps a frame member's names). An outline sketch counts as used (sketch_consumed), like a base flange's.
  • Format: one pending note, format/pending/sheet-perforation-outline.rs; no migration: a field without an outline covers the whole sheet as before.

Not in this PR: an outline picker in the Perforate dialog, and centring the grid in the outline.

Changes a default: no

Validation

New engine tests in crates/engine/src/cmd/sheet_tests.rs, next to the #732 ones:

  • perforation_outline_bounds_the_field_and_keeps_out_its_inner_loops: Rv 1.0-1.5, margin 1, on a 100 × 60 × 0.8 sheet bounded by a 50 × 30 rectangle at (10, 10) less a Ø10 circle (the ring picked by a point). Expected 594 holes: the documented grid over the rectangle less those within 6.5 of the circle's centre. Measured: 594 in flat_pattern and in the DXF, the first at (11.5, 11.5), none outside 11.5…58.5 × 11.5…38.5 or nearer than 6.5 to (35, 25). A timeline.redefine without sketch keeps the count.
  • perforation_outline_on_a_folded_sheet_stays_in_its_panel: an outline reaching past the bend of a 90° flange puts every hole on the base side of the bend zone (centre at least r + margin beyond it); a sketch 10 mm above the sheet is refused ("must lie on a face of the sheet").
  • a_mirrored_sheet_copy_mirrors_its_perforation_outline: the mirrored copy's holes are the original's mirrored, relative to the field's corner, to 1e-6 mm.
  • Walk goldens (golden_refs.txt, golden_geometry.txt) and the fixture record the new sketch/profiles; the PerforateParams key test lists them.

Run on Linux x86_64 (Fedora 44, Rust 1.99.1), --profile ci, on this branch rebased on main at 653bc00:

  • cargo xtask ci: fmt, assets, icons, licences, lists, skills, layering, UI containers, clippy --workspace --all-targets -D warnings, the wasm check and the doc tests pass. 81 of the 83 test binaries pass, among them solidcraft-engine lib (3094 passed, 0 failed), solidcraft-doc lib (516 passed) and the UI scenarios test (146 passed). The two that fail have failures unrelated to this change:

    • solidcraft-gpu lib: 28 of 165 tests, all path-tracer tests on this machine's AMD Radeon 890M (RADV). On main at 653bc00, 28 fail as well.
    • solidcraft-ui-egui lib: 3969 passed and 2 failed. render_workspace::tests::progressive_image_controls_route_to_the_registered_host is a path-traced render ("inconsistent glass medium paths") and fails on main too. op_auto::tests::primitives_and_revolves_join_cut_or_stand_apart failed only inside the full run; run alone, it passes on this branch and on main.
    • The web offline test wasn't reached. With no Chrome on this machine, it would have been skipped anyway.
  • Rebased again onto main at e77c555 (see below): cargo fmt --check, the assets, icons, layers, UI containers, skills, lists and licences gates, and clippy --workspace --all-targets -D warnings pass there; cargo test -p solidcraft-doc --lib: 521 passed, -p solidcraft-engine --lib: 3102 passed, and -p solidcraft-ui-egui --lib sheet perforat: 56 passed, 0 failed.

  • Reproduction with solidcraft-cli run, built from main at 653bc00 and from this branch: the setup below, then sheet.perforate Ø4 on a 6 mm staggered pitch, margin 1, and sheet.flat_pattern.

    sheet.perforate main this branch
    no sketch 845 holes, the whole flat 845 holes, the whole flat
    sketch: "Field", the ring picked 845 holes: the outline is ignored (with --param-check strict: "invalid parameters for sheet.perforate: unknown key profiles") 345 holes, only in the ring
Script (with the outline)
{"commands": [
 {"command": "sheet.manage_rules", "params": {"name": "Steel 1.0", "thickness": "1 mm", "active": true}},
 {"command": "sketch.create", "params": {"plane": "XY", "name": "Blank"}},
 {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [200, 120]}},
 {"command": "sketch.finish", "params": {}},
 {"command": "sheet.flange", "params": {"sketch": "Blank"}},
 {"command": "sheet.flange", "params": {"edges": [[100, 0, 1]], "height": 25}},
 {"command": "sketch.create", "params": {"plane": {"origin": [0, 0, 1], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0]}, "name": "Field"}},
 {"command": "sketch.rectangle.two_point", "params": {"p0": [20, 20], "p1": [180, 100]}},
 {"command": "sketch.circle.center", "params": {"center": [100, 60], "radius": 18}},
 {"command": "sketch.finish", "params": {}},
 {"command": "sheet.perforate", "params": {"diameter": "4 mm", "pitch": "6 mm", "margin": "1 mm", "sketch": "Field", "profiles": [{"point": [30, 30]}]}},
 {"command": "sheet.flat_pattern", "params": {}}
]}

Screenshots of the behavior (when observable)

Steps: a 200 × 120 × 1 sheet (base flange) with a 25 mm flange on its front edge; on its top face a sketch "Field" with a 160 × 80 rectangle and a Ø36 circle; Sheet Metal → Perforate Ø4 on a 6 mm staggered pitch, margin 1; Sheet Metal → Flat Pattern. Then the same feature redefined with sketch: "Field" and the ring picked (profiles: [{"point": [30, 30]}]).

Setup: the folded sheet and the Field sketch on its top face.

Before (no outline, as on main): 845 holes over the whole flat, the flange below the bend line included.

After (outline): 345 holes, only in the ring: the circle and the flange stay solid, and the Field sketch is hidden as used.

Captured from this branch's desktop build (--profile ci) on Linux x86_64 under Xvfb, driven through the control channel (engine.execute, ui.screenshot).

Integration status

Rebased on main at 176c22a (2026-11-01); the checks above ran at e77c555, and the two commits since touch none of these files. One conflict: #10545 had turned sketch_consumed in crates/engine/src/view.rs into a set worked out once per design (consumed_sketches); the perforation outline is one more arm there. #732 is labelled area:kernel, but this doesn't touch crates/kernel: the outline is 2D work on the flat in crates/doc/src/sheet.rs. Open PRs touching the same files: #7652 (contour flange options) merges cleanly alongside it; #8397 (draft) and #6028 (draft) also change crates/doc/src/sheet.rs, but both conflict with main in other files at the moment, so I couldn't try them together with this; several PRs add lines to the walk goldens. None adds a perforation outline.

Assisted-by: LLM

…ncy-labs#732)

`sheet.perforate` takes an optional `sketch` (and `profiles`): the field's
outline. Holes go only inside its closed profiles, out of their inner loops,
`margin` clear of its edges, on a grid from the outline's lower-left corner.
On a folded sheet the outline maps through each panel the sketch lies on and
is clipped to it, so it never reaches a flange in the flat; a sketch on no
face of the sheet is refused. A mirrored copy mirrors the outline, and an
edit that names no sketch (the Perforate dialog has no outline) keeps it.
Without a sketch the field covers the whole sheet as before.

The feature saves `sketch` and `profiles`, a sheet's perforation its outline
in flat coordinates: one pending format note, no migration.

Assisted-by: LLM
@github-actions github-actions Bot added the area:doc timeline, components, parameters, naming label Nov 1, 2026
@bherbruck bherbruck added ready to merge reviewed, every ask addressed; merge once CI is green approved-for-ci Reviewed: run the full PR CI (build, tests, goldens, Clippy) labels Nov 1, 2026
@bherbruck
bherbruck enabled auto-merge November 1, 2026 23:56
@bherbruck bherbruck added approved-for-ci Reviewed: run the full PR CI (build, tests, goldens, Clippy) and removed approved-for-ci Reviewed: run the full PR CI (build, tests, goldens, Clippy) labels Nov 1, 2026
@bherbruck
bherbruck added this pull request to the merge queue Nov 2, 2026
Merged via the queue into tangency-labs:main with commit d7a17de Nov 2, 2026
106 of 119 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved-for-ci Reviewed: run the full PR CI (build, tests, goldens, Clippy) area:doc timeline, components, parameters, naming ready to merge reviewed, every ask addressed; merge once CI is green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants