Skip to main content

loam_math/
sectionable.rs

1use glam::{Vec3, Vec4};
2
3/// Edges below this absolute w separation count as parallel.
4pub const EDGE_PARALLEL_EPSILON: f32 = 1e-6;
5
6/// Slices within this distance of a vertex shift by this amount before cap assembly.
7pub const SLICE_PERTURBATION_EPSILON: f32 = 1e-5;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct WPlane {
11    pub w_slice: f32,
12}
13
14impl WPlane {
15    pub const fn new(w_slice: f32) -> Self {
16        Self { w_slice }
17    }
18}
19
20impl WPlane {
21    /// Returns the segment parameter and xyz intersection; near-parallel edges return `None`.
22    pub fn intersect_edge(&self, p0: Vec4, p1: Vec4) -> Option<(f32, Vec3)> {
23        let dw = p1.w - p0.w;
24        if dw.abs() < EDGE_PARALLEL_EPSILON {
25            return None;
26        }
27        let t = (self.w_slice - p0.w) / dw;
28        if !(0.0..=1.0).contains(&t) {
29            return None;
30        }
31
32        let p = p0 + t * (p1 - p0);
33        Some((t, Vec3::new(p.x, p.y, p.z)))
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn r4_edge_section_midpoint() {
43        let slice = WPlane::new(0.0);
44        let p0 = Vec4::new(1.0, 2.0, 3.0, -1.0);
45        let p1 = Vec4::new(5.0, 6.0, 7.0, 1.0);
46        let (t, p3) = slice.intersect_edge(p0, p1).unwrap();
47        assert!((t - 0.5).abs() < 1e-6);
48        assert_eq!(p3, Vec3::new(3.0, 4.0, 5.0));
49    }
50
51    #[test]
52    fn r4_edge_section_no_crossing_returns_none() {
53        let slice = WPlane::new(0.0);
54        let p0 = Vec4::new(0.0, 0.0, 0.0, 0.5);
55        let p1 = Vec4::new(0.0, 0.0, 0.0, 1.5);
56        assert!(slice.intersect_edge(p0, p1).is_none());
57    }
58
59    #[test]
60    fn r4_edge_section_parallel_edge_returns_none() {
61        let slice = WPlane::new(0.0);
62        let p0 = Vec4::new(0.0, 0.0, 0.0, 0.0);
63        let p1 = Vec4::new(1.0, 1.0, 1.0, 1e-7);
64        assert!(slice.intersect_edge(p0, p1).is_none());
65    }
66
67    #[test]
68    fn r4_edge_section_endpoint_on_slice_returns_t_zero() {
69        let slice = WPlane::new(0.0);
70        let p0 = Vec4::new(2.0, 2.0, 2.0, 0.0);
71        let p1 = Vec4::new(5.0, 5.0, 5.0, 1.0);
72        let (t, p3) = slice.intersect_edge(p0, p1).unwrap();
73        assert!(t.abs() < 1e-6);
74        assert_eq!(p3, Vec3::new(2.0, 2.0, 2.0));
75    }
76}