From 7d7ef18f49675e0641a93afe82a0170f7dc93a8f Mon Sep 17 00:00:00 2001 From: Jonas Wrede Date: Mon, 12 Oct 2026 16:08:38 +0200 Subject: [PATCH] test: failing regression test for #262: sheet.fold: bend zone reaching the blank outline is reported as a slanted or curved edge (#277) * test: reproduce #262 (sheet.fold: bend zone reaching the blank outline is reported as a slanted or curved edge) * fix(sheet): name the bend zone reaching an outline corner instead of blaming a slanted edge (#262) A fold whose bend zone contains a corner of the panel outline (a fold at a tab's root) was reported as crossing a slanted or curved edge. Name the corner and how far to move the fold line to clear it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT --------- Co-authored-by: bherbruck Co-authored-by: Claude Opus 5.5 --- crates/doc/src/sheet.rs | 13 +++++++++++++ crates/engine/src/cmd/sheet_tests.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/crates/doc/src/sheet.rs b/crates/doc/src/sheet.rs index d34bc152..d06c1451 100644 --- a/crates/doc/src/sheet.rs +++ b/crates/doc/src/sheet.rs @@ -1383,6 +1383,19 @@ impl SheetBody { let us: Vec = strip.iter().map(|q| q.dot(d)).collect(); let (u0, u1) = (us.iter().cloned().fold(f64::MAX, f64::min), us.iter().cloned().fold(f64::MIN, f64::max)); if ((u1 - u0) * ba - signed_area(&strip).abs()).abs() > 1e-6 * (u1 - u0).max(1.0) * ba.max(1.0) { + // A corner of the outline inside the zone (e.g. a fold right at a tab's root): name + // it and how far the line has to move to clear it. + let corners: Vec<(Vec2, f64)> = pts.iter().map(|q| (*q, q.dot(n) - s0)).filter(|(_, s)| *s > 1e-6 && *s < ba - 1e-6).collect(); + if let Some(&(c, _)) = corners.first() { + let on = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(*s)); + let back = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(ba - *s)); + let w = apply_point(&self.world(panel), Vec3::new(c.x, c.y, self.t)); + return Err(DocError::Invalid(format!( + "the bend zone ({ba:.3} mm wide) reaches a corner of the sheet's outline at ({:.3}, {:.3}, {:.3}): \ + move the fold line at least {on:.3} mm toward the side that folds or {back:.3} mm toward the side that stays", + w.x, w.y, w.z + ))); + } return Err(DocError::Invalid("not supported yet: folds whose bend zone crosses a slanted or curved edge of the sheet".into())); } let up = !flip; diff --git a/crates/engine/src/cmd/sheet_tests.rs b/crates/engine/src/cmd/sheet_tests.rs index 1b185501..0fdef519 100644 --- a/crates/engine/src/cmd/sheet_tests.rs +++ b/crates/engine/src/cmd/sheet_tests.rs @@ -1558,3 +1558,29 @@ fn partial_depth_intersect_sub_extrusion_resolution_layers_refuse_atomically() { assert!((solvecraft_kernel::measure(&s.world_state().body(&name).unwrap().body).unwrap().volume - 15000.0).abs() < 1e-5); } } + +/// A 0.8 mm steel rule and a base flange from a closed outline on XY (the sheet spans z 0..0.8). +fn outline_blank(s: &mut Session, outline: Value) { + run(s, "sheet.manage_rules", json!({"name": "Steel 0.8", "thickness": "0.8", "active": true})); + run(s, "sketch.create", json!({"plane": "XY", "name": "Blank"})); + run(s, "sketch.line", json!({"points": outline, "closed": true})); + run(s, "sketch.finish", json!({})); + run(s, "sheet.flange", json!({"sketch": "Blank"})); +} + +/// A tab whose fold line sits exactly at its root: the bend zone reaches the blank outline beside +/// the tab. Every edge is straight, so the error must not blame a slanted or curved edge (#262). +#[test] +fn fold_at_a_tab_root_names_the_real_cause() { + let mut s = Session::default(); + outline_blank(&mut s, json!([[0, 0], [100, 0], [100, 50], [60, 50], [60, 70], [40, 70], [40, 50], [0, 50]])); + if let Err(e) = s.execute("sheet.fold", &json!({"points": [[40, 50, 0.8], [60, 50, 0.8]], "fixed": [50, 25, 0.8]})) { + let e = e.to_string(); + assert!(!e.contains("slanted or curved"), "all edges are straight: {e}"); + assert!(e.contains("bend zone"), "name the bend zone reaching the outline: {e}"); + } + // 1 mm into the tab the same fold works. + let mut ok = Session::default(); + outline_blank(&mut ok, json!([[0, 0], [100, 0], [100, 50], [60, 50], [60, 70], [40, 70], [40, 50], [0, 50]])); + run(&mut ok, "sheet.fold", json!({"points": [[40, 51, 0.8], [60, 51, 0.8]], "fixed": [50, 25, 0.8]})); +}