Join a floor into the hole it fills: grow the boss into the wall around it (#1212).
This commit is contained in:
1 parent
a4a8b48567
commit
a629236650
6 files changed
+253
-22
No files matched your search
@@ -14,6 +14,8 @@ pub enum BooleanStrategy {
|
||||
ShiftedOperands,
|
||||
CoplanarSeparation,
|
||||
CommonBore,
|
||||
/// A boss in the other body's bore, grown into the material around it (nothing recut).
|
||||
GrownBoss,
|
||||
PlanarFallback,
|
||||
ToolNudge,
|
||||
Piecewise,
|
||||
@@ -34,6 +36,15 @@ pub struct BooleanFallback {
|
||||
pub tolerance_mm: f64,
|
||||
}
|
||||
|
||||
impl BooleanStrategy {
|
||||
/// Whether a result made this way can differ from the exact one: the tool was moved
|
||||
/// against the other body, or part of where they meet was ignored. The other retries work
|
||||
/// on temporary geometry or tolerances only, and their results are the exact ones.
|
||||
pub fn alters_result(self) -> bool {
|
||||
matches!(self, BooleanStrategy::ToolNudge | BooleanStrategy::UnverifiedShortcut)
|
||||
}
|
||||
}
|
||||
|
||||
impl BooleanFallback {
|
||||
pub(crate) fn new(strategy: BooleanStrategy) -> Self {
|
||||
Self { strategy, translation_mm: 0., rotation_rad: 0., face_shift_mm: 0., tolerance_mm: 0. }
|
||||
@@ -47,6 +58,7 @@ impl BooleanFallback {
|
||||
}
|
||||
BooleanStrategy::CoplanarSeparation => format!("temporary coincident-face separation up to {:.6e} mm per face", self.face_shift_mm),
|
||||
BooleanStrategy::CommonBore => format!("temporary bore offset {:.6e} mm, followed by recutting the original bore", self.face_shift_mm),
|
||||
BooleanStrategy::GrownBoss => format!("boss grown {:.6e} mm into the material around the bore it fills", self.face_shift_mm),
|
||||
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(),
|
||||
@@ -79,13 +91,26 @@ impl BooleanDiagnostics {
|
||||
self.events.iter().find(|event| event.strategy == BooleanStrategy::UnverifiedShortcut).map(BooleanFallback::description)
|
||||
}
|
||||
|
||||
/// Every retry the result took, for a message about the boolean (one that failed its
|
||||
/// checks lists what was tried).
|
||||
pub fn summary(&self) -> Option<String> {
|
||||
self.described(&self.events.iter().collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// The note a feature carries: the retries that can make its result differ from the exact
|
||||
/// one ([`BooleanStrategy::alters_result`]). Exact retries are in `events` all the same
|
||||
/// (`timeline.inspect`), but they leave the result as it should be: no reason to warn.
|
||||
pub fn warning(&self) -> Option<String> {
|
||||
if self.events.is_empty() && !self.truncated {
|
||||
self.described(&self.events.iter().filter(|e| e.strategy.alters_result()).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn described(&self, events: &[&BooleanFallback]) -> Option<String> {
|
||||
if events.is_empty() && !self.truncated {
|
||||
return None;
|
||||
}
|
||||
let mut descriptions: Vec<String> = self.events.iter().take(4).map(BooleanFallback::description).collect();
|
||||
if self.events.len() > 4 {
|
||||
descriptions.push(format!("{} additional retries", self.events.len() - 4));
|
||||
let mut descriptions: Vec<String> = events.iter().take(4).map(|e| e.description()).collect();
|
||||
if events.len() > 4 {
|
||||
descriptions.push(format!("{} additional retries", events.len() - 4));
|
||||
}
|
||||
if self.truncated {
|
||||
descriptions.push(format!("report capped at {MAX_EVENTS} retries"));
|
||||
@@ -169,6 +194,26 @@ mod tests {
|
||||
assert!(capture_boolean_diagnostics(|| {}).1.events.is_empty());
|
||||
}
|
||||
|
||||
/// Only retries whose result can differ from the exact one make a warning; the summary
|
||||
/// lists every retry.
|
||||
#[test]
|
||||
fn only_retries_that_alter_the_result_warn() {
|
||||
let report = |strategies: &[BooleanStrategy]| {
|
||||
capture_boolean_diagnostics(|| {
|
||||
for s in strategies {
|
||||
record(BooleanFallback::new(*s));
|
||||
}
|
||||
})
|
||||
.1
|
||||
};
|
||||
let exact = report(&[BooleanStrategy::ShiftedOperands, BooleanStrategy::GrownBoss, BooleanStrategy::CoplanarSeparation]);
|
||||
assert!(exact.warning().is_none(), "{exact:?}");
|
||||
assert!(exact.summary().is_some_and(|s| s.contains("shifted operands") && s.contains("boss grown")));
|
||||
let nudged = report(&[BooleanStrategy::GrownBoss, BooleanStrategy::ToolNudge]);
|
||||
let warning = nudged.warning().unwrap();
|
||||
assert!(warning.contains("tool displacement") && !warning.contains("boss grown"), "{warning}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_operations_and_failed_calls_have_no_retry_report() {
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
Reference in new issue
Block a user