Planar cuts beside rounds take the exact route before the boolean retries (#1197).
This commit is contained in:
1 parent
56747edb46
commit
17b48d9e7e
4 files changed
+153
-3
No files matched your search
@@ -431,7 +431,7 @@ pub fn planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
|
||||
/// two parallel planes holds `b` and none of `a`'s curved faces (a pocket cut beside a round
|
||||
/// hole): `a` is split across the slab, the middle piece takes the exact boolean, and the
|
||||
/// pieces outside are joined back on. `None` when no such slab exists.
|
||||
fn sliced_planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Option<Result<Option<Body>>> {
|
||||
pub(crate) fn sliced_planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Option<Result<Option<Body>>> {
|
||||
let pb = planar_polys(b, 1).ok()?;
|
||||
let size = a.size().max(b.size());
|
||||
let tol = (a.size() * 1e-3).max(1e-3);
|
||||
@@ -447,6 +447,10 @@ fn sliced_planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Option<Result<Option
|
||||
bx.add(p);
|
||||
}
|
||||
}
|
||||
// All planar: the exact boolean itself, not this.
|
||||
if curved.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut tool = solidcraft_geom::Aabb3::EMPTY;
|
||||
for p in pb.iter().flat_map(|p| &p.v) {
|
||||
tool.add(*p);
|
||||
@@ -504,6 +508,13 @@ fn sliced_planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Option<Result<Option
|
||||
for (slot, (cut, keep_positive)) in [(2, (lo, true)), (4, (hi, false))] {
|
||||
let Some(x) = cut else { continue };
|
||||
let plane = solidcraft_geom::Plane::new(axis * x, u, w).ok_or_else(|| fail("plane"))?;
|
||||
// Split along the plane (an imprint: no boolean); two booleans with half boxes
|
||||
// where the split can't be sorted into one piece kept and the rest away.
|
||||
if let Some((away, kept)) = split_beside(&middle, &plane, axis, x, keep_positive) {
|
||||
outside.extend(away);
|
||||
middle = kept;
|
||||
continue;
|
||||
}
|
||||
let (away, kept) = (crate::ops::half_box(&middle.0, &plane, !keep_positive)?, crate::ops::half_box(&middle.0, &plane, keep_positive)?);
|
||||
let (h_away, h_kept) = (recorded::kept(&away, slot, n), recorded::kept(&kept, slot + 1, n));
|
||||
if let Some(o) = crate::ops::boolean(&middle.0, &away, BoolOp::Intersect)? {
|
||||
@@ -584,6 +595,41 @@ fn sliced_planar_boolean(a: &Body, b: &Body, op: BoolOp) -> Option<Result<Option
|
||||
Some(run())
|
||||
}
|
||||
|
||||
/// `piece` split by `plane` (at `x` along `axis`): the parts on the side away from the tool
|
||||
/// and the one part on its side (positive along `axis` when `keep_positive`), each with its
|
||||
/// history over the operands `piece`'s is over. `None` when the split leaves no part on either
|
||||
/// side, or several on the tool's.
|
||||
fn split_beside(piece: &Traced, plane: &solidcraft_geom::Plane, axis: Vec3, x: f64, keep_positive: bool) -> Option<(Vec<Traced>, Traced)> {
|
||||
let parts = crate::splitface::split_body(&piece.0, &crate::splitface::SplitTool::Plane(*plane)).ok()?;
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let tol = piece.0.size() * 1e-6 + 1e-9;
|
||||
let (mut away, mut kept) = (Vec::new(), Vec::new());
|
||||
for part in parts {
|
||||
let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
|
||||
for v in part.solid.vertex_iter() {
|
||||
let t = from_p3(v.point()).dot(axis) - x;
|
||||
lo = lo.min(t);
|
||||
hi = hi.max(t);
|
||||
}
|
||||
let h = match (part.history(), piece.1.as_ref()) {
|
||||
(Some(hp), Some(h)) => recorded::composed(hp.clone().substituted(0, h)),
|
||||
_ => None,
|
||||
};
|
||||
let positive = if lo >= -tol {
|
||||
true
|
||||
} else if hi <= tol {
|
||||
false
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
if positive == keep_positive { kept.push((part, h)) } else { away.push((part, h)) }
|
||||
}
|
||||
let kept = kept.pop().filter(|_| kept.is_empty())?;
|
||||
(!away.is_empty()).then_some((away, kept))
|
||||
}
|
||||
|
||||
/// `h` over `[a, b]`: the operands after them (the boxes a piece was cut with) dropped, which
|
||||
/// fails where something of them is left.
|
||||
fn dropped(h: Option<History>) -> Option<History> {
|
||||
|
||||
Reference in new issue
Block a user