1use crate::{
2 primitives::{InfinitePlane3d, Plane2d},
3 Dir2, Dir3, Vec2, Vec3,
4};
5
6#[cfg(feature = "bevy_reflect")]
7use bevy_reflect::Reflect;
8#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
9use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
10
11#[derive(Clone, Copy, Debug, PartialEq)]
13#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
15#[cfg_attr(
16 all(feature = "serialize", feature = "bevy_reflect"),
17 reflect(Deserialize, Serialize)
18)]
19pub struct Ray2d {
20 pub origin: Vec2,
22 pub direction: Dir2,
24}
25
26impl Ray2d {
27 #[inline]
29 pub const fn new(origin: Vec2, direction: Dir2) -> Self {
30 Self { origin, direction }
31 }
32
33 #[inline]
35 pub fn get_point(&self, distance: f32) -> Vec2 {
36 self.origin + *self.direction * distance
37 }
38
39 #[inline]
41 pub fn intersect_plane(&self, plane_origin: Vec2, plane: Plane2d) -> Option<f32> {
42 let denominator = plane.normal.dot(*self.direction);
43 if denominator.abs() > f32::EPSILON {
44 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
45 if distance > f32::EPSILON {
46 return Some(distance);
47 }
48 }
49 None
50 }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq)]
55#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
56#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
57#[cfg_attr(
58 all(feature = "serialize", feature = "bevy_reflect"),
59 reflect(Deserialize, Serialize)
60)]
61pub struct Ray3d {
62 pub origin: Vec3,
64 pub direction: Dir3,
66}
67
68impl Ray3d {
69 #[inline]
71 pub const fn new(origin: Vec3, direction: Dir3) -> Self {
72 Self { origin, direction }
73 }
74
75 #[inline]
77 pub fn get_point(&self, distance: f32) -> Vec3 {
78 self.origin + *self.direction * distance
79 }
80
81 #[inline]
83 pub fn intersect_plane(&self, plane_origin: Vec3, plane: InfinitePlane3d) -> Option<f32> {
84 let denominator = plane.normal.dot(*self.direction);
85 if denominator.abs() > f32::EPSILON {
86 let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator;
87 if distance > f32::EPSILON {
88 return Some(distance);
89 }
90 }
91 None
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn intersect_plane_2d() {
101 let ray = Ray2d::new(Vec2::ZERO, Dir2::Y);
102
103 assert_eq!(
105 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::Y)),
106 Some(1.0)
107 );
108 assert_eq!(
109 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::NEG_Y)),
110 Some(1.0)
111 );
112 assert!(ray
113 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::Y))
114 .is_none());
115 assert!(ray
116 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::NEG_Y))
117 .is_none());
118
119 assert_eq!(
121 ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::ONE)),
122 Some(1.0)
123 );
124 assert!(ray
125 .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::ONE))
126 .is_none());
127
128 assert!(ray
130 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X))
131 .is_none());
132
133 assert!(ray
135 .intersect_plane(Vec2::X, Plane2d::new(Vec2::X + Vec2::Y * f32::EPSILON))
136 .is_none());
137 }
138
139 #[test]
140 fn intersect_plane_3d() {
141 let ray = Ray3d::new(Vec3::ZERO, Dir3::Z);
142
143 assert_eq!(
145 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::Z)),
146 Some(1.0)
147 );
148 assert_eq!(
149 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::NEG_Z)),
150 Some(1.0)
151 );
152 assert!(ray
153 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::Z))
154 .is_none());
155 assert!(ray
156 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::NEG_Z))
157 .is_none());
158
159 assert_eq!(
161 ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::ONE)),
162 Some(1.0)
163 );
164 assert!(ray
165 .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::ONE))
166 .is_none());
167
168 assert!(ray
170 .intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X))
171 .is_none());
172
173 assert!(ray
175 .intersect_plane(
176 Vec3::X,
177 InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON)
178 )
179 .is_none());
180 }
181}