Join walls swept along splines in 2D (#908)

Walls swept along a spline have flat B-spline tops and bottoms, so booleans between them went to the general intersection, which fails on coincident spline faces. Such faces are now put on true planes, and two prisms of the same height and direction are decided in 2D with their spline outlines fitted by arcs within a micron. The result is kept only when its volume is plausible; otherwise the general boolean takes over. The free-form flag is cleared by a drop guard so an unwinding boolean can't leave it set.

Fixes #844.
This commit is contained in:
Giovanni authored and GitHub committed 2026-10-13 20:48:27 -04:00
1 parent 3f1c023563
commit 57d738eac4
8 files changed
+413 -1

No files matched your search

+19
View File
@@ -432,7 +432,26 @@ fn prism_result(a: &Body, b: &Body, op: BoolOp, size: f64, counts: &[(usize, usi
})
})
}
/// Walls swept along splines have flat B-spline tops and bottoms. With those made planes the
/// operands are prisms, and two of them (same height, one direction) are decided in 2D: fast, and
/// with no intersection of spline surfaces, which is what fails on them. The outlines are fitted
/// by arcs within a micron (`prism_boolean_free_form`). `None` when the operands have no such
/// face, are not such prisms, or the answer is implausible: the general boolean then takes over.
fn swept_prism(a: &Body, b: &Body, op: BoolOp) -> Option<Body> {
let (pa, pb) = (crate::planar_faces::planarized(a), crate::planar_faces::planarized(b));
if pa.is_none() && pb.is_none() {
return None;
}
let (ra, rb) = (pa.as_ref().unwrap_or(a), pb.as_ref().unwrap_or(b));
let Some(Ok(Some(body))) = crate::prism::prism_boolean_free_form(ra, rb, op) else { return None };
let (va, vb) = (volume(a), volume(b));
plausible(volume(&body), op, va, vb, 2e-3 * (va + vb) + 1e-9).then_some(body)
}
fn boolean_whole(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
if let Some(body) = swept_prism(a, b, op) {
return Ok(Some(body));
}
a.require_brep("a boolean")?;
b.require_brep("a boolean")?;
let size = a.size().max(b.size());