mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
Tessellation: two corners of a face a hair apart, within their tolerance, are one point of it
A planar boolean leaves a cut-out's corner that touches another loop's corner as two vertices a hair apart: in the sheet refold tests (doc sheet a_cut_out_on_one_side_of_a_bend_line... and a_cut_out_with_a_corner_on_a_bend_line...), a triangle's corner 1e-8 to 5e-8 mm from the base's corner on the bend line, both vertices of tolerance 1.05e-4. Their loops then cross by 1e-8 in (u, v), which no constrained triangulation can keep, and the face was left to the lenient fill. The boundary build already makes a vertex the face passes twice one point of its (u, v). Now a vertex within the tolerance of another of the face's vertices (the larger of the two, at most the chord tolerance) takes that one's parameters too, so the loops touch at one point, a pinch, as at a shared vertex. Not for two vertices an edge of the face joins: the edge says they are two points, however near (a_ledge_beside_an_edge_as_tall_as_the_setback_goes_with_the_strip has a 1e-4 mm step, within the vertices' 1.18e-4 tolerance), nor at a pole. Test changed, with the reason: thread_display_mesh_stays_on_its_surfaces checked every triangle whose three corners lie at the core radius rc as one of the core cylinder, and wanted its centre within 2 tol of rc. The flat end disc of the M10x1.5 thread (z = 12) has its corners a hair apart too; joined, it is meshed strictly from its boundary (a plane, e39fbdebb), and one of its triangles has its three corners on the rim, centre at r = 3.956, on the disc. The end-disc exemption the test already makes for its radius check now covers this check as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -288,6 +288,12 @@ fn boundary(
|
||||
let mut out = Boundary { loops: Vec::with_capacity(face.loops.len()), segments: Vec::with_capacity(face.loops.len()), reach };
|
||||
// Each vertex's parameters, from the first edge use of the face to start there.
|
||||
let mut at_vertex: std::collections::HashMap<u32, [f64; 2]> = std::collections::HashMap::new();
|
||||
// The vertex pairs an edge of the face joins.
|
||||
let joined: std::collections::HashSet<(u32, u32)> = face_edges(face)
|
||||
.filter_map(|oe| b.edges.get(oe.edge as usize))
|
||||
.map(|e| (e.vertices[0].min(e.vertices[1]), e.vertices[0].max(e.vertices[1])))
|
||||
.collect();
|
||||
let vertex_point = |v: u32| b.vertices.get(v as usize).map(|x| (Vec3::new(x.point[0], x.point[1], x.point[2]), x.tolerance.max(0.0)));
|
||||
for l in &face.loops {
|
||||
let Loop::Edges { edges } = l else { continue };
|
||||
let (mut pts, mut uvs): (Vec<Vec3>, Vec<[f64; 2]>) = (Vec::new(), Vec::new());
|
||||
@@ -341,6 +347,24 @@ fn boundary(
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Another vertex of the face within the tolerance of both (and of the
|
||||
// chord tolerance), and not joined to this one by an edge (which makes
|
||||
// them two points however near: a step that small), is the same point:
|
||||
// the record's corners a hair apart (a planar boolean's, where a cut-out's
|
||||
// corner touches another loop's corner) are one point of the face, so its
|
||||
// loops touch there instead of crossing.
|
||||
let same = vertex_point(v).and_then(|(p, tv)| {
|
||||
at_vertex.iter().filter(|(w, _)| **w != v && !joined.contains(&(v.min(**w), v.max(**w)))).find_map(|(w, wq)| {
|
||||
let (pw, tw) = vertex_point(*w)?;
|
||||
(p.dist(pw) <= tv.max(tw).min(rules.tol)).then_some(*wq)
|
||||
})
|
||||
});
|
||||
if let Some(mut w) = same.filter(|w| !(0..2).any(|d| pole_along(*w, d) || pole_along(*q, d))) {
|
||||
for d in 0..2 {
|
||||
w[d] = uv::unwrap(w[d], q[d], dom.period[d]);
|
||||
}
|
||||
*q = w;
|
||||
}
|
||||
at_vertex.insert(v, *q);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +144,14 @@ fn thread_display_mesh_stays_on_its_surfaces() {
|
||||
let [a, b2, c] = t.map(|i| mesh.positions[i as usize]);
|
||||
let (ra, rb, rcn) = (r(a), r(b2), r(c));
|
||||
let centre = r((a + b2 + c) * (1.0 / 3.0));
|
||||
// An end disc is flat: its triangles are its boundary's, and one with its three
|
||||
// corners on the rim (at r = rc) lies on the disc, not on a cylinder.
|
||||
let end_disc = [a, b2, c].iter().all(|v| v.z.abs() < 1e-9) || [a, b2, c].iter().all(|v| (v.z - (turns + 2.0) * p).abs() < 1e-9);
|
||||
for want in [rc, rm] {
|
||||
if [ra, rb, rcn].iter().all(|x| (x - want).abs() < 1e-4) {
|
||||
if !end_disc && [ra, rb, rcn].iter().all(|x| (x - want).abs() < 1e-4) {
|
||||
assert!(centre > want - 2.0 * tol, "{name}: triangle {ti} on the r = {want} cylinder cuts in to {centre}");
|
||||
}
|
||||
}
|
||||
let end_disc = [a, b2, c].iter().all(|v| v.z.abs() < 1e-9) || [a, b2, c].iter().all(|v| (v.z - (turns + 2.0) * p).abs() < 1e-9);
|
||||
assert!(end_disc || (centre > rc - 2.0 * tol && centre < rm + 1e-6), "{name}: triangle {ti} at radius {centre}, outside {rc}…{rm}");
|
||||
}
|
||||
// A mesh within the tolerance of the surfaces is within area × tolerance of the volume.
|
||||
|
||||
Reference in New Issue
Block a user