1use crate::{EPSILON, Ray3, Vec3};
2
3#[derive(Clone, Copy, Debug, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Plane {
7 pub normal: Vec3,
9 pub distance: f32,
11}
12
13impl Plane {
14 #[inline]
16 pub fn new(normal: Vec3, distance: f32) -> Self {
17 let normal = normal.normalize();
18 Self { normal, distance }
19 }
20
21 #[inline]
23 pub fn from_normal_and_point(normal: Vec3, point: Vec3) -> Self {
24 let normal = normal.normalize();
25 Self {
26 normal,
27 distance: -normal.dot(point),
28 }
29 }
30
31 #[inline]
33 pub fn from_three_points(a: Vec3, b: Vec3, c: Vec3) -> Self {
34 let normal = (b - a).cross(c - a).normalize();
35 Self::from_normal_and_point(normal, a)
36 }
37
38 #[inline]
40 pub fn signed_distance(self, point: Vec3) -> f32 {
41 self.normal.dot(point) + self.distance
42 }
43
44 #[inline]
46 pub fn project_point(self, point: Vec3) -> Vec3 {
47 point - self.normal * self.signed_distance(point)
48 }
49
50 pub fn intersect_ray(self, ray: Ray3) -> Option<f32> {
52 let denom = self.normal.dot(ray.direction);
53 if denom.abs() <= EPSILON {
54 return None;
55 }
56 let t = -self.signed_distance(ray.origin) / denom;
57 if t >= 0.0 { Some(t) } else { None }
58 }
59
60 pub fn intersect_line(self, a: Vec3, b: Vec3) -> Option<Vec3> {
62 let ab = b - a;
63 let denom = self.normal.dot(ab);
64 if denom.abs() <= EPSILON {
65 return None;
66 }
67 let t = -self.signed_distance(a) / denom;
68 if (0.0..=1.0).contains(&t) {
69 Some(a + ab * t)
70 } else {
71 None
72 }
73 }
74}
75
76impl Default for Plane {
77 #[inline]
78 fn default() -> Self {
79 Self::from_normal_and_point(Vec3::Y, Vec3::ZERO)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::assert_close;
87
88 #[test]
89 fn plane_distance_projection_and_ray_intersection_work() {
90 let plane = Plane::from_normal_and_point(Vec3::Y, Vec3::ZERO);
91 assert_close(plane.signed_distance(Vec3::new(0.0, 2.0, 0.0)), 2.0);
92 assert_eq!(
93 plane.project_point(Vec3::new(1.0, 2.0, 3.0)),
94 Vec3::new(1.0, 0.0, 3.0)
95 );
96 let ray = Ray3::new(Vec3::new(0.0, 2.0, 0.0), Vec3::new(0.0, -1.0, 0.0));
97 assert_close(plane.intersect_ray(ray).unwrap(), 2.0);
98 }
99
100 #[test]
101 fn plane_from_three_points_has_expected_normal() {
102 let plane = Plane::from_three_points(Vec3::ZERO, Vec3::X, Vec3::Z);
103 assert_close(plane.normal.y, -1.0);
104 }
105}