fix: edge flanges trim the base along their own edge and relieve bends that end beside it (#634)
An edge flange whose edge ends at an inside corner of the base now trims only the strip under its own edge, so legs and stems past that corner stay. Where the bend ends beside more of the base, the rule's bend relief (relief_width, relief_depth) is cut into the base, or a note says why it was left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
24cff445ad
commit
90869732a3
3 files changed
+343
-15
No files matched your search
@@ -1940,6 +1940,72 @@ fn edge_flanges_on_two_collinear_tab_ends_in_one_feature() {
|
||||
assert!(rel(volume(&mut s), volume(&mut separate)) < 1e-6);
|
||||
}
|
||||
|
||||
/// A 160 x 30 bar with a 20 x 30 leg standing past its top edge at the left end (2 mm, R 1,
|
||||
/// K 0.5, so folding keeps the volume): a base flange, or a plate converted to sheet metal.
|
||||
fn bar_with_leg(s: &mut Session, converted: bool) {
|
||||
run(s, "sheet.manage_rules", json!({"name": "AL2", "thickness": "2 mm", "k_factor": 0.5, "bend_radius": "1 mm", "active": true}));
|
||||
run(s, "sketch.create", json!({"plane": "XY", "name": "Bar"}));
|
||||
run(s, "sketch.line", json!({"points": [[0, 0], [160, 0], [160, 30], [20, 30], [20, 60], [0, 60]], "closed": true}));
|
||||
run(s, "sketch.finish", json!({}));
|
||||
if converted {
|
||||
run(s, "solid.extrude", json!({"sketch": "Bar", "distance": 2, "body_name": "Plate"}));
|
||||
run(s, "sheet.convert", json!({"body": "Plate", "face": [80, 15, 2]}));
|
||||
} else {
|
||||
run(s, "sheet.flange", json!({"sketch": "Bar"}));
|
||||
}
|
||||
}
|
||||
|
||||
/// An edge flange takes its setback off its own edge only: the leg standing past the edge's end
|
||||
/// stays, and the rule's bend relief frees the bend and the flange from it.
|
||||
#[test]
|
||||
fn an_edge_flange_beside_a_leg_of_the_base_trims_its_own_edge_and_relieves_its_bend() {
|
||||
// Bend allowance (π/2)·(R + K·T) = π; the rule's relief is Thickness wide, half of it deep.
|
||||
let (ba, width, depth) = (PI, 2.0, 1.0);
|
||||
for (converted, position, setback) in [(false, "inside", 3.0), (false, "outside", 0.0), (true, "inside", 3.0)] {
|
||||
let what = format!("converted {converted}, {position}");
|
||||
let mut s = Session::default();
|
||||
bar_with_leg(&mut s, converted);
|
||||
// Where the sheet's bottom face lies.
|
||||
let z0 = bbox(&mut s)["min"][2].as_f64().unwrap_or(f64::NAN);
|
||||
// A 90° flange 15 high on the bar's top edge (x 20…160); the leg stands past its left end.
|
||||
run(&mut s, "sheet.flange", json!({"edges": [[90, 30, z0 + 2.0]], "height": 15, "angle": 90, "position": position}));
|
||||
assert!(no_errors_or_warnings(&s), "{what}: {:?}", s.model.results);
|
||||
// By hand: the base loses the setback under that edge alone, and the relief beside the
|
||||
// bend's end at x = 20, from `depth` before the bend line to `width` past the flange's
|
||||
// far edge in the flat (BA and the 12 mm panel past the line).
|
||||
let (line, panel) = (30.0 - setback, 12.0);
|
||||
let relief = width * (depth + ba + panel + width);
|
||||
let base = 160.0 * 30.0 + 20.0 * 30.0 - 140.0 * setback - relief;
|
||||
let want = 2.0 * (base + 140.0 * (ba + panel));
|
||||
assert!(rel(volume(&mut s), want) < 1e-5, "{what}: {} vs {want}", volume(&mut s));
|
||||
assert!(contains(&s, [10.0, 50.0, z0 + 1.0]), "{what}: the leg is kept");
|
||||
assert!(!contains(&s, [19.0, line + 0.5, z0 + 1.0]), "{what}: the relief is cut");
|
||||
assert!(contains(&s, [17.0, line + 0.5, z0 + 1.0]), "{what}: the leg beside the relief stays");
|
||||
assert!(contains(&s, [19.0, 50.0, z0 + 1.0]), "{what}: the leg past the relief's end stays");
|
||||
// The flange's mid plane is R + T/2 = 2 past the bend line; it ends at the leg.
|
||||
assert!(contains(&s, [90.0, line + 2.0, z0 + 10.0]), "{what}: the flange stands");
|
||||
assert!(!contains(&s, [10.0, line + 2.0, z0 + 10.0]), "{what}: the flange ends at the leg");
|
||||
let f = flat(&mut s);
|
||||
assert_eq!(f["outline"].as_array().map(Vec::len), Some(1), "{what}: {f}");
|
||||
assert_eq!(f["cutouts"].as_array().map(Vec::len), Some(0), "{what}: {f}");
|
||||
assert!((f["bends"][0]["length"].as_f64().unwrap_or(f64::NAN) - 140.0).abs() < 1e-9, "{what}: {f}");
|
||||
run(&mut s, "sheet.unfold", json!({}));
|
||||
assert!(rel(volume(&mut s), want) < 1e-9, "{what}: flat {} vs {want}", volume(&mut s));
|
||||
}
|
||||
}
|
||||
|
||||
/// A bend relief the rule makes too narrow to cut is left out, and the flange says where.
|
||||
#[test]
|
||||
fn a_bend_relief_left_out_is_reported_on_the_flange() {
|
||||
let mut s = Session::default();
|
||||
bar_with_leg(&mut s, false);
|
||||
run(&mut s, "sheet.manage_rules", json!({"name": "AL2", "relief_width": "0 mm"}));
|
||||
run(&mut s, "sheet.flange", json!({"edges": [[90, 30, 2]], "height": 15, "angle": 90}));
|
||||
let w = s.model.results.last().and_then(|r| r.warning.clone()).unwrap_or_default();
|
||||
assert!(w.contains("no bend relief at (20.000, 27.000, 0.000)") && w.contains("relief width"), "{w}");
|
||||
assert!(contains(&s, [10.0, 50.0, 1.0]), "the leg is kept");
|
||||
}
|
||||
|
||||
/// `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() {
|
||||
|
||||
Reference in new issue
Block a user