test: failing regression test for #264: Sheet metal: collinear tabs cannot be folded or flanged together (#279)

* test: reproduce #264 (Sheet metal: collinear tabs cannot be folded or flanged together)

* fix(sheet): fold and flange tabs that sit side by side on one line (#264)

A fold's bend zone and a flange's setback were cut with an infinite line, so
a second tab on the same line blocked a fold ('slanted or curved edge') and
two tab-end flanges in one feature shared one trimmed edge (nonmanifold).
When the zone falls apart into pieces, fold only the pieces the fold points
span, each as its own bend; trim a flange's setback only from the material
along its own edge, and attach each flange to the edge it was picked on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-12 11:19:21 -04:00
1 parent 3b8028a040
commit 14163d0965
2 files changed
+216 -7

No files matched your search

+43
View File
@@ -1568,6 +1568,49 @@ fn outline_blank(s: &mut Session, outline: Value) {
run(s, "sheet.flange", json!({"sketch": "Blank"}));
}
/// A 100 x 40 blank with two 15 mm tabs on its right edge (y 5..12 and y 28..35), on one bend line.
fn two_tab_blank(s: &mut Session) {
outline_blank(
s,
json!([[0, 0], [100, 0], [100, 5], [115, 5], [115, 12], [100, 12], [100, 28], [115, 28], [115, 35], [100, 35], [100, 40], [0, 40]]),
);
}
/// Folding one tab must not be refused because another tab lies on the same line (#264).
#[test]
fn fold_one_of_two_collinear_tabs() {
let mut s = Session::default();
two_tab_blank(&mut s);
let flat = volume(&mut s);
run(&mut s, "sheet.fold", json!({"points": [[107, 5, 0.8], [107, 12, 0.8]], "fixed": [50, 20, 0.8]}));
assert!(rel(volume(&mut s), flat) < 1e-3, "a fold keeps the material");
}
/// One fold line across both tabs bends both (#264).
#[test]
fn fold_across_two_collinear_tabs() {
let mut s = Session::default();
two_tab_blank(&mut s);
let flat = volume(&mut s);
run(&mut s, "sheet.fold", json!({"points": [[107, 5, 0.8], [107, 35, 0.8]], "fixed": [50, 20, 0.8]}));
assert!(rel(volume(&mut s), flat) < 1e-3);
let bb = &run(&mut s, "inspect.measure", json!({}))["bodies"][0]["bbox"];
assert!(bb["max"][2].as_f64().unwrap() > 7.0, "both tabs stand up: {bb}");
}
/// Edge flanges on both tab ends in one feature (two separate features already work) (#264).
#[test]
fn edge_flanges_on_two_collinear_tab_ends_in_one_feature() {
let mut s = Session::default();
two_tab_blank(&mut s);
run(&mut s, "sheet.flange", json!({"edges": [[115, 8, 0.8], [115, 31, 0.8]], "height": "10"}));
let mut separate = Session::default();
two_tab_blank(&mut separate);
run(&mut separate, "sheet.flange", json!({"edges": [[115, 8, 0.8]], "height": "10"}));
run(&mut separate, "sheet.flange", json!({"edges": [[115, 31, 0.8]], "height": "10"}));
assert!(rel(volume(&mut s), volume(&mut separate)) < 1e-6);
}
/// `fixed` inside a cut-out touches no material: the fold must refuse it or say which side it kept (#263).
#[test]
fn fold_fixed_point_off_the_sheet_is_not_silently_ignored() {