Skip to main content

viewport_lib/geometry/
intersect.rs

1//! Ray/primitive intersection helpers shared across interaction widgets.
2
3/// Compute the intersection point of a ray with a plane.
4///
5/// The plane equation is `dot(p, normal) + distance = 0`, matching
6/// [`ClipShape::Plane`](crate::renderer::types::ClipShape::Plane).
7///
8/// Returns `Some(point)` if the ray hits the plane in front of the origin,
9/// `None` if the ray is parallel to the plane or the intersection is behind it.
10pub fn ray_plane_intersection(
11    ray_origin: glam::Vec3,
12    ray_dir: glam::Vec3,
13    plane_normal: glam::Vec3,
14    plane_distance: f32,
15) -> Option<glam::Vec3> {
16    let denom = plane_normal.dot(ray_dir);
17    if denom.abs() < 1e-6 {
18        return None;
19    }
20    // dot(ray_origin + t*ray_dir, n) + d = 0  =>  t = -(dot(o, n) + d) / dot(dir, n)
21    let t = -(plane_normal.dot(ray_origin) + plane_distance) / denom;
22    if t < 0.0 {
23        return None;
24    }
25    Some(ray_origin + ray_dir * t)
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn ray_plane_intersection_hits() {
34        // XY plane at y = 2: dot(p, [0,1,0]) + (-2) = 0
35        let origin = glam::Vec3::new(0.0, 5.0, 0.0);
36        let dir = glam::Vec3::new(0.0, -1.0, 0.0);
37        let hit = ray_plane_intersection(origin, dir, glam::Vec3::Y, -2.0);
38        assert!(hit.is_some());
39        let p = hit.unwrap();
40        assert!((p.y - 2.0).abs() < 1e-5);
41    }
42
43    #[test]
44    fn ray_plane_intersection_parallel_returns_none() {
45        let origin = glam::Vec3::new(0.0, 1.0, 0.0);
46        let dir = glam::Vec3::new(1.0, 0.0, 0.0);
47        assert!(ray_plane_intersection(origin, dir, glam::Vec3::Y, 0.0).is_none());
48    }
49
50    #[test]
51    fn ray_plane_intersection_behind_returns_none() {
52        // plane at y=0, ray at y=5 pointing up (+y) : intersection is behind origin
53        let origin = glam::Vec3::new(0.0, 5.0, 0.0);
54        let dir = glam::Vec3::new(0.0, 1.0, 0.0);
55        assert!(ray_plane_intersection(origin, dir, glam::Vec3::Y, 0.0).is_none());
56    }
57}