diff options
Diffstat (limited to 'third_party/rust/euclid/src/homogen.rs')
-rw-r--r-- | third_party/rust/euclid/src/homogen.rs | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/third_party/rust/euclid/src/homogen.rs b/third_party/rust/euclid/src/homogen.rs new file mode 100644 index 0000000000..afd4f22957 --- /dev/null +++ b/third_party/rust/euclid/src/homogen.rs @@ -0,0 +1,223 @@ +// Copyright 2018 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::point::{Point2D, Point3D}; +use crate::vector::{Vector2D, Vector3D}; + +use crate::num::{One, Zero}; + +use core::cmp::{Eq, PartialEq}; +use core::fmt; +use core::hash::Hash; +use core::marker::PhantomData; +use core::ops::Div; +#[cfg(feature = "serde")] +use serde; +#[cfg(feature = "bytemuck")] +use bytemuck::{Zeroable, Pod}; + +/// Homogeneous vector in 3D space. +#[repr(C)] +pub struct HomogeneousVector<T, U> { + pub x: T, + pub y: T, + pub z: T, + pub w: T, + #[doc(hidden)] + pub _unit: PhantomData<U>, +} + +impl<T: Copy, U> Copy for HomogeneousVector<T, U> {} + +impl<T: Clone, U> Clone for HomogeneousVector<T, U> { + fn clone(&self) -> Self { + HomogeneousVector { + x: self.x.clone(), + y: self.y.clone(), + z: self.z.clone(), + w: self.w.clone(), + _unit: PhantomData, + } + } +} + +#[cfg(feature = "serde")] +impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U> +where + T: serde::Deserialize<'de>, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let (x, y, z, w) = serde::Deserialize::deserialize(deserializer)?; + Ok(HomogeneousVector { + x, + y, + z, + w, + _unit: PhantomData, + }) + } +} + +#[cfg(feature = "serde")] +impl<T, U> serde::Serialize for HomogeneousVector<T, U> +where + T: serde::Serialize, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + (&self.x, &self.y, &self.z, &self.w).serialize(serializer) + } +} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Zeroable, U> Zeroable for HomogeneousVector<T, U> {} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Pod, U: 'static> Pod for HomogeneousVector<T, U> {} + +impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {} + +impl<T, U> PartialEq for HomogeneousVector<T, U> +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w + } +} + +impl<T, U> Hash for HomogeneousVector<T, U> +where + T: Hash, +{ + fn hash<H: core::hash::Hasher>(&self, h: &mut H) { + self.x.hash(h); + self.y.hash(h); + self.z.hash(h); + self.w.hash(h); + } +} + +impl<T, U> HomogeneousVector<T, U> { + /// Constructor taking scalar values directly. + #[inline] + pub const fn new(x: T, y: T, z: T, w: T) -> Self { + HomogeneousVector { + x, + y, + z, + w, + _unit: PhantomData, + } + } +} + +impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> { + /// Convert into Cartesian 2D point. + /// + /// Returns None if the point is on or behind the W=0 hemisphere. + #[inline] + pub fn to_point2d(self) -> Option<Point2D<T, U>> { + if self.w > T::zero() { + Some(Point2D::new(self.x / self.w, self.y / self.w)) + } else { + None + } + } + + /// Convert into Cartesian 3D point. + /// + /// Returns None if the point is on or behind the W=0 hemisphere. + #[inline] + pub fn to_point3d(self) -> Option<Point3D<T, U>> { + if self.w > T::zero() { + Some(Point3D::new( + self.x / self.w, + self.y / self.w, + self.z / self.w, + )) + } else { + None + } + } +} + +impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> { + #[inline] + fn from(v: Vector2D<T, U>) -> Self { + HomogeneousVector::new(v.x, v.y, T::zero(), T::zero()) + } +} + +impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> { + #[inline] + fn from(v: Vector3D<T, U>) -> Self { + HomogeneousVector::new(v.x, v.y, v.z, T::zero()) + } +} + +impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> { + #[inline] + fn from(p: Point2D<T, U>) -> Self { + HomogeneousVector::new(p.x, p.y, T::zero(), T::one()) + } +} + +impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> { + #[inline] + fn from(p: Point3D<T, U>) -> Self { + HomogeneousVector::new(p.x, p.y, p.z, T::one()) + } +} + +impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("") + .field(&self.x) + .field(&self.y) + .field(&self.z) + .field(&self.w) + .finish() + } +} + +#[cfg(test)] +mod homogeneous { + use super::HomogeneousVector; + use crate::default::{Point2D, Point3D}; + + #[test] + fn roundtrip() { + assert_eq!( + Some(Point2D::new(1.0, 2.0)), + HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d() + ); + assert_eq!( + Some(Point3D::new(1.0, -2.0, 0.1)), + HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d() + ); + } + + #[test] + fn negative() { + assert_eq!( + None, + HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d() + ); + assert_eq!( + None, + HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d() + ); + } +} |