Skip to content

Sheet metal: one model, the part's surfaces; the flat pattern is derived (#6748) - #9245

Open
h0witzer wants to merge 133 commits into
tangency-labs:mainfrom
h0witzer:fix-6748-develop
Open

h0witzer wants to merge 133 commits into
tangency-labs:mainfrom
h0witzer:fix-6748-develop

Conversation

@h0witzer

@h0witzer h0witzer commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Change

Sheet metal on one model, the part's surfaces (RFC #6748; direction confirmed by @jyviko on #6748). A sheet is its panels (flat, rolled or conical surfaces) and the bends, seams and rips between them; the folded body and the flat pattern are both built from that, so they always agree. The flat-pattern SheetBody is deleted (crates/doc/src/sheet.rs 10,961 → 825 lines; sheet_convert.rs and sheet_flat_lineage.rs gone), and every sheet feature builds from surfaces: Base, Contour, Edge and Path Flange, Hem, Fold, Unfold/Refold, Perforate, Convert, Recognize, cuts and holes read back onto the sheet, the flat pattern, DXF and bend table.

Where the geometry lives. The development engine is in solidcore (h0witzer#20 by @brett19, merged into this branch): solidcore::develop (charts, layout, fold, rigid motions, and hermite: cubic pieces proved within a tolerance of a curve carried into the flat), solidcore::eval::{query, folded} (exact record queries; the folded pcurve moved out of crates/kernel/src/sew.rs, which is now 198 lines shorter than main's), record types in solidcore-record::develop, calls in solidcore-ops::{develop, query}. crates/develop is a thin adapter that words the kernel's typed refusals. crates/geom is main's apart from the flux quadrature, whose pieces come from solidcore. What is still computed app-side is listed in #16938.

Developments are closed form and refused otherwise. Plane, right circular cylinder and right circular cone panels develop exactly; a free-form panel is refused with a typed reason pointing at #15743 (jyviko's conditions 1 and 2). Bends develop at the rule's K-factor; rolls and rolled cones at K 0.5 (the neutral fiber at mid-thickness, DIN 6935).

Landing. One PR, as discussed with @brett19: the engine moved into solidcore here rather than in a separate PR 1, and the flat model is already deleted, so a harness that runs both models can't exist. Parity is shown instead by main's sheet tests ported onto surfaces as the spec, and by designs saved on main and rebuilt here (below). The scope is frozen: only fixes go in from here; new sheet features (fork PRs h0witzer#8, #12, #13, #14) go to main after this lands.

Intentional behavior changes

Every user-visible change from main, with the test that pins it and the reason. "R" is a changed result of the same input (Changes a default: no per CONTRIBUTING); "T" is a change of what a tool does with its default inputs, or of what a saved value means.

# Change (main → this branch) Pinned by Reason Default?
1 A fillet on a sheet corner is kept as an arc of the outline; a later flange keeps it. main refused the flat pattern ("can't include Fillet1"), and the next sheet feature dropped the round with a warning sheet_tests a_fillet_on_a_sheet_corner_is_an_arc_of_its_outline_kept_by_a_later_flange; kc/3324-flat-pattern-refused-after-a-fillet-on-the-sheet (expectation rewritten; the directory name still says "refused"); cut_tests a_corner_rounded_through_the_sheet_is_an_arc_of_its_outline A round through the thickness is an outline arc of the flat. A 3D change is read back into the sheet, so nothing is dropped at the next sheet feature R: no
2 A cut grazing a bend near its edge takes every bend fiber it meets and the base. main refused it ("intersects a folded bend") sheet_tests a_cut_grazing_a_bend_near_its_edge_takes_every_fiber_it_meets_and_one_clear_of_it_cuts_the_base; cut_tests a_hole_through_a_plate_that_runs_into_its_bend_is_cut_from_both, a_drill_through_a_bend_is_cut_as_its_shadow A 3D cut is a 3D boolean read back into the flat; the flat keeps the cut square through the thickness, as a shop cuts it R: no
3 #14616's drill from a contour flange's foot into its bend now builds. main refused it sheet_tests holes_in_a_contour_flanges_end_panels_are_cut_outs_or_notches_of_its_flat_pattern (its last part; the volume bound T × outline length × the shadow's sag is derived in the test) As change 2 R: no
4 Perforation runs through a bend made after it. main dropped the holes that fell in a later bend's zone. A field made after a bend still keeps clear of it sheet_tests perforation_runs_through_bends_and_survives_fold_unfold_and_reopen; props_tests perforated_sheet_hole_a_later_fold_runs_through_weighs_as_the_same_hole_cut_and_folded; kc/cov-perforate-keeps-clear-of-a-bend-zone (unchanged, passes) A field is perforated stock: the press brake bends the holes with it, and the grid stays continuous on the flat R: no
5 A bend picked in the middle of a chamfer is refused ("is on a face, not an edge"). main made it one bend of the rule's radius at the virtual sharp convert_tests a_bend_picked_on_a_chamfer_is_refused A chamfer is a face of the body; Convert bends sharp edges and modelled radii, and takes faces as they are R: no
6 Bend radius 0 is refused wherever it is given (rule, fold, flange), and hem gap 0 too. main (#15288) built a sharp bend with its outer face only sheet_tests a_bend_radius_of_0_is_refused_wherever_it_is_given, a_refused_rule_names_the_value_at_fault, a_tiny_bend_radius_keeps_both_cylindrical_faces No material bends to a knife edge: the inside fiber would close to nothing, no tooling forms it, and the bend's end wall can't be built. A rule saved with radius 0 now fails at evaluation, and no format step converts it T: a value users could enter is now refused. Not a default value; maintainer call
7 A gap under 0.01 mm is refused, by sheet.manage_rules and when a saved rule is evaluated. main left those corners open with a warning ("left open") sheet_tests returns_follow_the_rules_gap_and_a_gap_of_zero_is_refused, a_design_whose_rule_has_no_gap_says_so_on_its_flanges (a saved gap of 0 errors on the base flange until it is set) Flanges closed with no gap would touch T: brett19's item 6. No format step
8 A 5 mm plate with a 2 mm wall is refused ("joins a wall 2.000 mm thick to a sheet 5.000 mm thick"). main built it as a low wall convert_tests a_bend_ends_where_its_wall_ends_with_the_rules_bend_relief_beside_it (last part; now run through sheet.recognize) A body with walls of two thicknesses is not one sheet; Convert takes its surfaces instead R: no
9 Recesses kept at any depth: Recognize keeps a recess of any depth short of the thickness, from either face, as a pocket. main refused one deeper than half the plate, recesses in both faces, and a recess leaving a raised rim. Convert keeps a recess as a pocket only where its faces are given in engrave; without that, the recess's opening is a hole of the panel convert_tests a_plates_recesses_are_the_sheets_at_any_depth_from_either_face, a_hole_reaching_into_a_bend_is_refused_and_a_deep_pocket_kept, an_engraving_is_converted_only_where_its_faces_are_picked A pocket is kept as its z-range, so any depth short of T can be represented. A shallow cut may be a pocket the design means, so Convert keeps one only when asked R for Recognize; T for Convert's unmarked recess
10 Convert: every face becomes a panel at the rule's thickness. On main, face was required: a plate kept its own thickness, a shell kept its wall thickness, and a solid took the rule's inward. Here body alone converts every face (less skip); face and faces take the faces picked and every face a bend joins to them; direction and engrave are new. A rod picked on its end is a disc of the rule's thickness (main: a plate as thick as the rod is long), and a block thicker at one end converts (main refused it). mode was never on main: it existed only on this branch (added in 057ef5f82, removed in bbac52e3f) surface_convert_tests a_thin_block_converts_to_six_panels_parted_by_rips, a_solid_converts_whole_with_its_material_inside_or_outside, a_surface_body_converts_with_the_rules_thickness_on_either_side, a_whole_thin_block_under_a_thick_rule_is_refused_naming_its_sides; convert_tests one_face_of_a_bent_part_thickens_alone, the_sheet_follows_the_solid_through_parameter_changes_undo_and_reopening; sheet_tests unfold_cut_refold_and_convert Convert takes surfaces; thickness and radii come from the rule (#6748). Recognize is the command for a body that is sheet metal already T: brett19's item 6. A saved plate conversion opens as Recognize of that plate under its rule (format/pending/sheet-surface-convert.rs); a saved conversion of a shelled body opens at the rule's thickness, with a note
11 Recognize (new) also builds at the rule's thickness and radius; new_rule makes a rule from the body. main's Convert of a plate or shell kept the body's thickness recognize_tests a_recognized_body_takes_the_rules_thickness_and_radius, a_new_rule_from_the_body_gives_the_body_back; convert_tests a_converted_shell_takes_the_rules_thickness_and_radius The rule's thickness is the sheet's R (new command)
12 Kernel cases moved from sheet.convert to sheet.recognize (10, each with a rule at the body's own thickness; expectations unchanged but for the method text): 1040-convert-a-1200-mm-strip-1-mm-thick, 1040-convert-a-990-mm-strip-keeps-its-1-mm-hole, 1092-convert-a-channel-with-its-bends-modelled, 1092-convert-an-L-with-a-modelled-bend-to-the-rules-radius, 1092-convert-an-engraved-plate, 1285-convert-a-bracket-whose-wall-covers-part-of-an-edge, 3316-convert-a-plate-keeps-its-place, 4328-convert-l-section-with-modelled-bend, 725-convert-a-shelled-tray-and-unfold-it, 774-convert-a-plate-with-nine-round-holes. tests/kernel-cases/EXEMPT now lists sheet.convert, so no kernel case runs Convert the cases themselves What main called Convert of a thin body is Recognize here see changes 10 and 11
13 A modelled corner with an inside radius from 10^0.7·T (about 5 T) is a roll that keeps the model's radius. main made every modelled bend one bend of the rule's radius convert_tests a_modelled_corner_is_a_bend_short_of_about_five_thicknesses_and_a_roll_from_there; surface_convert_tests a_roll_keeps_its_modeled_radius DIN 6935: from that ratio the neutral fiber lies at mid-thickness; a roll is developed there (row 30) R: no
14 Bend up/down labels flip on converted parts. The flat is read from the sheet's surface, and "up" means away from the material (sheet_surface_convert.rs, up: !b.toward_material). Base and edge flanges still read "up". The same rule swaps POCKET_TOP and POCKET_BOTTOM for a plate recognized from its engraved face, and mirrors the #14616 hat's flat (v = 20 − y) convert_tests a_shelled_tray_keeps_its_walls_and_unfolds_by_the_k_factor (BEND_DOWN ×4), bends_picked_inside_and_a_fixed_face_inside_give_the_same_sheet, a_trapezoid_section_unfolds_through_72_and_108_degree_bends, a_tapered_tray_converts_with_72_degree_walls_and_slanted_corners, bend_edges_changed_in_an_edit_are_the_ones_bent, holes_and_a_slot_in_a_channels_faces_land_where_the_bends_put_them, a_bend_ends_where_its_wall_ends_with_the_rules_bend_relief_beside_it, a_plate_with_an_engraving_converts_with_the_engraving_as_a_pocket; sheet_tests base_and_edge_flange_follow_the_k_factor ("up", unchanged) One reading for every sheet. Commit 057ef5f82 gave no reason; the reason above is the code's comment T: brett19's item 6. DXF layers BEND_UP and BEND_DOWN change for such parts
15 Torn corners 2 → 3. A corner shared by two features takes the relief of the later feature that asks for one, else the rule's. main gave a feature's relief only to corners between two of its own bends. The test's tear request also moved from the near feature to the far one. In the same way, a feature's rectangular relief now covers its shared corners (and the test's size went 3 → 8 mm, because a 3 mm relief at the new center takes nothing) sheet_tests an_edge_flange_feature_can_tear_its_corners_under_a_notch_rule, each_edge_flange_feature_chooses_the_corner_relief_of_its_own_corners, an_edge_flange_feature_follows_the_rule_for_the_relief_it_leaves_out A corner relief belongs to the corner's two bends, whichever feature made them, and does not depend on the order R: no
16 Corner relief centered where the two bends' zone center lines cross (c = s0 − BA/2), and cut from every piece of the flat it reaches. main cut it from the base at the corner of the bends (s0 in) and shortened both bends by r sheet_tests corner_reliefs_of_the_rule_reach_the_part_the_flat_pattern_and_the_dxf, corner_reliefs_at_non_orthogonal_corners_reach_the_flat_pattern_and_the_dxf, side_edge_flanges_across_a_bend_keep_their_relief_through_rebuild_and_reopen, thickness_in_a_rule_is_the_rules_even_beside_a_parameter_of_that_name; convert_tests a_converted_trays_corners_are_closed_and_relieved_as_a_flanged_trays_are, a_wall_on_part_of_an_edge_meets_a_wall_at_a_relieved_corner_and_is_relieved_at_its_end; ladder/006, ladder/007; kc/3417; scenario s51 (107.640745 → 110.154019); develop_tests a_corner_relief_is_centered_where_the_bends_cross The relief belongs to the two bends and sits where their center lines cross; derivations are in each expected file R: no
17 Flanges from separate features close their shared corner, mitred by the rule's gap as one feature's are, whichever was made first. main let the later flange trim the base under the first bend, and the earlier flange's panel ran on, so the result depended on the order oracle/75-sm-bracket-holes/expected.json (KNOWN_FAILURES = {} in tools/oracle-analytic/cases_sheet_metal.py; main had "75-sm-bracket-holes": "#296"); surface_convert_tests flanges_meeting_at_a_corner_close_it_as_converting_the_same_metal_does No order dependence; a convention change, not a corrected value. kc/296-hole-in-a-base-with-two-adjacent-edge-flanges is re-derived for the mitred corner R: no
18 Hem gap is the space between the leg and the face it folds over, so the bend's inside radius is gap/2. On main the gap was the inside radius. Saved designs migrate (format/pending/sheet-hem-gap.rs doubles saved values, and a rule without one gets 0.02). The default hem_gap is still "0.01 mm", so a new default hem has inside radius 0.005 (main: 0.01) format/pending/sheet-hem-gap.rs test saved_hem_gaps_double_and_a_rule_without_one_gets_twice_the_old_default; oracle/73-sm-hem (21334.723 → 21332.046); ladder/009; kc/cov-hem-open-with-a-gap-flipped, kc/cov-export-bend-table-csv-flange-and-hem; sheet_tests contour_flange_hem_and_tray; convert_tests designs_from_before_bend_edges_open_unchanged The gap a shop measures; an inside radius of gap/2 leaves exactly that space (brett19 checked this) R for saved files (migrated); T for scripts: a script passing gap: 1 now gets R 0.5
19 A contour flange's tangent arc is a roll that keeps its radius: the bend table has no row for it, and roll_table lists it. main listed it as a bend (R 8, 90°), developed at the rule's K; a roll develops at K 0.5 (row 30) sheet_tests contour_flange_takes_tangent_arcs_and_develops_their_bends (bends 0, roll_table 1) A drawn arc is a rolled panel; a sharp corner is a bend R: no; the bend table output changes
20 No warning when a cut parts a piece from the sheet. main said "1 piece of Body1 is no longer joined to its base … it is kept". Now each piece is a sheet with its own flat sheet_tests a_slot_across_a_flange_beside_its_bend_leaves_two_pieces_folded_or_flat, a_hole_across_a_flanges_width_leaves_two_pieces, a_ring_over_a_flange_keeps_the_piece_of_its_inside, cuts_that_split_a_sheet_leave_its_pieces_each_with_its_flat There is no base panel to be parted from R: no
21 A free bend end on a slanted plate edge continues the plate's outline. On main it ended square across sheet_tests a_cut_out_with_a_corner_on_a_bend_line_refolds (1.0 mm² vs 2.0) The decision recorded on #6748 (option B) R: no
22 A low fold across a flange's mitred slanted end is refused (at z 14). main built it sheet_tests a_fold_across_a_flanges_slanted_end_folds The folded top would run into the slanted wall's material R: no
23 Beside a ledge as tall as the setback, the bend spans the whole picked edge (0…100). On main it stopped at 97 sheet_tests an_edge_flange_beside_a_ledge_as_tall_as_its_setback_or_a_little_taller_folds A hair's difference in the ledge's height doesn't move the bend 3 mm R: no
24 A cut passing a hair off a sheet corner (nearer than the body's working tolerance, max(size·1e-6, 1e-5) mm, but farther than the linear resolution) is refused by name. main (#15346) built every offset sheet_tests a_cut_passing_a_hair_off_a_flange_corner_builds No snapping: the kernel would take the two as one point, and moving the cut onto the corner would change what was drawn R: no
25 One face per plane: an L bracket measures 10 faces (main 14), #15566's sharp tabs 18 (main 26), and an unfolded body keeps the folded body's faces sheet_tests base_and_edge_flange_follow_the_k_factor, fold_across_two_rounded_tabs_keeps_one_face_per_arc, elliptic_cut_out_stays_exact_in_the_flat_pattern_and_follows_arcs_when_folded Coplanar side walls are one face of the folded solid R: no
26 Sheet face and edge names are the model's. main: sheet:<body>:<panel>:<side> in model.faces's sheet_face, sketches attached as sheet_face, and flange edge_names like base:l2:top. Here: names like F12:top:F3:side:l1, sheet_face always empty, sketches attached as exact_face, and flange edge_names like F3:side:l2|F3:top names_tests (8); sheet_tests flange_edges_are_named_and_follow_the_base_sketch, a_sketch_on_a_flange_face_attaches_to_its_panel, every_flat_face_of_a_sheet_takes_an_attached_sketch #7990 plan of record: no pick-order or index names T: replies and saved files. As a design opens, a base flange's panel and rims, a converted sheet's fixed face, and saved flange and hem edge names map to the model's names (format/pending/sheet-face-names.rs, sheet-edge-names.rs); main's index face name on a sheet (F2:face:1) finds the one face of that feature its saved point lies on; another old sheet: name reports a lost attachment, to be picked again
27 Convert dialog rows went from "Fixed Face, Bend Edges" to "Body, Bend Edges, Skip Faces, Faces, Engrave Faces" plus the material side; a Recognize dialog is added dialogs_tests convert_takes_the_faces_picked_and_the_bend_edges, convert_takes_the_body_its_bends_and_the_faces_it_skips, recognize_makes_the_bodys_rule_and_convert_points_to_it Follows changes 10 and 11 T: a UI change (CONTRIBUTING asks for screenshots of UI additions)
28 A fold stores the side that stays (stays), decided once. main decided it again on every rebuild (the larger side, or the fixed point). Older designs get the side once, as they open sheet_tests a_fold_keeps_the_side_its_fixed_point_picked_when_its_line_moves_past_the_point, an_older_designs_fold_gets_its_side_once_as_it_opens_and_keeps_it; names_tests a_fold_keeps_its_side_and_that_sides_names_when_its_line_moves Names and placement stay stable when the line moves R: no (changes what a parameter edit does). The new stays: left|right input is not passed by any test
29 A junction cut between opposite-side segments is reported as slits in the flat JSON (two OUTLINE lines in the DXF), not spliced into the outline loop dxf_tests junction tests (read through junction_cuts) The flat is the sheet's pieces; a slit is not material R: no; the reply shape changes
30 Rolls and rolled cones develop at K 0.5 (the neutral fiber at mid-thickness), whatever the rule's K-factor; bends keep the rule's K. main developed every bend at the rule's K and had no rolls surface_convert_tests a_conversion_shows_in_the_flat_pattern (rule K 0.4), a_roll_keeps_its_modeled_radius, a_pipe_rolls_out_from_its_surface_alone; sheet_tests a_contour_flange_rolls_a_tangent_arc_at_its_own_radius (its folded volume is then the flat's area × T exactly), a_contour_flange_of_one_half_turn_is_a_half_pipe; cut_tests a_rolls_flat_has_lines_where_it_starts_and_ends_and_halfway_between A roll is formed, not braked, so the rule's K (its press brake's) is not a roll's; DIN 6935 puts a formed sheet's neutral fiber at T/2 from R/T = 10^0.7, where Convert takes a modelled corner for a roll (row 13). brett19's roll-threshold point R: no
31 A torn corner's bend end follows the corner's closing plane, drawn tight to the rip as a clamped cubic within 1e-3 mm, so its flat edge is a curve; main cut it straight across in the flat (a helix when folded). Flat area +1.5 to +3.4 mm² on the torn trays; bend lengths unchanged kc/723-tray-with-torn-corners-folded, kc/723-unfold-a-tray-with-torn-corners, kc/1797-tray-with-torn-corners-at-72-degrees-folded; sheet_tests a_tear_of_the_rule_reaches_the_part_the_flat_pattern_and_the_dxf The tear's two sides keep the rule's clearance along the whole bend; a straight cut in the flat leaves the folded edges closing or crossing (#6748) R: no

Parity with main

Saved designs. Each sheet-metal kernel case's recipe was run by main's build (1843e1f, main's own copy of the recipe), saved, and the saved file rebuilt by main's build and by this one, comparing every body's volume and area and each sheet's flat size, flat area and bend table. Of 71 saved designs: 46 rebuild identically; 17 differ as rows of the table predict (thickness from the rule, bend labels, relief placement, corners closed across features); 3 where this branch hits the closed form exactly and main was a hair under (an ellipse cut-out, 0.013 to 0.016 mm³); 3 differ by the torn corner's shape (row 31); 1 asks for a face to be picked again (a sketch on an edge flange's panel by its sheet: name, row 26); and 1 is refused: a Convert of an L whose recipe draws the inner arc three quarters of a turn, so its inside corner is a bump, not a bend (main dropped the bump's material). main's index face names (F2:face:1), a Convert picked on the bend's other cylinder, and a mirrored sheet's flange edge names were fixed in 55fdc95. The case-by-case tables are in the comments below.

Capabilities. Every sheet capability on main, each with the test here that covers it, is in the parity comment (#9245 (comment)).

Validation

Locally on the pushed head (3fd02c8; Windows, GNU toolchain, dev profile):

Known, with follow-ups

Changes a default: yes — no preference keeps the old behaviour; the T rows above are the user-facing changes, each listed with its reason for a maintainer's OK, and older designs are converted once as they open where the geometry can be kept (hem gaps, plate conversions, sheet face and edge names).

Test change: kernel cases, the ladder and oracles 73 and 75 changed only where this PR changes the result, each row of the table above pointing at its test, with the number derived by hand in the expected file.

🤖 Generated with Claude Code

@github-actions github-actions Bot added area:doc timeline, components, parameters, naming area:infra CI, tooling, repo, docs area:kernel B-rep kernel, booleans, blends, STEP/IGES area:ui desktop/web front end, dialogs, viewport kernel-paused labels Oct 30, 2026
@h0witzer h0witzer changed the title Sheet metal on SMLib: Convert from the body's surfaces, reading the B-rep (#6748) Sheet metal on SMLib: Convert and Thicken from the body's surfaces, with flanges, hems, cuts and engravings (#6748) Oct 31, 2026
@h0witzer
h0witzer marked this pull request as ready for review November 1, 2026 03:23
@h0witzer

h0witzer commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

@bherbruck could you add approved-for-ci to this PR? It's out of draft with develop at 05f5e86 merged in. On b6d3815 every check that runs without the label passes: quick compile, format and repository checks, every kernel rig area, and the ladder (168 of 168). The defaults line check stays red, as the description explains. The full suite (Linux tests, clippy, reference and doc tests) needs the label to run.

h0witzer and others added 3 commits November 3, 2026 10:36
…angency-labs#6748: two files still named the record through solidcraft_geom

main at dc38349 fails to compile: crates/smlib/src/trace.rs and
apps/solidcraft-cli/src/kernel_cases_export.rs name `solidcraft_geom::brep::Brep`, which moved to
solidcore_record. Drop this commit once main has its own fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…solidcore

fix-6748-develop (d7ac9223) brought onto main at dc38349: crates/develop, the sheet made from
surfaces (Convert, Thicken, its features, cuts read back onto the flat), brep::query and the
tests, on solidcore and the record types main now takes from solidcore-record
(`solidcore_record::{Vec2, Vec3, Seg2, Loop2, Region2, Plane}`, the record's brep types, the
geometry through `solidcraft_geom::prelude`).

Taken from main where both changed: the Hem and Fold commands' typed contracts (Hem's gap
described as the space between its leg and the face, twice the bend's inner radius), main's
layer comment for solidcore, the geom tests' imports. The flat pattern's DXF keeps main's writing
order and loose pieces, in the one writer flat-first and surface-first sheets share. main's new
cut test converts with Thicken and counts the surface sheet's holes. The old flat-first
sheet_convert.rs goes, as on develop.

main's tray round trip (tangency-labs#7529) is restored and FAILS here: Convert keeps a bend modelled with its
radius as a roll, so the tray converts with the right solid and flat but no bends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-record's types

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LorbusChris added a commit to LorbusChris/solidcraft that referenced this pull request Nov 3, 2026
`sheet.perforate` takes a `shape`: round (default), hex (a regular hexagon
`diameter` across its flats, a corner up, so a staggered field is a
honeycomb) or slot (`diameter` wide, `length` long overall, along the
rows), and a `row_pitch` for rows closer or further apart than the
pattern's own. Each hole keeps `margin` clear of the edges, cut-outs,
bends and outline by its own outline, from a first row and column half
its width (or height) in. Holes that would meet their neighbours, a
slot no longer than it is wide, and a length without a slot are refused.

The flat pattern reports each field's shape, row pitch, area (the outline
on the material, else the flat), its open share of that and the grid's
nominal open share (Ø5 on 6 staggered: 62.98 %). The DXF draws a hexagon
as six LINEs and a slot as two LINEs and two ARCs on PERFORATION, and the
measured volume and area take each shape's area and perimeter. The
Perforate dialog gets Shape, Slot length and Row pitch, and the flat view
draws hexagons and slots as their outlines.

The feature saves `shape`, `length` and `row_pitch`, a sheet's
perforation `shape` and `row_pitch`: one pending format note, no
migration. The holes stay marks on the flat pattern; cutting them into
the solid is left for the surface model (tangency-labs#9245).

Assisted-by: LLM
h0witzer and others added 4 commits November 3, 2026 12:50
…a bend, a roll past DIN 6935's limit, and rolls have lines in the flat

Convert and Thicken tell a cylinder modelled between two planes by its inside radius in
thicknesses. Short of 10^0.7 (about 5), where DIN 6935's k(R/T) = 0.65 + 0.5 lg(R/T) reaches 1
and the neutral fiber lies halfway through the sheet, it is a bend: the cylinder is no panel, the
two planes run on to their virtual sharp (each end carried along the line beside it, or stepped
out square where the face runs on past the bend), and they bend there with the cylinder's own
inside radius, so the sheet is the part as modelled. From there on it is a roll, as before. A bend
picked on a modelled bend's cylinder or on one of its edges is on the sheet.

A roll's flat has its lines, as a bend's has: along its rulings where it starts and ends, and
halfway between (`roll_lines` in sheet.flat_pattern); the DXF draws them on a ROLL layer, so a
reader of the bend layers doesn't take a roll for bends.

The tests that had a modelled corner of one or one and a half thicknesses as a roll have it as a
bend of its radius; the flat-pattern test's channel is rolled six thicknesses inside to stay a
roll. main's tray round trip (tangency-labs#7529) converts its bends now: the notched tray of one feature comes
back exactly; the others differ at their corners' reliefs (round -4 mm2 of flat, notched and
round in two features -2, torn +4.1 with its bends 0.58 longer), which is still to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lines, dotted where a roll starts and ends and dashed halfway

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the tooling would: its thickness, its bends' radius and its reliefs

A vendor's file with other radii or another thickness converts to the sheet the rule makes. The
sheet takes the rule's thickness from the side picked, whatever the body's, and a bend modelled
with its radius (short of DIN 6935's 10^0.7 thicknesses) bends at the rule's radius at its
virtual sharp. The body's own thickness only tells its sides apart and whether a modelled corner
is a bend or a roll; a roll keeps its own radius. Engravings are read against the sheet as the
body has it (its thickness and its corners' radii), and kept on the rule's sheet each as deep
from its face. Convert notes where the rule changes the body: its thickness, or a corner's radius.

main's 1092 case (an L modelled R 2 under an R 3 rule: one bend at the rule's radius at the legs'
virtual sharp, V = 9000 + 200 pi) is back in place of this branch's rename; its recipe keeps
Thicken and the quarter arc. Tests whose plates are not about thickness convert under a rule of
their stock; the parametric shell's stock is its thickness parameter; the shell, L, block and
threshold tests expect the rule's thickness and radius. The guide says the rule wins.

The tray round trip (tangency-labs#7529) still fails: the rule's reliefs as Convert cuts them differ from the
ones the edge flange cut under the same rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… as it reports each bend

A roll's row in sheet.flat_pattern (`roll_table`) and in the Flat Pattern dialog: how far it
turns, its inside radius (none for a cone, whose radius runs along it), its length along its
rulings and how wide it lies in the flat, read from its start, end and middle lines. The bend
table CSV keeps its bend rows only, as press-brake readers take it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sheets (tangency-labs#6748)

Base Flange and Contour Flange build sheets made from surfaces; Fold,
Path Flange, further bends and perforation fields work on them. The flat
reads from the sheet's surface side (up bends toward the reader); a cut
that parts pieces holding no joint keeps them in place, as flat-first did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jyviko
jyviko changed the base branch from develop to main November 4, 2026 03:15
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brett19

brett19 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

@h0witzer thanks for the second round. I checked c429118 against my review of 11-12, and the items are settled:

Two checks fail on this head. Both are small:

  • Format: cargo fmt --all --check wants to rewrap one use crate::schema::sheet::{…} list at crates/engine/src/cmd/sheet.rs:17.
  • Defaults line: the check only accepts Changes a default: no or yes — <what>, old behaviour kept behind preference <name>. Your line says no preference keeps the old behaviour, so it fails by design until a maintainer OKs the T rows. That's the third of your asks to @bherbruck, so nothing for you to change there.

On jyviko's item 5: the certified outline from solidcore::develop::hermite failing the planar boolean with status 1001 on the 30° hole looks like a solidcore boolean problem, not a sheet one. If you can post the two operands, or a script that builds them, we'll take the 1001 as a solidcore issue so it doesn't hold the wiring in #16938.

Written by Claude Code (Anthropic) on behalf of @brett19.

h0witzer and others added 2 commits November 14, 2026 16:34
…tests (tangency-labs#6748)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts run again (tangency-labs#6748)

- ladder/12-sheet-metal/006 and 007 re-recorded: the corner-relief trays have the relief walls of
  each bend, flange and panel as faces of their own (006: 73 faces, 181 edges, 110 vertices; 007:
  132 edges, 88 vertices). Volume and area are unchanged and every check passes.
- The two ball-on-roll cuts are no longer ignored: tangency-labs#15886's trim fix is on main (merged here), and
  both pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@h0witzer

Copy link
Copy Markdown
Contributor Author

@brett19, a regression report for #15475 (SolidCraft's planar region booleans on solidcore-ops), found merging main into this branch.

Five sheet tests on fix-6748-develop pass with main at 1843e1f merged and fail with main at be93b56. Reverting 5531932 alone on 63e22b3 makes all five pass again (git revert --no-commit 55319322d, keeping this branch's sheet.rs), so it's #15475:

cargo test -p solidcraft-engine --lib -- \
  a_long_thin_strip_keeps_its_small_round_holes \
  a_converted_trays_corners_are_closed_and_relieved_as_a_flanged_trays_are \
  a_round_notch_over_a_junctions_far_edge_ends_the_cut_on_the_arc \
  each_corners_relief_walls_are_named_after_the_edge_between_its_rims \
  a_corner_closed_against_an_earlier_bend_is_named_after_that_bends_end
  • convert_tests::a_long_thin_strip_keeps_its_small_round_holes: Recognize of a 990 × 100 × 1 strip with round holes of 10, 4, 2 and 1 mm now fails in the planar boolean: planar regions: topology::brep::p7: loop does not close.
  • face_names_tests::each_corners_relief_walls_are_named_after_the_edge_between_its_rims and a_corner_closed_against_an_earlier_bend_is_named_after_that_bends_end: a round corner relief is two arcs that meet only in the gap past the corner, so no wall it leaves has a vertex of the circle on it. After solidcore: SolidCraft's planar region booleans run on solidcore-ops, made to match first (RFC 0003 step 2.10) #15475 the base's relief wall comes back split in two (…#[F2:bottom,F2:top]#1 and #2), as if the region's circle were split again at the point opposite where its arcs meet.
  • convert_tests::a_converted_trays_corners_are_closed_and_relieved_as_a_flanged_trays_are: the round-relief tray's flat outline from Convert no longer matches the flanged tray's (same metal), at the relief arcs.
  • dxf_tests::a_round_notch_over_a_junctions_far_edge_ends_the_cut_on_the_arc: the junction cut no longer runs from the bend line to the arc of a round notch.

All five involve arcs in the regions the sheet passes to planar_regions_boolean (circles as one or two arcs, notches cut by arcs). Locally the rest of the sheet, convert and loft tests pass (483 of 488), and CI's kernel rig shards, sheet-metal included, are green on this branch. Should these be fixed on the ops side, or is there a difference in what the sheet should pass in now?

@brett19

brett19 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

@h0witzer thanks, that's a clean bisect. It's ours: #15475 moved the planar region booleans onto solidcore-ops, and all five of your cases have arcs in the regions. The sheet passes what main's path accepted, so we're treating this as an ops regression to fix on our side, not something for this branch to change.

We're reproducing it now on c429118 with your five tests. Then we'll write minimal repros that run on main without the sheet code: the strip with 10, 4, 2 and 1 mm holes, a circle passed as two arcs meeting off-axis, and the arc notch. The fix comes as its own PR, with those repros as solidcore-ops regression tests, and we'll link it here.

If something you pass in does turn out to be the cause, we'll say so with the trace before asking you to change anything.

Written by Claude Code (Anthropic) on behalf of @brett19.

…d rim hole is as deep as drilled (tangency-labs#6748)

- main's index face names on a sheet (`F2:face:1`): a sheet feature names its faces after what
  made each and numbers none, so the name found nothing. It now finds the one face of a sheet body
  the same feature made that the saved point lies on (`numbered_sheet_face`, as tangency-labs#16661 finds a
  replaced line's side face); the index is not read. Holes main saved on a base's bottom, in its
  rim and through a flange rebuild as main built them.
- Convert: a modelled bend picked on its cylinder across the thickness from the faces picked is
  the bend on their side (the coaxial cylinder of another radius that runs smoothly into what they
  reach), as main's Convert took it. Before, that cylinder and its faces became panels too and
  overlapped the faces picked.
- sheet-edge-names: a flange's `base:<line>:<side>` maps where no copy comes before the flange; a
  mirror after it (a part mirrored once its flanges are on) no longer leaves the names unmapped.
- A bore lying in a panel runs a hair past an end only where the panel's material ends: a blind
  hole into a rim was T/100 deeper than drilled (10.04 for 10 in a 4 mm rim).

Test change: four tests added (`holes_main_saved_on_a_sheets_numbered_faces_open_on_those_faces`,
`a_part_main_mirrored_after_its_flanges_rebuilds_both_copies`,
`a_bend_picked_on_the_cylinder_across_from_the_face_converts_as_the_face_sides`,
`a_blind_hole_into_a_rim_is_as_deep_as_drilled`) and one case in the sheet-edge-names unit test;
none changed or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@h0witzer

Copy link
Copy Markdown
Contributor Author

Pushed 55fdc95 to fix-6748-develop: the saved-design gaps from the parity run, and one bug the rerun found (designs saved on main, rebuilt here), each with its test. Fixes only, under the scope freeze.

  • main's index face names on a sheet (F2:face:1). main named a sheet feature's faces by their number among the faces it made; here a sheet feature names its faces after what made each (F2:bottom, F2:side:l3, F3:bottom:F2:side:l1) and numbers none, so the saved name found nothing and the hole asked for its face again. Such a name now finds the face its saved point lies on, when that is the one face of a sheet body the same feature made there (name_lookup::numbered_sheet_face, the rule Shell face / construction-axis references are lost when a sketch's curves are replaced, and timeline.edit cannot reselect a shell face #16661 uses for a replaced sketch line's side face). The index is not read. A point on no face of that feature, or on two, still asks for the face again. sheet_upgrade_tests holes_main_saved_on_a_sheets_numbered_faces_open_on_those_faces: holes on a base's bottom, into its rim, and through a flange's leg, and one whose point lies on another feature's face.

  • A Convert with its bend picked on the cylinder across the thickness from the face (the outer arc, the face picked inside, or the other way round). main's Convert took that (its a_bend_modelled_with_its_radius_is_one_bend_of_the_rules_radius); here Convert takes every face it is given as a panel, so the other cylinder and the faces it runs into became panels too, and their material overlapped the face's. Such a pick is now the bend on the face's side: the coaxial cylinder of another radius that runs smoothly into what the faces picked reach. sheet_convert_tests a_bend_picked_on_the_cylinder_across_from_the_face_converts_as_the_face_sides: an L converted from either face with either pick gives Recognize's blank and metal.

    The saved 1092-convert-an-L-with-a-modelled-bend-to-the-rules-radius still refuses, and I'd keep it so: its recipe draws the inner arc with sketch.arc.center_point from (4, 2) to (2, 4), which runs counterclockwise three quarters of a turn, so the body's inside corner is a round bump (the face is 464.4 mm², 2 × 4.64 rad × 50), not a bend. main converted it to a clean L and dropped the bump's material without a word; Convert here says the panels overlap. The kernel case moved to Recognize (row 12), which reads the L from its outside and also gives the clean L; whether Recognize should refuse the bump instead is in Sheet metal: geometry still computed app-side, and certified cut outlines (follow-up to #9245) #16938.

  • A mirrored sheet's flange edge names (3353-mirrored-sheet-body-keeps-its-flat-pattern). The sheet-edge-names format step mapped base:<line>:<side> only where no pattern, mirror or copying move was anywhere in the design. It now looks only at the features before the flange: a mirror after it copied a sheet that already had the edge. sheet-edge-names unit test, and sheet_upgrade_tests a_part_main_mirrored_after_its_flanges_rebuilds_both_copies.

  • A blind hole into a rim was a hair too deep. Rebuilding 955 found it once the hole opened: a bore lying in a panel was kept running T/100 past both of its ends, so a hole drilled 10 deep into a 4 mm rim was 10.04 deep (0.04π mm³ over; the kernel case's 1e-3 tolerance hid it). It now runs a hair past an end only where the panel's material ends; a blind end is the drill's floor. sheet_tests a_blind_hole_into_a_rim_is_as_deep_as_drilled, exact to 1e-6, folded and flat.

Parity, rerun on this head: 71 designs saved by main: the 65 before and the 6 that export a file, now that the harness puts their $OUT where the kernel-case runner does. 46 rebuild identically (560, 955, 1280, 3353 and the exported flange with a hole among them); 17 differ as rows of the table predict; 3 where this branch hits the closed form and main was a hair under (659, and the two exported ellipse cut-outs: 12000 − 120π = 11623.008882, main 0.013 mm³ under); 3 torn corners (row 31); 1 asks for a face to be picked again (1295: a sketch on an edge flange's panel by its sheet: name, row 26); and 1 refused (1092's L, above).

Gate on this tree: solidcraft-doc --lib 691 pass; solidcraft-engine --lib sheet, convert and loft 489 pass and 5 fail, the five #15475 tests reported above and no others; clippy -D warnings clean on doc and engine; fmt clean on every changed file.

The certified cut outlines (#16938) and the #15475 regression (brett19's fix to come) are unchanged by this push.

@brett19

brett19 commented Nov 15, 2026

Copy link
Copy Markdown
Contributor

@h0witzer, a status update on the five #15475 engine tests that still fail on this branch (relief wall naming, the tray outline, the round notch, and the two others in the list above). The cause is in solidcore, on main too, so the fix is coming as its own PR, #17061 (draft).

Cause. curve_on_frame (solidcore/src/prism/profile.rs, the arc branch) builds every arc on one shared circle that starts on the frame's x axis, then edits the trim onto it. The old SolidCraft path rotated each arc's circle to start at the arc's own start. That difference causes two failures:

  • Two arcs of one circle get identical curves. boolean_2d then drops the vertex where they meet. A plate minus a circle drawn as two arcs at 30° and 120° comes back with 5 edges instead of 6, and the reader then splits the whole circle at start + π.
  • An arc starting past π gets a degree range past 360° (for example [215°, 395°]). The sheet's 0.5 mm hole then fails with "loop does not close", even when intersected only with itself.

The sheet data is valid as it stands. Switching only that arc branch back to the old construction makes all five tests pass on this branch, with no SolidCraft change.

Fix (#17061). Each arc gets its own circle, starting at the arc's start, trimmed [0, sweep], with no re-parameterisation. It adds solidcore tests that fail on main:

  • an off-axis circle as two arcs keeps both meeting vertices at their closed-form points;
  • the strip with the 0.5 mm hole at a 395° start builds and passes check(All);
  • an arc notch.

It is in its gate now. It then gets an independent review, and we'll mark it ready and post here when it does. Once it merges, merging main into this branch should be all the five tests need. Whether boolean_2d should merge two arcs of one circle at all is left as an open question in that PR, not changed.

Written by Claude Code (Anthropic) on behalf of @brett19.

@brett19

brett19 commented Nov 15, 2026

Copy link
Copy Markdown
Contributor

@h0witzer thanks for the report: it's on the ops side, and the fix is #17061; nothing changes in what the sheet passes in. solidcore-ops built every arc on one shared circle per centre (angle 0 on the frame's x axis), so two arcs of one circle became one curve and the 2D boolean dropped the point where they meet, and an arc starting past π got a trim past 360° (the 0.5 mm hole's p7: loop does not close). #17061 builds each arc on a circle of its own starting at the arc's start, as SolidCraft's path did, and your five tests pass with it on 9cf1ce0.

Written by Claude Code (Anthropic) on behalf of @brett19.

…ntour's bend (tangency-labs#6748)

The full suite's `design_corpus` and `action_matrix` found three faces of the corpus's
`modern-sheet-metal` (a contour flange of three legs) that no sketch could stay attached to: the
first leg's far end and its two ends were named by their place among the faces (`F2:face:2`),
which is no identity (tangency-labs#7990). A later leg's rims are named after the bend that makes it (`far`,
`end0`, `end1`); the first leg is made by no bend, and a contour's sketch line draws none of its
rims, so nothing said what made them. They are now read from the contour's bend into the next
leg, as that leg's are: `far:l1`, `end0:l1`, `end1:l1`, at the same ends as the next leg's. A
flange added on the leg later never counts, so the names hold when one is.

Test change: one test added (`a_contour_flanges_first_leg_names_its_rims_as_the_others_do`);
none changed or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The section of a B-spline extrusion by a plane parallel to its profile (tangency-labs#6748)
came without UV curves, as upstream's analytic special cases do. The general
intersection it replaces gave them, and the offset's mate proves a curve
without them only on analytic surfaces, so moving a cylinder's top was refused
with ExactCurveUncut (local_operation
a_raised_cylinder_top_mates_on_its_exact_circle_within_the_derived_reach).

extrusion_section_uv_curves fills both, exactly: on the B-spline the iso line
at the sweep parameter where the plane cuts (sigma = f / (c - f (c - 1)), c
the weight ratio across the sweep), on the plane the section's control net in
the plane's parameters. The unit test checks both lift onto the section to
1e-12.

Result that changes: WR-450 with analytic_copy_keeps_parameterization off
(upstream's defect) now has trims 0.78 off on the arcs at z = -14 and z = 14
as well as the line at x = -14 (3 edges and 4 vertices, was 1 and 2): the
box's top and bottom now cut the cylinder in its exact section. With the fix
on, the result is valid as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@h0witzer

Copy link
Copy Markdown
Contributor Author

Pushed 5c10d98 to fix-6748-develop (fixes only). Before it, I ran the full suite locally on a2bbf21, including SolidCore's own checks with upstream/TestFiles at the pin, and compared every failure against main (be93b56) on the same machine.

  • Ours, fixed in 5c10d98. When a plane parallel to a B-spline extrusion's profile cuts it (this PR's exact case, so a sheet cut by an extruded ellipse gets one curve on both faces), the section came back without UV curves. The offset's mate can prove such a curve only on analytic surfaces, so moving a cylinder's top was refused (a_raised_cylinder_top_mates_on_its_exact_circle_within_the_derived_reach). The case now gives both UV curves exactly: the iso line on the B-spline, and the control net mapped into the plane's parameters.
  • A result that changes (SolidCore AGENTS.md): WR-450 with analytic_copy_keeps_parameterization off, which is upstream's defect, now has trims 0.78 off on the arcs at z = ±14 as well as the line at x = −14 (3 edges and 4 vertices, was 1 and 2). The box's top and bottom now cut the cylinder in its exact section. With the fix on, which SolidCraft runs, the result is valid as before. The test's pinned list is updated with that reason.
  • Ours, fixed in a2bbf21. A contour flange's first leg named its rims by index (F2:face:2), so a sketch on them lost its attachment (design corpus modern-sheet-metal, action matrix oracle/72-sm-contour-flange). New test: a_contour_flanges_first_leg_names_its_rims_as_the_others_do.
  • Not ours. Every other local failure fails the same way on main on this machine (Windows): the kernel fillet and G2 corner tests, STEP export bytes, SolidCore's setback 3.5 vs 3.563, section-end trims, tab corner arcs, flat_parity b725, an error test comparing a / path, six naming-corpus designs and the lid-ribs shell. The five solidcore: SolidCraft's planar region booleans run on solidcore-ops, made to match first (RFC 0003 step 2.10) #15475 sheet tests pass with solidcore: planar region arcs keep their meeting points and close past a whole turn #17061.

The full suite on Linux still waits for approved-for-ci.

@artem-nerel

Copy link
Copy Markdown

A tab cut free by a U-shaped slot still can't be folded on this branch. This is the Fold case from #11903, and I'd like to help with it.

The part. A 120 × 80 mm plate, 2 mm thick. A U-shaped slot leaves a 35 × 40 mm tongue joined to the plate along one side. The fold line runs across the tongue's root, 2 mm into the tongue.

[{"command":"sheet.manage_rules","params":{"name":"AL2","thickness":"2","bend_radius":"2","k_factor":"0.43","active":true}},
 {"command":"sketch.create","params":{"plane":"XY","name":"Base"}},
 {"command":"sketch.line","params":{"points":[[-60,-40],[60,-40],[60,40],[-60,40]],"closed":true}},
 {"command":"sketch.line","params":{"points":[[-10,-25],[30,-25],[30,25],[-10,25],[-10,20],[25,20],[25,-20],[-10,-20]],"closed":true}},
 {"command":"sketch.finish","params":{}},
 {"command":"sheet.flange","params":{"sketch":"Base","profiles":[0]}},
 {"command":"sheet.fold","params":{"points":[[-8,-15,2],[-8,15,2]],"angle":"45 deg"}}]

What happens.

Build Plate only Plate with an edge flange on [0, 40, 2]
main (0.42.0) The whole strip beyond the line folds, not only the tongue. Refused: "a fold through a flange's bend".
This PR (#9245) Refused: "the fold cuts its panel into more than one piece on its fixed side: not supported yet". The same refusal.

Expected. Only the tongue folds. The plate around the slot stays flat, and the flat pattern keeps its size. The line's points pick the stretch of the bend that folds, as they already do for collinear tabs on an edge (#264).

What I have. I made this work on main in a local branch. The bend zone and the material beyond it are cut by the cut-outs first. A tab then folds alone when it touches the zone only along the line's stretch and touches the outline nowhere. Otherwise the fold behaves as before. Four tests cover it:

  • the tongue folds alone, both fold directions, with the slot drawn in the base sketch or cut later;
  • with an edge flange on the plate;
  • a hole drawn in the tongue stays cut after the fold;
  • unfold and refold give the same part back.

All existing sheet tests in solidcraft-doc and solidcraft-engine still pass.

This PR replaces the model that branch changes, so I won't open a PR against main. Would you take this as a follow-up after this PR merges, or would you rather fit it in here? I can port the tests and the change to the surface model either way.

@h0witzer

Copy link
Copy Markdown
Contributor Author

@artem-nerel thanks, yes please. This should land as a follow-up after #9245, which is under a scope freeze (fixes only) while it waits for CI. Please build it now against fix-6748-develop as a draft PR on the fork (h0witzer/solidcraft), the way the held PRs are being ported. It retargets to main the day #9245 merges.

What it is on the surface model. A folded tongue needs nothing new in the model: it is two panels (the plate and the tongue) joined by one bend, whose zone ends lie on the slot. Develop, settle, the flat pattern, the DXF and naming already handle that. The change is in how a fold picks the material it bends: plan_fold in crates/doc/src/sheet_surface_features.rs. Today it cuts the bend zone from the panel's outer outline only and cuts holes from it afterwards, so a cut-out inside the panel never ends a stretch of the bend, and the slot can't free the tongue.

The rule, in four steps:

  1. Chords. Continue the drawn line both ways across the panel. Each stretch of it through material is a chord, ending where the material ends: at the outline or at a cut-out.
  2. Pick. The chords the drawn stretch touches are picked, each whole. Drawing part of a chord bends all of it.
  3. Part. Cut the picked chords. Each must have a different piece on each of its two sides. Where one doesn't, because the material runs round a cut-out from one side of it to the other, the bend carries on along the line to the next chord, taking as few more chords as it needs. A fold therefore never fails because of where the cut-outs are; at worst it bends the whole line.
  4. Fold. One side of the line folds. Each piece freed on that side turns on its own bend, along its chords; where a piece hangs from several chords, the gaps between them are cut-outs across that one bend. A piece that still reaches across the line stays. The side that stays is chosen as today: the fixed point's side, or else the side with more material.

This replaces the outline-band pieces and three refusals: "a fold whose bend zone holds two pieces beyond it", "the fold cuts its panel into more than one piece on its fixed side" and "folding tabs that join again beyond the bend". In their place is the one parting test, and the #264 drawn-stretch path becomes step 2. Everything else stays as it is: the bend allowance and fold position, bend ends following the outline, the corner check when a zone reaches a corner of the outline, and the flat pattern.

Cases. The rule is AL2 (T 2, R 2, K 0.43). Each row uses your 120 × 80 plate unless it says otherwise, and "Today" is #9245 at 5c10d98.

Line Today With the rule
Plain plate, line drawn partway across The full width folds. Same.
Across a Ø20 hole on the line One bend in two stretches, with the hole across it. Same.
From the edge up to that hole The full width folds, with the hole across the bend. Same: the bend carries on across the hole.
From the left edge up to a slot that runs in from the top edge, past the line The flap between the edge and the slot folds alone. Same.
Your slot, line across the tongue's root only Refused. The tongue folds alone.
Your slot, line drawn full length Refused. The tongue and the ring around the slot fold, each on its own bend; the ring's bend has the slot across it.
Your slot, line across the tongue and the top strip only Refused. The top strip can't part, because the plate runs round the slot and back along the bottom strip. The bend carries on across the bottom strip, giving the same part as the full-length line.
Two tabs on one edge (#264), line across one That tab folds alone. Same.

Your four tests are the right ones; please port them as written, and add the rows above as tests too. The "Same" rows guard today's results, and the last three rows with your slot pin step 3. Please don't port the selection condition from your main branch ("touches the zone only along the line's stretch and touches the outline nowhere"). It tests the flat outline of a model that #9245 deletes, and the chords cover it.

This also covers the Fold route of #11903, since each collar tab's root runs from slit to slit. An edge flange on a hole's edge is still refused on #9245; that is a separate change.

I'll review the draft against the repository's rules before it merges into the fork.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:doc timeline, components, parameters, naming area:infra CI, tooling, repo, docs area:kernel B-rep kernel, booleans, blends, STEP/IGES area:ui desktop/web front end, dialogs, viewport

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants