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]
46 pub fn intersect_plane(&self, plane_origin: Vec2, plane: Plane2d) -> Option<f32> {
47 let denominator = plane.normal.dot(*self.direction);
48 if ops::abs(denominator) > f32::EPSILON {
49 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
50 if distance > f32::EPSILON {
51 return Some(distance);
52 }
53 }
54 None
55 }
56}
57
58#[derive(Clone, Copy, Debug, PartialEq)]
60#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
61#[cfg_attr(
62 feature = "bevy_reflect",
63 derive(Reflect),
64 reflect(Debug, PartialEq, Clone)
65)]
66#[cfg_attr(
67 all(feature = "serialize", feature = "bevy_reflect"),
68 reflect(Deserialize, Serialize)
69)]
70pub struct Ray3d {
71 pub origin: Vec3,
73 pub direction: Dir3,
75}
76
77impl Ray3d {
78 #[inline]
80 pub const fn new(origin: Vec3, direction: Dir3) -> Self {
81 Self { origin, direction }
82 }
83
84 #[inline]
86 pub fn get_point(&self, distance: f32) -> Vec3 {
87 self.origin + *self.direction * distance
88 }
89
90 #[inline]
92 pub fn intersect_plane(&self, plane_origin: Vec3, plane: InfinitePlane3d) -> Option<f32> {
93 let denominator = plane.normal.dot(*self.direction);
94 if ops::abs(denominator) > f32::EPSILON {
95 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
96 if distance > f32::EPSILON {
97 return Some(distance);
98 }
99 }
100 None
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn intersect_plane_2d() {
110 let ray = Ray2d::new(Vec2::ZERO, Dir2::Y);
111
112 assert_eq!(
114 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::Y)),
115 Some(1.0)
116 );
117 assert_eq!(
118 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::NEG_Y)),
119 Some(1.0)
120 );
121 assert!(ray
122 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::Y))
123 .is_none());
124 assert!(ray
125 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::NEG_Y))
126 .is_none());
127
128 assert_eq!(
130 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::ONE)),
131 Some(1.0)
132 );
133 assert!(ray
134 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::ONE))
135 .is_none());
136
137 assert!(ray
139 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X))
140 .is_none());
141
142 assert!(ray
144 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X + Vec2::Y * f32::EPSILON))
145 .is_none());
146 }
147
148 #[test]
149 fn intersect_plane_3d() {
150 let ray = Ray3d::new(Vec3::ZERO, Dir3::Z);
151
152 assert_eq!(
154 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::Z)),
155 Some(1.0)
156 );
157 assert_eq!(
158 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::NEG_Z)),
159 Some(1.0)
160 );
161 assert!(ray
162 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::Z))
163 .is_none());
164 assert!(ray
165 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::NEG_Z))
166 .is_none());
167
168 assert_eq!(
170 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::ONE)),
171 Some(1.0)
172 );
173 assert!(ray
174 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::ONE))
175 .is_none());
176
177 assert!(ray
179 .intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X))
180 .is_none());
181
182 assert!(ray
184 .intersect_plane(
185 Vec3::X,
186 InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON)
187 )
188 .is_none());
189 }
190}