mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 18:45:55 +00:00
kernel: a vertex a face's loops pass twice is one point of its (u, v)
Where a face's loops pass a vertex more than once (two holes touching at a
point, a loop through another hole's corner), each pass took its parameters
from the pcurve of the edge leaving it, and the two pcurves round them 1e-14
apart. The triangulation got two nodes 1e-14 apart with constraints crossing
between them and refused the face ("boundary crosses itself"): rig 307, 3092
and 3887. A pass whose parameters agree with the vertex's first pass to that
rounding (1e-9 relative) now takes the first's; farther apart they are two
places of the surface (a seam's two sides, a closed spline's ends).
Rig 494 now fails: its revolved face has a dead-end edge walked down and back
up in one loop, which met the triangulation as a needle 1e-14 wide and now as
the slit it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -205,9 +205,14 @@ pub(crate) fn plan(b: &brep::Brep, tol: f64, turn: Option<size::Turn>) -> Result
|
||||
/// its loop would wind round the surface). Each loop then starts at a node off every pole whose
|
||||
/// predecessor is off them too, so the step from its last node to its first, which
|
||||
/// [`seam::drift`] takes as the nearest copy, never crosses a pole. A vertex loop (a pole)
|
||||
/// bounds no area and has no nodes of its own. A pcurve that does not cover its edge's interval, or puts a
|
||||
/// node farther off the edge than the tolerances allow, is an error naming the face and edge:
|
||||
/// the record is wrong there.
|
||||
/// bounds no area and has no nodes of its own. Where the face's loops pass a vertex more than once
|
||||
/// (two holes touching at a point, a loop through another's corner), the pcurves of the edges from
|
||||
/// it give its parameters apart by their rounding, and two nodes 1e-14 apart put crossing
|
||||
/// constraints into the triangulation: a pass whose parameters agree with the first pass's to
|
||||
/// that rounding (1e-9 relative) takes the first's. Farther apart they are two places of the
|
||||
/// surface (a seam's two sides, a closed spline's ends). A pcurve that does not cover its edge's
|
||||
/// interval, or puts a node farther off the edge than the tolerances allow, is an error naming
|
||||
/// the face and edge: the record is wrong there.
|
||||
fn boundary(
|
||||
b: &brep::Brep,
|
||||
fi: usize,
|
||||
@@ -220,6 +225,8 @@ fn boundary(
|
||||
let dom = uv::domain(s);
|
||||
let fail = |e: u32, why: String| unsupported(format!("face {fi} ({}): edge {e}: {why}", kind(&face.surface)));
|
||||
let mut out = Boundary { loops: Vec::with_capacity(face.loops.len()), segments: Vec::with_capacity(face.loops.len()) };
|
||||
// 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();
|
||||
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());
|
||||
@@ -265,6 +272,18 @@ fn boundary(
|
||||
q[0] += shift[0];
|
||||
q[1] += shift[1];
|
||||
}
|
||||
if let (Some(&v), Some(q)) = (edge.vertices.get(usize::from(!oe.forward)), eq.first_mut()) {
|
||||
match at_vertex.get(&v) {
|
||||
Some(first) => {
|
||||
if (0..2).all(|d| (first[d] - q[d]).abs() <= 1e-9 * first[d].abs().max(q[d].abs()).max(1.0)) {
|
||||
*q = *first;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
at_vertex.insert(v, *q);
|
||||
}
|
||||
}
|
||||
}
|
||||
end = eq.last().copied();
|
||||
ep.pop();
|
||||
eq.pop();
|
||||
|
||||
@@ -128,6 +128,19 @@ fn holes_and_trimmed_faces_are_watertight() {
|
||||
assert!((a - b).abs() < 0.01 * b, "cross hole volume {a} vs {b}");
|
||||
}
|
||||
|
||||
/// Through holes in pairs whose circles touch at one point (rig booleans/307): the plate's faces
|
||||
/// have one loop round each pair, through their common vertex twice. Both passes are one point of
|
||||
/// the face's (u, v), not two nodes the pcurves put 1e-14 apart, whose constraints crossed
|
||||
/// ("boundary crosses itself").
|
||||
#[test]
|
||||
fn holes_touching_at_a_point_are_watertight() {
|
||||
let mut body = ok(crate::box_solid(Vec3::new(-50.0, -50.0, 0.0), Vec3::new(50.0, 50.0, 5.0)));
|
||||
for (x, y) in [(25.0, 0.0), (-25.0, 0.0), (0.0, 25.0), (0.0, -25.0), (15.0, 0.0), (-15.0, 0.0), (0.0, 15.0), (0.0, -15.0)] {
|
||||
body = cut(&body, &ok(crate::cylinder(Vec3::new(x, y, -1.0), Vec3::Z, 5.0, 7.0)));
|
||||
}
|
||||
check("holes touching at a point", &body, 0.01, 10000.0 * 5.0 - 8.0 * PI * 25.0 * 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn faces_and_edges_keep_the_body_order() {
|
||||
let body = ok(crate::cylinder(Vec3::ZERO, Vec3::Z, 2.0, 5.0));
|
||||
|
||||
Reference in New Issue
Block a user