1use phys_geom::math::*;
16use phys_geom::shape::InfinitePlane;
17
18use crate::{Raycast, RaycastHitResult};
19
20pub trait InfinitePlaneExt {
22 fn distance(&self, p: Point3) -> Real;
27}
28
29fn normal() -> UnitVec3 {
30 Vec3::y_axis()
31}
32
33impl InfinitePlaneExt for InfinitePlane {
34 #[inline]
35 fn distance(&self, p: Point3) -> Real {
36 p.coords.dot(&normal().into_inner())
37 }
38}
39
40impl Raycast for InfinitePlane {
41 fn raycast(
42 &self,
43 local_ray: phys_geom::Ray,
44 max_distance: Real,
45 discard_inside_hit: bool,
46 ) -> Option<RaycastHitResult> {
47 let origin_to_plane_distance = self.distance(local_ray.origin);
48
49 if origin_to_plane_distance.abs() <= Real::EPSILON && !discard_inside_hit {
51 return Some(RaycastHitResult {
52 distance: 0.0,
53 normal: normal(),
54 });
55 }
56
57 let dn = local_ray.direction.dot(&normal().into_inner());
58
59 if (-Real::EPSILON < dn) && (dn < Real::EPSILON) {
61 return None;
62 }
63
64 let hit_distance = -origin_to_plane_distance / dn;
65
66 if hit_distance < 0.0 {
68 return None;
69 }
70
71 if hit_distance > 0.0 && hit_distance <= max_distance {
72 if origin_to_plane_distance > 0.0 {
74 Some(RaycastHitResult {
75 distance: hit_distance,
76 normal: normal(),
77 })
78 } else if discard_inside_hit {
80 None
81 } else {
82 Some(RaycastHitResult {
83 distance: hit_distance,
84 normal: -normal(),
85 })
86 }
87 } else {
88 None
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use approx::assert_relative_eq;
96
97 use super::*;
98
99 #[test]
100 fn test_raycast_infinite_plane() {
101 let plane = InfinitePlane::default();
102
103 let ray = phys_geom::Ray::new(
105 Point3::new(0.0, 2.0, 0.0),
106 UnitVec3::new_normalize(Vec3::new(0.0, -1.0, 0.0)),
107 );
108
109 let hit = plane.raycast(ray, 10.0, false).unwrap();
110 assert_relative_eq!(hit.distance, 2.0);
111 assert_relative_eq!(hit.normal, Vec3::y_axis());
112 }
113
114 #[test]
115 fn test_raycast_infinite_plane_from_below() {
116 let plane = InfinitePlane::default();
117
118 let ray = phys_geom::Ray::new(
120 Point3::new(0.0, -2.0, 0.0),
121 UnitVec3::new_normalize(Vec3::new(0.0, 1.0, 0.0)),
122 );
123
124 let hit = plane.raycast(ray, 10.0, false).unwrap();
125 assert_relative_eq!(hit.distance, 2.0);
126 assert_relative_eq!(
127 hit.normal,
128 UnitVec3::new_normalize(Vec3::new(0.0, -1.0, 0.0))
129 );
130 }
131
132 #[test]
133 fn test_raycast_infinite_plane_from_below_discarded() {
134 let plane = InfinitePlane::default();
135
136 let ray = phys_geom::Ray::new(
138 Point3::new(0.0, -2.0, 0.0),
139 UnitVec3::new_normalize(Vec3::new(0.0, 1.0, 0.0)),
140 );
141
142 assert_eq!(plane.raycast(ray, 10.0, true), None);
143 }
144
145 #[test]
146 fn test_raycast_infinite_plane_parallel() {
147 let plane = InfinitePlane::default();
148
149 let ray = phys_geom::Ray::new(
151 Point3::new(0.0, 2.0, 0.0),
152 UnitVec3::new_normalize(Vec3::new(1.0, 0.0, 0.0)),
153 );
154
155 assert_eq!(plane.raycast(ray, 10.0, false), None);
156 }
157
158 #[test]
159 fn test_raycast_infinite_plane_on_plane() {
160 let plane = InfinitePlane::default();
161
162 let ray = phys_geom::Ray::new(
164 Point3::new(0.0, 0.0, 0.0),
165 UnitVec3::new_normalize(Vec3::new(0.0, 1.0, 0.0)),
166 );
167
168 let hit = plane.raycast(ray, 10.0, false).unwrap();
169 assert_relative_eq!(hit.distance, 0.0);
170 assert_relative_eq!(hit.normal, Vec3::y_axis());
171 }
172
173 #[test]
174 fn test_raycast_infinite_plane_on_plane_discarded() {
175 let plane = InfinitePlane::default();
176
177 let ray = phys_geom::Ray::new(
179 Point3::new(0.0, 0.0, 0.0),
180 UnitVec3::new_normalize(Vec3::new(0.0, 1.0, 0.0)),
181 );
182
183 assert_eq!(plane.raycast(ray, 10.0, true), None);
184 }
185
186 #[test]
187 fn test_raycast_infinite_plane_away_from_plane() {
188 let plane = InfinitePlane::default();
189
190 let ray = phys_geom::Ray::new(
192 Point3::new(0.0, 2.0, 0.0),
193 UnitVec3::new_normalize(Vec3::new(0.0, 1.0, 0.0)),
194 );
195
196 assert_eq!(plane.raycast(ray, 10.0, false), None);
197 }
198
199 #[test]
200 fn test_raycast_infinite_plane_max_distance() {
201 let plane = InfinitePlane::default();
202
203 let ray = phys_geom::Ray::new(
204 Point3::new(0.0, 5.0, 0.0),
205 UnitVec3::new_normalize(Vec3::new(0.0, -1.0, 0.0)),
206 );
207
208 assert_eq!(plane.raycast(ray, 1.0, false), None);
210
211 let hit = plane.raycast(ray, 10.0, false).unwrap();
213 assert_relative_eq!(hit.distance, 5.0);
214 }
215
216 #[test]
217 fn test_infinite_plane_distance() {
218 let plane = InfinitePlane::default();
219
220 assert_relative_eq!(plane.distance(Point3::new(0.0, 3.0, 0.0)), 3.0);
222
223 assert_relative_eq!(plane.distance(Point3::new(0.0, 0.0, 0.0)), 0.0);
225
226 assert_relative_eq!(plane.distance(Point3::new(0.0, -3.0, 0.0)), -3.0);
228 }
229}