1use crate::{
2 ops,
3 primitives::{InfinitePlane3d, Plane2d},
4 Dir2, Dir3, Vec2, Vec3,
5};
6
7#[cfg(feature = "bevy_reflect")]
8use bevy_reflect::Reflect;
9#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
10use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
11
12#[derive(Clone, Copy, Debug, PartialEq)]
14#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(
16 feature = "bevy_reflect",
17 derive(Reflect),
18 reflect(Debug, PartialEq, Clone)
19)]
20#[cfg_attr(
21 all(feature = "serialize", feature = "bevy_reflect"),
22 reflect(Deserialize, Serialize)
23)]
24pub struct Ray2d {
25 pub origin: Vec2,
27 pub direction: Dir2,
29}
30
31impl Ray2d {
32 #[inline]
34 pub const fn new(origin: Vec2, direction: Dir2) -> Self {
35 Self { origin, direction }
36 }
37
38 #[inline]
40 pub fn get_point(&self, distance: f32) -> Vec2 {
41 self.origin + *self.direction * distance
42 }
43
44 #[inline]
48 pub fn intersect_plane(&self, plane_origin: Vec2, plane: Plane2d) -> Option<f32> {
49 let denominator = plane.normal.dot(*self.direction);
50 if ops::abs(denominator) > f32::EPSILON {
51 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
52 if distance > f32::EPSILON {
53 return Some(distance);
54 }
55 }
56 None
57 }
58
59 #[inline]
63 pub fn plane_intersection_point(&self, plane_origin: Vec2, plane: Plane2d) -> Option<Vec2> {
64 self.intersect_plane(plane_origin, plane)
65 .map(|distance| self.get_point(distance))
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq)]
71#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
72#[cfg_attr(
73 feature = "bevy_reflect",
74 derive(Reflect),
75 reflect(Debug, PartialEq, Clone)
76)]
77#[cfg_attr(
78 all(feature = "serialize", feature = "bevy_reflect"),
79 reflect(Deserialize, Serialize)
80)]
81pub struct Ray3d {
82 pub origin: Vec3,
84 pub direction: Dir3,
86}
87
88impl Ray3d {
89 #[inline]
91 pub const fn new(origin: Vec3, direction: Dir3) -> Self {
92 Self { origin, direction }
93 }
94
95 #[inline]
97 pub fn get_point(&self, distance: f32) -> Vec3 {
98 self.origin + *self.direction * distance
99 }
100
101 #[inline]
105 pub fn intersect_plane(&self, plane_origin: Vec3, plane: InfinitePlane3d) -> Option<f32> {
106 let denominator = plane.normal.dot(*self.direction);
107 if ops::abs(denominator) > f32::EPSILON {
108 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
109 if distance > f32::EPSILON {
110 return Some(distance);
111 }
112 }
113 None
114 }
115
116 #[inline]
120 pub fn plane_intersection_point(
121 &self,
122 plane_origin: Vec3,
123 plane: InfinitePlane3d,
124 ) -> Option<Vec3> {
125 self.intersect_plane(plane_origin, plane)
126 .map(|distance| self.get_point(distance))
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn intersect_plane_2d() {
136 let ray = Ray2d::new(Vec2::ZERO, Dir2::Y);
137
138 assert_eq!(
140 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::Y)),
141 Some(1.0)
142 );
143 assert_eq!(
144 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::NEG_Y)),
145 Some(1.0)
146 );
147 assert!(ray
148 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::Y))
149 .is_none());
150 assert!(ray
151 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::NEG_Y))
152 .is_none());
153
154 assert_eq!(
156 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::ONE)),
157 Some(1.0)
158 );
159 assert!(ray
160 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::ONE))
161 .is_none());
162
163 assert!(ray
165 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X))
166 .is_none());
167
168 assert!(ray
170 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X + Vec2::Y * f32::EPSILON))
171 .is_none());
172 }
173
174 #[test]
175 fn intersect_plane_3d() {
176 let ray = Ray3d::new(Vec3::ZERO, Dir3::Z);
177
178 assert_eq!(
180 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::Z)),
181 Some(1.0)
182 );
183 assert_eq!(
184 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::NEG_Z)),
185 Some(1.0)
186 );
187 assert!(ray
188 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::Z))
189 .is_none());
190 assert!(ray
191 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::NEG_Z))
192 .is_none());
193
194 assert_eq!(
196 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::ONE)),
197 Some(1.0)
198 );
199 assert!(ray
200 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::ONE))
201 .is_none());
202
203 assert!(ray
205 .intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X))
206 .is_none());
207
208 assert!(ray
210 .intersect_plane(
211 Vec3::X,
212 InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON)
213 )
214 .is_none());
215 }
216}