From cb399c9c657962ef209ce0e1c87c7b0fcd74249b Mon Sep 17 00:00:00 2001 From: ira Date: Fri, 2 Dec 2022 02:36:44 +0000 Subject: [PATCH] Add methods `intersect_plane` and `get_point` to `Ray` (#6179) Co-authored-by: devil-ira --- crates/bevy_math/src/ray.rs | 55 ++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs index bf0a8f47310d8..a755465dc1470 100644 --- a/crates/bevy_math/src/ray.rs +++ b/crates/bevy_math/src/ray.rs @@ -6,6 +6,59 @@ use crate::Vec3; pub struct Ray { /// The origin of the ray. pub origin: Vec3, - /// The direction of the ray. + /// A normalized vector representing the direction of the ray. pub direction: Vec3, } + +impl Ray { + /// Returns the distance to the plane if the ray intersects it. + #[inline] + pub fn intersect_plane(&self, plane_origin: Vec3, plane_normal: Vec3) -> Option { + let denominator = plane_normal.dot(self.direction); + if denominator.abs() > f32::EPSILON { + let distance = (plane_origin - self.origin).dot(plane_normal) / denominator; + if distance >= f32::EPSILON { + return Some(distance); + } + } + None + } + + /// Retrieve a point at the given distance along the ray. + #[inline] + pub fn get_point(&self, distance: f32) -> Vec3 { + self.origin + self.direction * distance + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn intersects_plane() { + let ray = Ray { + origin: Vec3::ZERO, + direction: Vec3::Z, + }; + + // Orthogonal, and test that plane_normal direction doesn't matter + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::Z)); + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::NEG_Z)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::Z)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::NEG_Z)); + + // Diagonal + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::ONE)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::ONE)); + + // Parralel + assert_eq!(None, ray.intersect_plane(Vec3::X, Vec3::X)); + + // Parralel with simulated rounding error + assert_eq!( + None, + ray.intersect_plane(Vec3::X, Vec3::X + Vec3::Z * f32::EPSILON) + ); + } +}