// 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 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 { pub x: T, pub y: T, pub z: T, pub w: T, #[doc(hidden)] pub _unit: PhantomData, } impl Copy for HomogeneousVector {} impl Clone for HomogeneousVector { 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 where T: serde::Deserialize<'de>, { fn deserialize(deserializer: D) -> Result 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 serde::Serialize for HomogeneousVector where T: serde::Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { (&self.x, &self.y, &self.z, &self.w).serialize(serializer) } } #[cfg(feature = "bytemuck")] unsafe impl Zeroable for HomogeneousVector {} #[cfg(feature = "bytemuck")] unsafe impl Pod for HomogeneousVector {} impl Eq for HomogeneousVector where T: Eq {} impl PartialEq for HomogeneousVector 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 Hash for HomogeneousVector where T: Hash, { fn hash(&self, h: &mut H) { self.x.hash(h); self.y.hash(h); self.z.hash(h); self.w.hash(h); } } impl HomogeneousVector { /// 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 + 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(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> { if self.w > T::zero() { Some(Point3D::new( self.x / self.w, self.y / self.w, self.z / self.w, )) } else { None } } } impl From> for HomogeneousVector { #[inline] fn from(v: Vector2D) -> Self { HomogeneousVector::new(v.x, v.y, T::zero(), T::zero()) } } impl From> for HomogeneousVector { #[inline] fn from(v: Vector3D) -> Self { HomogeneousVector::new(v.x, v.y, v.z, T::zero()) } } impl From> for HomogeneousVector { #[inline] fn from(p: Point2D) -> Self { HomogeneousVector::new(p.x, p.y, T::zero(), T::one()) } } impl From> for HomogeneousVector { #[inline] fn from(p: Point3D) -> 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 { 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::::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() ); } }