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 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-28 22:27:31 -04:00
committed by Irakli Kourtis
parent 1fb5e94da1
commit 9be29894e9
+20 -1
View File
@@ -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<Option<Body>> {
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<Body> {
})
}
/// 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<bool> {
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<Option<Body>>) -> Result<Option<Body>> {