euclid/
homogen.rs

1// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::point::{Point2D, Point3D};
11use crate::vector::{Vector2D, Vector3D};
12
13use crate::num::{One, Zero};
14
15use core::cmp::{Eq, PartialEq};
16use core::fmt;
17use core::hash::Hash;
18use core::marker::PhantomData;
19use core::ops::Div;
20#[cfg(feature = "serde")]
21use serde;
22
23/// Homogeneous vector in 3D space.
24#[repr(C)]
25pub struct HomogeneousVector<T, U> {
26    pub x: T,
27    pub y: T,
28    pub z: T,
29    pub w: T,
30    #[doc(hidden)]
31    pub _unit: PhantomData<U>,
32}
33
34impl<T: Copy, U> Copy for HomogeneousVector<T, U> {}
35
36impl<T: Clone, U> Clone for HomogeneousVector<T, U> {
37    fn clone(&self) -> Self {
38        HomogeneousVector {
39            x: self.x.clone(),
40            y: self.y.clone(),
41            z: self.z.clone(),
42            w: self.w.clone(),
43            _unit: PhantomData,
44        }
45    }
46}
47
48#[cfg(feature = "serde")]
49impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U>
50where
51    T: serde::Deserialize<'de>,
52{
53    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
54    where
55        D: serde::Deserializer<'de>,
56    {
57        let (x, y, z, w) = serde::Deserialize::deserialize(deserializer)?;
58        Ok(HomogeneousVector {
59            x,
60            y,
61            z,
62            w,
63            _unit: PhantomData,
64        })
65    }
66}
67
68#[cfg(feature = "serde")]
69impl<T, U> serde::Serialize for HomogeneousVector<T, U>
70where
71    T: serde::Serialize,
72{
73    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74    where
75        S: serde::Serializer,
76    {
77        (&self.x, &self.y, &self.z, &self.w).serialize(serializer)
78    }
79}
80
81impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {}
82
83impl<T, U> PartialEq for HomogeneousVector<T, U>
84where
85    T: PartialEq,
86{
87    fn eq(&self, other: &Self) -> bool {
88        self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
89    }
90}
91
92impl<T, U> Hash for HomogeneousVector<T, U>
93where
94    T: Hash,
95{
96    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
97        self.x.hash(h);
98        self.y.hash(h);
99        self.z.hash(h);
100        self.w.hash(h);
101    }
102}
103
104impl<T, U> HomogeneousVector<T, U> {
105    /// Constructor taking scalar values directly.
106    #[inline]
107    pub const fn new(x: T, y: T, z: T, w: T) -> Self {
108        HomogeneousVector {
109            x,
110            y,
111            z,
112            w,
113            _unit: PhantomData,
114        }
115    }
116}
117
118impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> {
119    /// Convert into Cartesian 2D point.
120    ///
121    /// Returns None if the point is on or behind the W=0 hemisphere.
122    #[inline]
123    pub fn to_point2d(self) -> Option<Point2D<T, U>> {
124        if self.w > T::zero() {
125            Some(Point2D::new(self.x / self.w, self.y / self.w))
126        } else {
127            None
128        }
129    }
130
131    /// Convert into Cartesian 3D point.
132    ///
133    /// Returns None if the point is on or behind the W=0 hemisphere.
134    #[inline]
135    pub fn to_point3d(self) -> Option<Point3D<T, U>> {
136        if self.w > T::zero() {
137            Some(Point3D::new(
138                self.x / self.w,
139                self.y / self.w,
140                self.z / self.w,
141            ))
142        } else {
143            None
144        }
145    }
146}
147
148impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> {
149    #[inline]
150    fn from(v: Vector2D<T, U>) -> Self {
151        HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
152    }
153}
154
155impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> {
156    #[inline]
157    fn from(v: Vector3D<T, U>) -> Self {
158        HomogeneousVector::new(v.x, v.y, v.z, T::zero())
159    }
160}
161
162impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> {
163    #[inline]
164    fn from(p: Point2D<T, U>) -> Self {
165        HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
166    }
167}
168
169impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> {
170    #[inline]
171    fn from(p: Point3D<T, U>) -> Self {
172        HomogeneousVector::new(p.x, p.y, p.z, T::one())
173    }
174}
175
176impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        f.debug_tuple("")
179            .field(&self.x)
180            .field(&self.y)
181            .field(&self.z)
182            .field(&self.w)
183            .finish()
184    }
185}
186
187#[cfg(test)]
188mod homogeneous {
189    use super::HomogeneousVector;
190    use crate::default::{Point2D, Point3D};
191
192    #[test]
193    fn roundtrip() {
194        assert_eq!(
195            Some(Point2D::new(1.0, 2.0)),
196            HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d()
197        );
198        assert_eq!(
199            Some(Point3D::new(1.0, -2.0, 0.1)),
200            HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d()
201        );
202    }
203
204    #[test]
205    fn negative() {
206        assert_eq!(
207            None,
208            HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d()
209        );
210        assert_eq!(
211            None,
212            HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d()
213        );
214    }
215}