1use crate::{InfinitePlane3d, Plane2d};
2use bevy_math::{ops, Dir2, Dir3, Vec2, Vec3};
3
4#[cfg(feature = "bevy_reflect")]
5use bevy_reflect::Reflect;
6#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
7use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
8
9#[derive(Clone, Copy, Debug, PartialEq)]
11#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
12#[cfg_attr(
13 feature = "bevy_reflect",
14 derive(Reflect),
15 reflect(Debug, PartialEq, Clone)
16)]
17#[cfg_attr(
18 all(feature = "serialize", feature = "bevy_reflect"),
19 reflect(Deserialize, Serialize)
20)]
21pub struct Ray2d {
22 pub origin: Vec2,
24 pub direction: Dir2,
26}
27
28impl Ray2d {
29 #[inline]
31 pub const fn new(origin: Vec2, direction: Dir2) -> Self {
32 Self { origin, direction }
33 }
34
35 #[inline]
37 pub fn get_point(&self, distance: f32) -> Vec2 {
38 self.origin + *self.direction * distance
39 }
40
41 #[inline]
45 pub fn intersect_plane(&self, plane_origin: Vec2, plane: Plane2d) -> Option<f32> {
46 let denominator = plane.normal.dot(*self.direction);
47 if ops::abs(denominator) > f32::EPSILON {
48 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
49 if distance > f32::EPSILON {
50 return Some(distance);
51 }
52 }
53 None
54 }
55
56 #[inline]
60 pub fn plane_intersection_point(&self, plane_origin: Vec2, plane: Plane2d) -> Option<Vec2> {
61 self.intersect_plane(plane_origin, plane)
62 .map(|distance| self.get_point(distance))
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq)]
68#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
69#[cfg_attr(
70 feature = "bevy_reflect",
71 derive(Reflect),
72 reflect(Debug, PartialEq, Clone)
73)]
74#[cfg_attr(
75 all(feature = "serialize", feature = "bevy_reflect"),
76 reflect(Deserialize, Serialize)
77)]
78pub struct Ray3d {
79 pub origin: Vec3,
81 pub direction: Dir3,
83}
84
85impl Ray3d {
86 #[inline]
88 pub const fn new(origin: Vec3, direction: Dir3) -> Self {
89 Self { origin, direction }
90 }
91
92 #[inline]
94 pub fn get_point(&self, distance: f32) -> Vec3 {
95 self.origin + *self.direction * distance
96 }
97
98 #[inline]
102 pub fn intersect_plane(&self, plane_origin: Vec3, plane: InfinitePlane3d) -> Option<f32> {
103 let denominator = plane.normal.dot(*self.direction);
104 if ops::abs(denominator) > f32::EPSILON {
105 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
106 if distance > f32::EPSILON {
107 return Some(distance);
108 }
109 }
110 None
111 }
112
113 #[inline]
117 pub fn plane_intersection_point(
118 &self,
119 plane_origin: Vec3,
120 plane: InfinitePlane3d,
121 ) -> Option<Vec3> {
122 self.intersect_plane(plane_origin, plane)
123 .map(|distance| self.get_point(distance))
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn intersect_plane_2d() {
133 let ray = Ray2d::new(Vec2::ZERO, Dir2::Y);
134
135 assert_eq!(
137 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::Y)),
138 Some(1.0)
139 );
140 assert_eq!(
141 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::NEG_Y)),
142 Some(1.0)
143 );
144 assert!(ray
145 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::Y))
146 .is_none());
147 assert!(ray
148 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::NEG_Y))
149 .is_none());
150
151 assert_eq!(
153 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::ONE)),
154 Some(1.0)
155 );
156 assert!(ray
157 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::ONE))
158 .is_none());
159
160 assert!(ray
162 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X))
163 .is_none());
164
165 assert!(ray
167 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X + Vec2::Y * f32::EPSILON))
168 .is_none());
169 }
170
171 #[test]
172 fn intersect_plane_3d() {
173 let ray = Ray3d::new(Vec3::ZERO, Dir3::Z);
174
175 assert_eq!(
177 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::Z)),
178 Some(1.0)
179 );
180 assert_eq!(
181 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::NEG_Z)),
182 Some(1.0)
183 );
184 assert!(ray
185 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::Z))
186 .is_none());
187 assert!(ray
188 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::NEG_Z))
189 .is_none());
190
191 assert_eq!(
193 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::ONE)),
194 Some(1.0)
195 );
196 assert!(ray
197 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::ONE))
198 .is_none());
199
200 assert!(ray
202 .intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X))
203 .is_none());
204
205 assert!(ray
207 .intersect_plane(
208 Vec3::X,
209 InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON)
210 )
211 .is_none());
212 }
213}