Planar regions: a hole goes to the material that holds it, read closer than the hole lies

Reading a planar sheet's faces back as regions, each hole went to the smallest outline whose
Loop2::contains held its middle: even-odd against the outline's polyline within 1e-3 mm. A
5e-5 mm square cut 1.7e-4 mm inside a radius-10 circle lies outside the polyline's chords there,
so no outline held it and the cut failed ("face 0 has a hole outside its material"). An
outline now holds a point by its polyline within a quarter of the point's distance from it (its
nearest point on the exact segments), which no chord can pass the point by.

Fixes kernel prism::planar_tests::tiny_tool_between_circle_and_display_chord_is_not_lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-29 22:10:42 -04:00
committed by Irakli Kourtis
parent f8b45f2772
commit 64188ce435
+10 -1
View File
@@ -260,7 +260,7 @@ mod sheets {
let mid = hole.segs.first().map(|s| s.point_at(0.5)).ok_or_else(|| failed("face", format!("face {f} has an empty loop")))?;
let owner = out[first..]
.iter_mut()
.filter(|r| r.outer.contains(mid))
.filter(|r| holds(&r.outer, mid))
.min_by(|a, b| a.outer.signed_area().total_cmp(&b.outer.signed_area()))
.ok_or_else(|| failed("face", format!("face {f} has a hole outside its material")))?;
owner.holes.push(hole);
@@ -269,6 +269,15 @@ mod sheets {
Ok(Regions { regions: out, touching })
}
/// Whether the loop `outer` holds `p`: even-odd against its polyline within a quarter of
/// `p`'s distance from it, so the polyline's chords cannot pass `p` by (a loop's own
/// [`Loop2::contains`] reads it within 1e-3 mm, and a hole closer than that to an arc of the
/// outline was taken for one outside it, a chord cutting it off).
fn holds(outer: &Loop2, p: Vec2) -> bool {
let d = outer.segs.iter().map(|s| s.point_at(s.closest_param(p)).dist(p)).fold(f64::INFINITY, f64::min);
outer.polyline((d * 0.25).clamp(1e-9, 1e-3)).contains(p)
}
/// The natural normal of a planar surface record.
fn natural_normal(s: &Surface) -> Option<Vec3> {
let v = |p: [f64; 3]| Vec3::new(p[0], p[1], p[2]);