From 354a267830804c6b481fd549d24c12d26cac3ed8 Mon Sep 17 00:00:00 2001 From: Jonas Wrede Date: Mon, 12 Oct 2026 18:18:59 +0200 Subject: [PATCH] test: failing regression test for #260: Targeted cut deletes another body that the tool fully covers (#275) * test: reproduce #260 (Targeted cut deletes another body that the tool fully covers) * Primitives honour `targets` for join/cut/intersect (#260) solid.box/cylinder/sphere/torus had no targets field, so a `targets` list was ignored and every body of the component took part: a targeted cut also cut a non-target body, and deleted one it covered fully. Their `targets` now become the feature's participants. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: bherbruck Co-authored-by: Claude Opus 5.5 --- crates/engine/src/tests.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 6acd799a..e72c3a8c 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -2511,6 +2511,31 @@ fn special_files_are_refused_by_every_file_command() { let _ = std::fs::remove_dir_all(&dir); } +/// A cut targeted at one body leaves a non-target body alone, even one the tool covers completely (#260). +#[test] +fn targeted_cut_keeps_a_covered_body_that_is_not_a_target() { + let mut s = Session::default(); + run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "operation": "new_body", "body_name": "Frame"})); + run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [20, 0, 0], "operation": "new_body", "body_name": "Other"})); + // Exactly Other's extent; it does not touch Frame. + run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [20, 0, 0], "operation": "cut", "targets": ["Frame"]})); + let names: Vec = s.model.state().bodies.iter().map(|b| b.name.clone()).collect(); + assert_eq!(names, ["Frame", "Other"]); + assert!(rel(volume(&mut s), 2000.0) < 1e-6); +} + +/// A frame's opening cut over a separate mesh body that sits in it: the mesh survives (#260). +#[test] +fn targeted_cut_keeps_an_overlapping_body_that_is_not_a_target() { + let mut s = Session::default(); + run(&mut s, "solid.box", json!({"length": 50, "width": 50, "height": 5, "operation": "new_body", "body_name": "Frame"})); + run(&mut s, "solid.box", json!({"length": 30, "width": 30, "height": 1, "corner": [10, 10, 2], "operation": "new_body", "body_name": "Mesh"})); + run(&mut s, "solid.box", json!({"length": 30, "width": 30, "height": 5, "corner": [10, 10, 0], "operation": "cut", "targets": ["Frame"]})); + let names: Vec = s.model.state().bodies.iter().map(|b| b.name.clone()).collect(); + assert_eq!(names, ["Frame", "Mesh"]); + assert!(rel(volume(&mut s), 50.0 * 50.0 * 5.0 - 30.0 * 30.0 * 5.0 + 900.0) < 1e-6); +} + /// A join with explicit `targets` merges into those bodies only; a touched non-target stays (#259). #[test] fn join_with_targets_leaves_other_touched_bodies_alone() {