pub(super) fn point_enclosed(point: [f64; 3], scale: f64, tris: &[[[f64; 3]; 3]]) -> bool {
let dirs = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
let mut valid = 0u32;
let mut inside = 0u32;
for (k, dir) in dirs.iter().enumerate() {
let j = (k as f64 + 1.0) * 1e-7 * scale;
let origin = [point[0] + j, point[1] + 1.3 * j, point[2] + 1.7 * j];
match count_crossings(origin, *dir, scale, tris) {
Some(n) => {
valid += 1;
if n % 2 == 1 {
inside += 1;
}
}
None => continue, }
}
valid >= 2 && inside >= 2
}
fn count_crossings(
origin: [f64; 3],
dir: [f64; 3],
scale: f64,
tris: &[[[f64; 3]; 3]],
) -> Option<u32> {
const BARY_EPS: f64 = 1e-9;
let t_eps = 1e-9 * scale;
let mut crossings = 0u32;
for tri in tris {
let (a, b, c) = (tri[0], tri[1], tri[2]);
let e1 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
let e2 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
let pv = [
dir[1] * e2[2] - dir[2] * e2[1],
dir[2] * e2[0] - dir[0] * e2[2],
dir[0] * e2[1] - dir[1] * e2[0],
];
let det = e1[0] * pv[0] + e1[1] * pv[1] + e1[2] * pv[2];
if det.abs() < 1e-16 {
continue; }
let inv = 1.0 / det;
let tv = [origin[0] - a[0], origin[1] - a[1], origin[2] - a[2]];
let u = (tv[0] * pv[0] + tv[1] * pv[1] + tv[2] * pv[2]) * inv;
if u < -BARY_EPS || u > 1.0 + BARY_EPS {
continue;
}
let qv = [
tv[1] * e1[2] - tv[2] * e1[1],
tv[2] * e1[0] - tv[0] * e1[2],
tv[0] * e1[1] - tv[1] * e1[0],
];
let v = (dir[0] * qv[0] + dir[1] * qv[1] + dir[2] * qv[2]) * inv;
if v < -BARY_EPS || u + v > 1.0 + BARY_EPS {
continue;
}
let t = (e2[0] * qv[0] + e2[1] * qv[1] + e2[2] * qv[2]) * inv;
if t <= -t_eps {
continue; }
if t < t_eps {
return None; }
if u < BARY_EPS || v < BARY_EPS || u + v > 1.0 - BARY_EPS {
return None; }
crossings += 1;
}
Some(crossings)
}
#[cfg(test)]
mod tests {
use super::point_enclosed;
const POINT: [f64; 3] = [0.0, 0.0, 0.0];
const SCALE: f64 = 1.0;
fn trap_x() -> [[f64; 3]; 3] {
[[1e-7, -1.0, -1.0], [1e-7, 1.0, -1.0], [1e-7, 0.0, 1.0]]
}
fn trap_y() -> [[f64; 3]; 3] {
[[-1.0, 2.6e-7, -1.0], [1.0, 2.6e-7, -1.0], [0.0, 2.6e-7, 1.0]]
}
fn trap_z() -> [[f64; 3]; 3] {
[[-1.0, -1.0, 5.1e-7], [1.0, -1.0, 5.1e-7], [0.0, 1.0, 5.1e-7]]
}
fn far_hit_x() -> [[f64; 3]; 3] {
[[10.0, -100.0, -100.0], [10.0, 100.0, -100.0], [10.0, 0.0, 100.0]]
}
fn far_hit_y() -> [[f64; 3]; 3] {
[[-100.0, 10.0, -100.0], [100.0, 10.0, -100.0], [0.0, 10.0, 100.0]]
}
#[test]
fn grazing_hit_on_every_axis_conservatively_keeps_the_point() {
let tris = [trap_x(), trap_y(), trap_z()];
assert!(
!point_enclosed(POINT, SCALE, &tris),
"a point straddling grazing hits on all three axes must be kept, never dropped"
);
}
#[test]
fn two_agreeing_valid_rays_mark_the_point_enclosed() {
let tris = [far_hit_x(), far_hit_y(), trap_z()];
assert!(
point_enclosed(POINT, SCALE, &tris),
"two valid rays that agree on 'inside' must satisfy the majority vote"
);
}
#[test]
fn two_disagreeing_valid_rays_conservatively_keep_the_point() {
let tris = [far_hit_x(), trap_z()];
assert!(
!point_enclosed(POINT, SCALE, &tris),
"two valid rays that disagree must not satisfy the majority vote"
);
}
}