From 3d38753cad31ff9b11c84e04f3c2ba57eab82dff Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:19:23 -0400 Subject: [PATCH] geometry: a partition the kernel can only nudge off its plane is refused (#427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel's last-resort boolean retry moves the tool 1.3 µm and turns it 2 µrad. For a partition that puts the cut beside the face it was meant to run along and leaves a cell bounded by slivers (the open boundary at x = 130 on the crank half). halves() now refuses a result-altering retry and names it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo --- crates/engine/src/cmd/datum.rs | 13 ++++++----- crates/engine/src/sample.rs | 35 ++++++++++++++++++++++++++++++ crates/geometry/src/partition.rs | 13 ++++++++++- skills/fieldcraft-meshing/SKILL.md | 3 ++- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/crates/engine/src/cmd/datum.rs b/crates/engine/src/cmd/datum.rs index b6b3bef..a4a337a 100644 --- a/crates/engine/src/cmd/datum.rs +++ b/crates/engine/src/cmd/datum.rs @@ -945,7 +945,8 @@ pub(crate) mod tests { /// #384: cuts stay on their plane (the kernel's own split put them 1.8 µm off it on the air /// of the enclosure, so the cut faces were not even recognized), and a plane along an - /// existing face or through a hole's axis is a warning naming the plane, the body and where. + /// existing face or through a hole's axis is a warning naming the plane, the body and where, + /// or refused where the kernel could only cut it off the plane. #[test] fn cuts_stay_on_the_plane_and_risky_ones_are_named() { let (_dir, mut s) = pi5(); @@ -972,11 +973,11 @@ pub(crate) mod tests { assert!(!cut.is_empty() && cut.iter().all(|x| (x - 16.5).abs() < 1e-9), "{cell}: {cut:?}"); } s.execute("edit.undo", &json!({})).unwrap(); - // Along the side of the heat sink's pedestal: named. - let r = plane(&mut s, "Side", 34.0); - let warnings = r["warnings"].to_string(); - assert!(warnings.contains("partition `PSide`: the plane lies on face `F45:-x` of `Air`"), "{r}"); - s.execute("edit.undo", &json!({})).unwrap(); + // Along the side of the heat sink's pedestal: the kernel only cuts with its box nudged + // off the plane, so the cut is refused, saying why (#427). + s.execute("geometry.datum", &json!({"name": "Side", "by": {"type": "offset", "plane": "yz", "offset": 34.0}})).unwrap(); + let e = s.execute("geometry.partition", &json!({"name": "PSide", "datum": "Side", "bodies": ["Air"]})).unwrap_err().to_string(); + assert!(e.contains("could not cut `Air`") && e.contains("only made off the plane"), "{e}"); // Through a standoff's hole: the cell whose boundary breaks at the hole's edge is named. let r = plane(&mut s, "Axis", 74.5); let warnings = r["warnings"].to_string(); diff --git a/crates/engine/src/sample.rs b/crates/engine/src/sample.rs index 08dce3b..8353a58 100644 --- a/crates/engine/src/sample.rs +++ b/crates/engine/src/sample.rs @@ -769,6 +769,41 @@ mod tests { } } + /// A cut at x = 130 runs along the pocket's end wall (#427), and the kernel only makes it + /// with its cutting box nudged 1.3 µm and 2 µrad off the plane: on the half y > 0 that left a + /// cell bounded by slivers next to the wall, which `mesh.cells` refused with an open boundary + /// at x = 130. The partition refuses such a cut, saying why, on the whole crank and on the + /// half, and leaves the body whole; the plane moved past the pocket (x = 136) cuts the half + /// into cells that mesh. + #[test] + fn a_cut_the_kernel_can_only_nudge_off_its_plane_is_refused() { + let datum = |name: &str, x: f64| json!({"name": name, "by": {"type": "offset", "plane": "yz", "offset": x}}); + let refused = |s: &mut Session, body: &str| { + let e = s.execute("geometry.partition", &json!({"name": "X130", "datum": "X130", "bodies": [body]})).unwrap_err().to_string(); + assert!(e.contains(&format!("could not cut `{body}`")) && e.contains("only made off the plane"), "{e}"); + }; + let names = |s: &mut Session| -> Vec { + let r = s.execute("geometry.bodies", &json!({})).unwrap(); + r.as_array().unwrap().iter().filter_map(|b| b["name"].as_str().map(String::from)).collect() + }; + let bytes = fieldcraft_geometry::script_design(CRANK_ARM).unwrap(); + let (_dir, path) = crate::tests::design_file(&bytes); + let mut whole = Session::default(); + whole.execute("geometry.open", &json!({"path": path})).unwrap(); + whole.execute("geometry.datum", &datum("X130", 130.0)).unwrap(); + refused(&mut whole, "CrankArm"); + assert_eq!(names(&mut whole), ["CrankArm"]); + + let mut s = open("crank-mirror"); + s.execute("geometry.datum", &datum("X130", 130.0)).unwrap(); + refused(&mut s, "CrankArm/b"); + assert_eq!(names(&mut s), ["CrankArm/a", "CrankArm/b"]); + s.execute("geometry.datum", &datum("X136", 136.0)).unwrap(); + let r = s.execute("geometry.partition", &json!({"name": "X136", "datum": "X136", "bodies": ["CrankArm/b"]})).unwrap(); + assert_eq!(r["cells"], json!(["CrankArm/b/a", "CrankArm/b/b"]), "{r}"); + s.execute("mesh.cells", &json!({"bodies": ["CrankArm/b/a", "CrankArm/b/b"], "default": "extrude", "size": 3})).unwrap(); + } + /// The whole crank extruded all-hex (#198): its back pocket's floor is a rounded rectangle, /// straight sides running tangent into corner arcs, so the region is a disk whose O-grid's /// middle block follows its 6:1 elongation; a square middle block squashed its elements to a diff --git a/crates/geometry/src/partition.rs b/crates/geometry/src/partition.rs index 4071001..f1489f0 100644 --- a/crates/geometry/src/partition.rs +++ b/crates/geometry/src/partition.rs @@ -187,6 +187,10 @@ impl Design { /// several times the body's size, and its boolean then puts the cut up to a few micrometres off /// the plane (1.8 µm on a 140 mm body), off the faces it should meet (#384); boxes of the body's own /// size keep the cut on the plane. +/// +/// A boolean the kernel could only make by moving one body against the other (its last-resort +/// tool nudge, micrometres and microradians) is refused: the cut would lie off the plane, next to +/// the face it was meant to run along, and leave a cell bounded by slivers no mesher takes (#427). fn halves(body: &Shape, plane: &Plane) -> solidcraft_kernel::Result> { let bbox = solidcraft_kernel::measure(&body.body)?.bbox; let reach = bbox.diagonal().max(LINEAR_REACH); @@ -203,7 +207,14 @@ fn halves(body: &Shape, plane: &Plane) -> solidcraft_kernel::Result