From 8e9defff959b159e2793b5ff842074214b04ae90 Mon Sep 17 00:00:00 2001 From: Joseph Li <13826851+joseph-x-li@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:55:01 -0700 Subject: [PATCH] Avoid 0/0 in get_barycentric_ratio when every weight is zeroed Each weight is zeroed when the point is within tol of that edge's line, then the weights are divided by their sum. On a thin triangle a point can be within tol of all three, so the sum is zero and every interpolated value became NaN. Fall back to the nearest corner, still a valid interpolation. Adds a unit test with such a triangle. Fixes #15 Co-Authored-By: Claude Opus 5.5 --- src/values.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/values.rs b/src/values.rs index 8b5f9e2b..cddd8d84 100644 --- a/src/values.rs +++ b/src/values.rs @@ -23,7 +23,18 @@ fn get_barycentric_ratio(v: &Vec3, pos: &[Vec3; 3], tol: Real) -> Vec3 { let c = dif[i].cross(*v - pos[nxt(i)]); uvw[i] = if c.length_squared() < lsq[i] * tol2 { 0. } else { c.dot(crs) }; } - uvw /= uvw[0] + uvw[1] + uvw[2]; + // Within `tol` of all three edge lines (a thin triangle at a coarse tolerance) + // every weight is zero; dividing would make every value NaN. Take the nearest + // corner instead, still a valid interpolation. + let sum = uvw[0] + uvw[1] + uvw[2]; + if sum.abs() > Real::MIN_POSITIVE && sum.is_finite() { + uvw /= sum; + return uvw; + } + let d = [0, 1, 2].map(|i| (*v - pos[i]).length_squared()); + let near = if d[0] <= d[1] && d[0] <= d[2] { 0 } else if d[1] <= d[2] { 1 } else { 2 }; + uvw = Vec3::ZERO; + uvw[near] = 1.; uvw } else { let next = nxt(long); @@ -76,4 +87,19 @@ pub fn annotate( } } vs -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_point_near_all_three_edges_of_thin_triangle() { + // height 1.05e-4 > tol, so the triangle is not treated as degenerate, yet the point + // is within tol = 1e-4 of all three edge lines and of no corner + let pos = [Vec3::new(0., 0., 0.), Vec3::new(1., 0., 0.), Vec3::new(0.5, 1.05e-4, 0.)]; + let uvw = get_barycentric_ratio(&Vec3::new(0.45, 0.5e-4, 0.), &pos, 1e-4); + assert!(uvw.is_finite(), "{uvw:?}"); + assert!((uvw[0] + uvw[1] + uvw[2] - 1.).abs() < 1e-12, "{uvw:?}"); + } +}