blob: 29a50735c8523b101f7994db10da9692643ce28b [file] [log] [blame]
 // 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use point::{TypedPoint2D, TypedPoint3D}; use vector::{TypedVector2D, TypedVector3D}; use num::{One, Zero}; use core::fmt; use core::marker::PhantomData; use core::ops::Div; /// Homogeneous vector in 3D space. #[derive(EuclidMatrix)] #[repr(C)] pub struct HomogeneousVector { pub x: T, pub y: T, pub z: T, pub w: T, #[doc(hidden)] pub _unit: PhantomData, } impl HomogeneousVector { /// Constructor taking scalar values directly. #[inline] pub fn new(x: T, y: T, z: T, w: T) -> Self { HomogeneousVector { x, y, z, w, _unit: PhantomData } } } impl + Zero + PartialOrd, U> HomogeneousVector { /// 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> { if self.w > T::zero() { Some(TypedPoint2D::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> { if self.w > T::zero() { Some(TypedPoint3D::new(self.x / self.w, self.y / self.w, self.z / self.w)) } else { None } } } impl From> for HomogeneousVector { #[inline] fn from(v: TypedVector2D) -> Self { HomogeneousVector::new(v.x, v.y, T::zero(), T::zero()) } } impl From> for HomogeneousVector { #[inline] fn from(v: TypedVector3D) -> Self { HomogeneousVector::new(v.x, v.y, v.z, T::zero()) } } impl From> for HomogeneousVector { #[inline] fn from(p: TypedPoint2D) -> Self { HomogeneousVector::new(p.x, p.y, T::zero(), T::one()) } } impl From> for HomogeneousVector { #[inline] fn from(p: TypedPoint3D) -> Self { HomogeneousVector::new(p.x, p.y, p.z, T::one()) } } impl fmt::Debug for HomogeneousVector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({:?},{:?},{:?},{:?})", self.x, self.y, self.z, self.w) } } impl fmt::Display for HomogeneousVector { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "({},{},{},{})", self.x, self.y, self.z, self.w) } } #[cfg(test)] mod homogeneous { use super::HomogeneousVector; use point::{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::::new(1.0, 2.0, 3.0, 0.0).to_point2d()); assert_eq!(None, HomogeneousVector::::new(1.0, -2.0, -3.0, -2.0).to_point3d()); } }