Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/ray.rs
6595 views
1
use crate::{
2
ops,
3
primitives::{InfinitePlane3d, Plane2d},
4
Dir2, Dir3, Vec2, Vec3,
5
};
6
7
#[cfg(feature = "bevy_reflect")]
8
use bevy_reflect::Reflect;
9
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
10
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
11
12
/// An infinite half-line starting at `origin` and going in `direction` in 2D space.
13
#[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
)]
24
pub struct Ray2d {
25
/// The origin of the ray.
26
pub origin: Vec2,
27
/// The direction of the ray.
28
pub direction: Dir2,
29
}
30
31
impl Ray2d {
32
/// Create a new `Ray2d` from a given origin and direction
33
#[inline]
34
pub const fn new(origin: Vec2, direction: Dir2) -> Self {
35
Self { origin, direction }
36
}
37
38
/// Get a point at a given distance along the ray
39
#[inline]
40
pub fn get_point(&self, distance: f32) -> Vec2 {
41
self.origin + *self.direction * distance
42
}
43
44
/// Get the distance to a plane if the ray intersects it
45
#[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
/// An infinite half-line starting at `origin` and going in `direction` in 3D space.
59
#[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
)]
70
pub struct Ray3d {
71
/// The origin of the ray.
72
pub origin: Vec3,
73
/// The direction of the ray.
74
pub direction: Dir3,
75
}
76
77
impl Ray3d {
78
/// Create a new `Ray3d` from a given origin and direction
79
#[inline]
80
pub const fn new(origin: Vec3, direction: Dir3) -> Self {
81
Self { origin, direction }
82
}
83
84
/// Get a point at a given distance along the ray
85
#[inline]
86
pub fn get_point(&self, distance: f32) -> Vec3 {
87
self.origin + *self.direction * distance
88
}
89
90
/// Get the distance to a plane if the ray intersects it
91
#[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)]
105
mod tests {
106
use super::*;
107
108
#[test]
109
fn intersect_plane_2d() {
110
let ray = Ray2d::new(Vec2::ZERO, Dir2::Y);
111
112
// Orthogonal, and test that an inverse plane_normal has the same result
113
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
// Diagonal
129
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
// Parallel
138
assert!(ray
139
.intersect_plane(Vec2::X, Plane2d::new(Vec2::X))
140
.is_none());
141
142
// Parallel with simulated rounding error
143
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
// Orthogonal, and test that an inverse plane_normal has the same result
153
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
// Diagonal
169
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
// Parallel
178
assert!(ray
179
.intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X))
180
.is_none());
181
182
// Parallel with simulated rounding error
183
assert!(ray
184
.intersect_plane(
185
Vec3::X,
186
InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON)
187
)
188
.is_none());
189
}
190
}
191
192