Booleans: prism cuts whose walls only touch the target are exact (#1894)
Thanks @NOWN-DEV.
This commit is contained in:
1 parent
b6f9b1deac
commit
40abcbc8cf
7 files changed
+400
No files matched your search
Vendored
+11
@@ -170,6 +170,17 @@ Changes:
|
||||
(`onto_edges`). Points within `TOLERANCE` stay on the surface, so exact geometry meshes as
|
||||
before. The meshes Boolean operations intersect keep truck's points.
|
||||
|
||||
- `tessellation/triangulation.rs`: in display and export meshes (`Deflection::rulings`), a
|
||||
direction left undivided along the rulings of a face ruled one way also gets a grid line
|
||||
through each point of the face's boundary that lies inside the face along it
|
||||
(`with_boundary_lines`), when there are at most 16 such points: corners standing into the
|
||||
face, not a boundary curve running through it, which the triangulation already follows. A hole in a cone or cylinder smaller than one cell (a notch into a
|
||||
chamfer) or a trim crossing the rulings of a narrow band (an oblique plane through a bore's
|
||||
chamfer) had no grid point beside it, and the triangulation fanned from it to the far ends of
|
||||
the face, many degrees round: those triangles cut through the surface and folded over each
|
||||
other (a cone face with a small hole meshed several mm² too large, and the boolean that made
|
||||
it was refused for its volume).
|
||||
|
||||
## matext4cgmath 0.1.0 (Apache-2.0, © RICOS Co. Ltd., https://github.com/ricosjp/matext4cgmath)
|
||||
|
||||
truck-geometry uses it for a matrix's operator norm (an intersection curve scales its tolerance
|
||||
|
||||
@@ -903,6 +903,8 @@ fn insert_surface(
|
||||
let (u, v) = balanced_grid(&surface, u, v, cap);
|
||||
let v = with_ruling_vertices(v, polyline, 1, tol.rulings && v_ruled, u.len(), cap);
|
||||
let u = with_ruling_vertices(u, polyline, 0, tol.rulings && u_ruled, v.len(), cap);
|
||||
let v = with_boundary_lines(v, polyline, 1, tol.rulings && v_ruled && !u_ruled, u.len(), cap);
|
||||
let u = with_boundary_lines(u, polyline, 0, tol.rulings && u_ruled && !v_ruled, v.len(), cap);
|
||||
(u, v)
|
||||
};
|
||||
let insert_res: Vec<Vec<Option<_>>> = udiv
|
||||
@@ -1183,6 +1185,46 @@ fn with_ruling_vertices(div: Vec<f64>, polyline: &PolyBoundary, axis: usize, rul
|
||||
out
|
||||
}
|
||||
|
||||
/// SolidCraft: `div` (the grid along coordinate `axis`, 0 for u and 1 for v) with a line through
|
||||
/// every point of the face's boundary that lies inside the face along that axis: the points of a
|
||||
/// hole cut into a cylinder or a cone, or of a trim that crosses its rulings (an oblique plane
|
||||
/// through a cone's narrow chamfer). A ruled direction gets few lines (one cell along a cone's
|
||||
/// short side), and a boundary point between them has no grid point beside it: the triangulation
|
||||
/// joins it to the far ends of the face, many degrees round, and those triangles cut through the
|
||||
/// surface (a cone face with a small hole meshed several mm² too large, and the boolean that made
|
||||
/// it was refused for its volume). Only a direction that needs no division of its own (`ruled`)
|
||||
/// while the other does gets them (a plane, ruled both ways, maps to its uv space without
|
||||
/// stretching and needs none), only for a few such points (corners standing into the face, at
|
||||
/// most [`MAX_BOUNDARY_LINES`]), and while the grid stays within `cap`.
|
||||
/// SolidCraft: most boundary points inside a ruled face that get grid lines through them.
|
||||
const MAX_BOUNDARY_LINES: usize = 16;
|
||||
|
||||
fn with_boundary_lines(div: Vec<f64>, polyline: &PolyBoundary, axis: usize, ruled: bool, other: usize, cap: usize) -> Vec<f64> {
|
||||
let (Some(&first), Some(&last)) = (div.first(), div.last()) else { return div };
|
||||
let span = last - first;
|
||||
if !ruled || !(span.is_finite() && span > 0.0) {
|
||||
return div;
|
||||
}
|
||||
// Lines closer than this to another add nothing but slivers.
|
||||
let apart = span * 1e-3;
|
||||
let mut at: Vec<f64> = polyline.0.iter().flatten().map(|p| p[axis]).filter(|t| t.is_finite() && *t > first + apart && *t < last - apart).collect();
|
||||
at.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
|
||||
at.dedup_by(|a, b| (*a - *b).abs() <= apart);
|
||||
// Many: a boundary curve running through the face, which the triangulation already follows
|
||||
// with short triangles (a cylinder cut by another); lines along it only crowd it.
|
||||
if at.len() > MAX_BOUNDARY_LINES {
|
||||
return div;
|
||||
}
|
||||
let extra: Vec<f64> = at.into_iter().filter(|t| div.iter().all(|d| (d - t).abs() > apart)).collect();
|
||||
if extra.is_empty() || (div.len() + extra.len()).saturating_mul(other) > cap {
|
||||
return div;
|
||||
}
|
||||
let mut out = div;
|
||||
out.extend(extra);
|
||||
out.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
|
||||
out
|
||||
}
|
||||
|
||||
/// Converts triangulation into `PolygonMesh`.
|
||||
fn triangulation_into_polymesh<'a>(
|
||||
vertices: VertexIterator<'a, SPoint2, (), CdtEdge<()>, ()>,
|
||||
|
||||
Reference in new issue
Block a user