diff --git a/src/ray.rs b/src/ray.rs index 7cf160a..ebe45b8 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,5 +1,6 @@ //! Generic rays +use core::convert::TryFrom; use core::fmt; use std::marker::PhantomData; @@ -9,6 +10,7 @@ use cgmath::{Point2, Point3}; use cgmath::{Vector2, Vector3}; use crate::traits::{Continuous, ContinuousTransformed, Discrete, DiscreteTransformed}; +use crate::Line; /// A generic ray starting at `origin` and extending infinitely in /// `direction`. @@ -141,3 +143,33 @@ where .map(|p| transform.transform_point(p)) } } + +impl TryFrom<&Line> for Ray +where + S: BaseFloat, + V: InnerSpace + VectorSpace, + P: EuclideanSpace + cgmath::UlpsEq + cgmath::AbsDiffEq, +{ + type Error = (); + + #[inline] + /// Converts an euclidean Line into an euclidean Ray. The created ray will inherit the origin + /// and direction of the line. This conversion will fail if the line describes a single point. + ///``` + /// use collision::{Line3,Ray}; + /// use cgmath::Point3; + /// use std::convert::TryFrom; + /// let line: Line3 = Line3::new(Point3::new(1f32, 2., 3.), Point3::new(5f32, 56., 6.)); + /// assert!(Ray::try_from(&line).is_ok()); + ///``` + fn try_from(l: &Line) -> Result { + if l.dest + .ulps_eq(&l.origin, S::epsilon(), S::default_max_ulps()) + { + // Can't build a ray from a single point + Err(()) + } else { + Ok(Self::new(l.origin, (l.dest - l.origin).normalize())) + } + } +} diff --git a/tests/ray.rs b/tests/ray.rs new file mode 100644 index 0000000..388849d --- /dev/null +++ b/tests/ray.rs @@ -0,0 +1,30 @@ +use cgmath::assert_ulps_eq; +use cgmath::{InnerSpace, Point2, Point3}; +use collision::{Line2, Line3, Ray, Ray2, Ray3}; +use std::convert::TryFrom; + +#[test] +fn test_ray_from_line_2d() { + let line = Line2::new(Point2::new(1f32, 2.), Point2::new(3f32, 4.)); + assert!(Ray::try_from(&line).is_ok()); + if let Ok(ray) = Ray2::try_from(&line) { + assert_ulps_eq!(ray.origin, &line.origin); + assert_ulps_eq!(ray.direction, (&line.dest - &line.origin).normalize()); + } + // A single point should not create a Ray + let line = Line2::new(Point2::new(1., 2.), Point2::new(1., 2.)); + assert!(!Ray::try_from(&line).is_ok()); +} + +#[test] +fn test_ray_from_line_3d() { + let line: Line3 = Line3::new(Point3::new(1f32, 2., 3.), Point3::new(5f32, 56., 6.)); + assert!(Ray::try_from(&line).is_ok()); + if let Ok(ray) = Ray3::try_from(&line) { + assert_ulps_eq!(ray.origin, &line.origin); + assert_ulps_eq!(ray.direction, (&line.dest - &line.origin).normalize()); + } + // A single point should not create a Ray + let line = Line2::new(Point2::new(1., 2.), Point2::new(1., 2.)); + assert!(!Ray::try_from(&line).is_ok()); +}