fix(kernel): a small overlap is not taken for bodies apart or inside (#484)

When sample points say two bodies are apart or one is inside the other, the facetings are now checked for surfaces passing through each other; if they do, the boolean is computed instead of shortcut. If it still can't be intersected, the sampled answer is kept as a flagged, unverified result, and Interference lists such pairs as "could not be checked" with the reason. Fixes #442 and #443.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Scott BowdenandClaude Opus 5.5 authored and GitHub committed 2026-10-13 00:13:16 -04:00
1 parent 340ac0313a
commit 3159fc0dd0
8 files changed
+406 -25

No files matched your search

+13
View File
@@ -17,6 +17,9 @@ pub enum BooleanStrategy {
PlanarFallback,
ToolNudge,
Piecewise,
/// No retry gave a result, and the answer from sample points was kept although the bodies'
/// facetings disagree with it.
UnverifiedShortcut,
}
/// Numerical adjustments used by a successful retry, in millimetres/radians.
@@ -47,6 +50,10 @@ impl BooleanFallback {
BooleanStrategy::PlanarFallback => "exact planar fallback (no displacement)".into(),
BooleanStrategy::ToolNudge => format!("tool displacement {:.6e} mm, rotation {:.6e} rad", self.translation_mm, self.rotation_rad),
BooleanStrategy::Piecewise => "piecewise retry (no displacement)".into(),
BooleanStrategy::UnverifiedShortcut => format!(
"not verified: the bodies' facets pass through each other by more than {:.6e} mm, but they could not be intersected there; that part is ignored",
self.tolerance_mm
),
}
}
}
@@ -66,6 +73,12 @@ impl BooleanDiagnostics {
}
}
/// Why the result is not verified, when it ignores a place where the bodies seem to pass
/// through each other ([`BooleanStrategy::UnverifiedShortcut`]).
pub fn unverified(&self) -> Option<String> {
self.events.iter().find(|event| event.strategy == BooleanStrategy::UnverifiedShortcut).map(BooleanFallback::description)
}
pub fn warning(&self) -> Option<String> {
if self.events.is_empty() && !self.truncated {
return None;