From 9be29894e959e9ee32f45b173b93b9e88fdcecdd Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 28 Oct 2026 22:27:31 -0400 Subject: [PATCH] kernel: a cut by a tool apart from the body is the body itself The truck booleans answered a tool clear of the body without intersecting (the operand kept, its record shared). The SMLib boolean always rebuilt the body, so a cut that could not reach a body still handed back a new record: a hole far from a second body reported that body changed (#2904's test), and a body clear of a pocket's cutter was no longer the same solid. A cut or intersection whose operands are apart (their exact boxes, grown by twice the tolerance as union_apart takes them, do not meet) now gives the body itself with its faces and edges kept, or nothing, with no SMLib call. Co-Authored-By: Claude Opus 5.5 --- crates/kernel/src/ops.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/kernel/src/ops.rs b/crates/kernel/src/ops.rs index 053368f669..eff7ee3db9 100644 --- a/crates/kernel/src/ops.rs +++ b/crates/kernel/src/ops.rs @@ -21,9 +21,18 @@ pub enum BoolOp { /// everything or an intersection of disjoint bodies. A result SMLib's check refuses is an error. /// /// The result carries its exact history ([`Body::history`]) over the operands `[a, b]`, from -/// SMLib's own tags, or none where a body's order cannot be matched to SMLib's. +/// SMLib's own tags, or none where a body's order cannot be matched to SMLib's. A cut by a tool +/// apart from the body is the body itself (its faces and edges kept), and their intersection empty. pub fn boolean(a: &Body, b: &Body, op: BoolOp) -> Result> { crate::profile::timed(crate::profile::Kind::Boolean, "boolean", || { + // A tool apart from the body (their exact boxes, grown by the tolerances, do not meet) + // leaves a cut the body itself and an intersection nothing, with no intersection work. + if matches!(op, BoolOp::Cut | BoolOp::Intersect) && apart(a, b)? { + return Ok(match op { + BoolOp::Cut => Some(crate::boolean_record::operand(a, 0, &crate::record::counts(&[a, b]))), + _ => None, + }); + } let r = guard("SMLib boolean", || crate::smlib_boolean::smlib_boolean(a, b, op)); with_operand_roles(a, b, op, r) }) @@ -44,6 +53,16 @@ pub fn union_all(bodies: &[&Body]) -> Result { }) } +/// Whether B-rep bodies `a` and `b` are apart: their exact boxes, each grown by twice its +/// record's tolerance (as [`union_apart`] takes them), do not meet. +fn apart(a: &Body, b: &Body) -> Result { + if a.is_mesh() || b.is_mesh() { + return Ok(false); + } + let grown = |x: &Body| x.exact_bounds().map(|bb| bb.inflated(2.0 * x.record().tolerance)); + Ok(!grown(a)?.meets(&grown(b)?)) +} + /// A boolean's result with what its operands' faces stood for carried over: regularization /// protection and the planar construction and shell roles on supports it kept. fn with_operand_roles(a: &Body, b: &Body, op: BoolOp, r: Result>) -> Result> {