Regression test: a box intersect splits the end off a raked wedge after its end rounds (#1617) (#1862)

Thanks @SychPL.
This commit is contained in:
Mateusz Sych authored and GitHub committed 2026-10-17 04:07:50 -04:00
1 parent 15a13e2452
commit 7f2ae11a06
1 file changed
+65
@@ -312,3 +312,68 @@ fn joined_loft_frames_plan_their_display_at_every_quality() {
}
}
}
/// The wedge of #1617: a side profile in (y, z) extruded 138 mm along x, its back face leaning
/// by `lean` (the tangent of its angle), with the issue's rounds. The last one runs along the
/// outline of both ends. The issue asked for R1 with `fit: reduce`, which reduces it to 0.854 mm;
/// that radius is asked for here directly, which builds the same body without the fit's search
/// (about a minute).
fn raked_wedge(lean: f64) -> Session {
let mut s = Session::default();
let top = 60.0 - 60.0 * lean;
let profile = [[-60.0, 0.0], [60.0, 0.0], [top, 60.0], [38.0, 60.0], [-22.0, 22.0], [-60.0, 22.0]];
let back = (60.0 + top) / 2.0;
let ends: Vec<[f64; 3]> = [-69.0, 69.0].iter().flat_map(|&x| [[x, -41.0, 22.0], [x, 8.0, 41.0], [x, (38.0 + top) / 2.0, 60.0]]).collect();
let steps = [
("sketch.create", json!({"plane": {"origin": [0, 0, 0], "x_dir": [0, 1, 0], "y_dir": [0, 0, 1]}, "name": "Profile"})),
("sketch.line", json!({"points": profile, "closed": true})),
("sketch.finish", json!({})),
("solid.extrude", json!({"distance": 138, "direction": "symmetric", "operation": "new", "body_name": "Wedge"})),
("solid.fillet", json!({"body": "Wedge", "radius": 1.2, "edges": [[0, -60, 22]]})),
("solid.fillet", json!({"body": "Wedge", "radius": 1.5, "edges": [[0, -22, 22]]})),
("solid.fillet", json!({"body": "Wedge", "radius": 1.5, "edges": [[0, 38, 60]]})),
("solid.fillet", json!({"body": "Wedge", "radius": 4, "edges": [[-69, -60, 12], [69, -60, 12], [-69, back, 30], [69, back, 30]]})),
("solid.fillet", json!({"body": "Wedge", "radius": 0.854, "edges": ends})),
];
for (command, params) in steps {
s.execute(command, &params).unwrap_or_else(|e| panic!("{command}: {e}"));
}
s
}
/// A box intersect taking the end slice x >= 55 off the wedge of #1617 failed with "no result"
/// once its ends were rounded, when its back face leaned 10 degrees (a vertical one worked).
/// The leaning back shortens the top edge the end round runs along to 11.4 mm. Its strip there
/// borrowed sections from that short neighbour and folded back on itself past the joint, and
/// the boolean could not intersect the folded faces (fixed with #1667). The slice and the
/// rest, cut off by the same box, are sound and add up to the wedge, with the cut plane where
/// the issue had it and moved by 0.02 mm, as it also tried.
#[test]
fn box_intersect_splits_the_end_off_a_raked_wedge_after_its_end_rounds() {
for lean in [10f64.to_radians().tan(), 0.0] {
let mut s = raked_wedge(lean);
let whole = volume(&s, "Wedge");
for (i, x0) in [55.0, 55.02].into_iter().enumerate() {
let mut piece = |op: &str| -> (f64, solidcraft_geom::Aabb3) {
let copy = s.execute("solid.move", &json!({"bodies": ["Wedge"], "translate": [0, 0, 0], "copy": true})).unwrap();
let copy = copy["created_bodies"][0].as_str().unwrap().to_string();
let cap = format!("Cap{i}{op}");
let length = 80.0 - x0;
let cap_box = json!({"center": [x0 + length / 2.0, 0, 40], "length": length, "width": 160, "height": 120, "operation": "new", "body_name": cap});
s.execute("solid.box", &cap_box).unwrap();
let (target, tool) = if op == "intersect" { (cap, copy) } else { (copy, cap) };
s.execute("solid.combine", &json!({"target": target, "tools": [tool], "operation": op}))
.unwrap_or_else(|e| panic!("lean {lean}, x >= {x0}, {op}: {e}"));
let state = s.model.state();
let body = &state.body(&target).unwrap().body;
assert!(body.validity().is_empty(), "lean {lean}, x >= {x0}, {op}: {:?}", body.validity());
(solidcraft_kernel::measure(body).unwrap().volume, body.tessellate(1e-3).unwrap().bounds())
};
let (slice, at) = piece("intersect");
let (rest, _) = piece("cut");
println!("#1617 lean {lean:.4}, x >= {x0}: slice {slice:.6} + rest {rest:.6} = {:.6}, wedge {whole:.6} mm³", slice + rest);
assert!((slice + rest - whole).abs() < 1e-6 * whole, "{slice} + {rest} vs {whole}");
assert!((at.min.x - x0).abs() < 1e-6 && (at.max.x - 69.0).abs() < 1e-3, "slice spans x {} to {}", at.min.x, at.max.x);
}
}
}