Extrude: a cut takes in a hole of its profile that lies over empty space (#1472)
Checked locally on main merged with this: cargo check --workspace, the kernel and doc tests, and all robustness goldens (all_0 to all_3) pass.
This commit is contained in:
1 parent
39f6d68a50
commit
6b30326e6d
2 files changed
+90
No files matched your search
@@ -1715,6 +1715,53 @@ fn region_samples(r: &Region2) -> Vec<Vec2> {
|
||||
out
|
||||
}
|
||||
|
||||
/// The region with each hole filled whose inside the tool would sweep with no change to the
|
||||
/// targets: all empty for a cut (the profile drawn round a hole in the part), all material for a
|
||||
/// join. Then the tool's walls don't lie on the part's walls there (the boolean can't intersect
|
||||
/// walls lying on each other, #1360), and what it cuts or joins is the same. A hole is filled only
|
||||
/// when points across it at levels through the tool are all empty (or all material) and no
|
||||
/// vertex of the targets lies inside the column over it (an island in it, a floor).
|
||||
fn fill_void_holes(st: &ModelState, r: &Region2, op: Operation, targets: &[String], plane: &Plane, (lo, hi): (f64, f64)) -> Region2 {
|
||||
if r.holes.is_empty() || !matches!(op, Operation::Cut | Operation::Join) || hi - lo <= 0.0 {
|
||||
return r.clone();
|
||||
}
|
||||
let bodies: Vec<&ModelBody> = st.bodies.iter().filter(|b| targets.is_empty() || targets.contains(&b.name)).collect();
|
||||
if bodies.is_empty() {
|
||||
return r.clone();
|
||||
}
|
||||
let meshes: Vec<Arc<Mesh>> = bodies.iter().map(|b| b.mesh()).collect();
|
||||
let size = bodies.iter().map(|b| b.body.size()).fold(0.0, f64::max);
|
||||
let margin = (size * 1e-4).max(1e-4);
|
||||
let n = plane.normal();
|
||||
let inside_any = |p: Vec3| meshes.iter().any(|m| m.contains(p));
|
||||
let want = op == Operation::Join;
|
||||
let mut holes = Vec::with_capacity(r.holes.len());
|
||||
for h in &r.holes {
|
||||
let column = Region2 { outer: h.ccw(), holes: vec![] };
|
||||
let samples = region_samples(&column);
|
||||
let swept = (1..=8).all(|k| {
|
||||
let z = lo + (hi - lo) * (k as f64 - 0.5) / 8.0;
|
||||
samples.iter().all(|s| inside_any(plane.to_world(*s) + n * z) == want)
|
||||
});
|
||||
// No boundary of the targets within the column (shrunk off its wall, short of its ends).
|
||||
let clear = swept
|
||||
&& meshes.iter().all(|m| {
|
||||
m.positions.iter().all(|p| {
|
||||
let z = (*p - plane.origin).dot(n);
|
||||
let q = plane.to_local(*p);
|
||||
!(z > lo + margin
|
||||
&& z < hi - margin
|
||||
&& column.contains(q)
|
||||
&& h.segs.iter().all(|sg| sg.point_at(sg.closest_param(q)).dist(q) > margin))
|
||||
})
|
||||
});
|
||||
if !clear {
|
||||
holes.push(h.clone());
|
||||
}
|
||||
}
|
||||
Region2 { outer: r.outer.clone(), holes }
|
||||
}
|
||||
|
||||
/// Grow profile edges that lie on a face of a target body away from the profile, by a small
|
||||
/// amount, when the space they grow into is already material (join) or empty (cut). `probe`
|
||||
/// maps a sketch point to the world points where the tool would be (along the extrude, around
|
||||
@@ -2850,6 +2897,7 @@ fn feature_tools_with_constructor(
|
||||
let probe = |p: Vec2| (1..4).map(|k| ss.plane.to_world(p) + n * (lo + off + (hi - lo) * k as f64 / 4.0)).collect::<Vec<_>>();
|
||||
let original = r;
|
||||
let r = grow_profile_for_coplanar(st, r, *operation, targets, &probe);
|
||||
let r = fill_void_holes(st, &r, *operation, targets, &ss.plane, (l2, h2));
|
||||
untrimmed &= l2 == lo + off && h2 == hi + off && &r == original;
|
||||
tools.extend(centered_extrude(&ss.plane, &r, l2, h2, Some(&ss))?);
|
||||
footprints.push(r);
|
||||
|
||||
Reference in new issue
Block a user