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#[cfg(feature = "bytemuck")]
23use bytemuck::{Zeroable, Pod};
24
25/// Homogeneous vector in 3D space.
26#[repr(C)]
27pub struct HomogeneousVector<T, U> {
28 pub x: T,
29 pub y: T,
30 pub z: T,
31 pub w: T,
32 #[doc(hidden)]
33 pub _unit: PhantomData<U>,
34}
35
36impl<T: Copy, U> Copy for HomogeneousVector<T, U> {}
37
38impl<T: Clone, U> Clone for HomogeneousVector<T, U> {
39 fn clone(&self) -> Self {
40 HomogeneousVector {
41 x: self.x.clone(),
42 y: self.y.clone(),
43 z: self.z.clone(),
44 w: self.w.clone(),
45 _unit: PhantomData,
46 }
47 }
48}
49
50#[cfg(feature = "serde")]
51impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U>
52where
53 T: serde::Deserialize<'de>,
54{
55 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56 where
57 D: serde::Deserializer<'de>,
58 {
59 let (x, y, z, w) = serde::Deserialize::deserialize(deserializer)?;
60 Ok(HomogeneousVector {
61 x,
62 y,
63 z,
64 w,
65 _unit: PhantomData,
66 })
67 }
68}
69
70#[cfg(feature = "serde")]
71impl<T, U> serde::Serialize for HomogeneousVector<T, U>
72where
73 T: serde::Serialize,
74{
75 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76 where
77 S: serde::Serializer,
78 {
79 (&self.x, &self.y, &self.z, &self.w).serialize(serializer)
80 }
81}
82
83#[cfg(feature = "bytemuck")]
84unsafe impl<T: Zeroable, U> Zeroable for HomogeneousVector<T, U> {}
85
86#[cfg(feature = "bytemuck")]
87unsafe impl<T: Pod, U: 'static> Pod for HomogeneousVector<T, U> {}
88
89impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {}
90
91impl<T, U> PartialEq for HomogeneousVector<T, U>
92where
93 T: PartialEq,
94{
95 fn eq(&self, other: &Self) -> bool {
96 self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
97 }
98}
99
100impl<T, U> Hash for HomogeneousVector<T, U>
101where
102 T: Hash,
103{
104 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
105 self.x.hash(state:h);
106 self.y.hash(state:h);
107 self.z.hash(state:h);
108 self.w.hash(state:h);
109 }
110}
111
112impl<T, U> HomogeneousVector<T, U> {
113 /// Constructor taking scalar values directly.
114 #[inline]
115 pub const fn new(x: T, y: T, z: T, w: T) -> Self {
116 HomogeneousVector {
117 x,
118 y,
119 z,
120 w,
121 _unit: PhantomData,
122 }
123 }
124}
125
126impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> {
127 /// Convert into Cartesian 2D point.
128 ///
129 /// Returns None if the point is on or behind the W=0 hemisphere.
130 #[inline]
131 pub fn to_point2d(self) -> Option<Point2D<T, U>> {
132 if self.w > T::zero() {
133 Some(Point2D::new(self.x / self.w, self.y / self.w))
134 } else {
135 None
136 }
137 }
138
139 /// Convert into Cartesian 3D point.
140 ///
141 /// Returns None if the point is on or behind the W=0 hemisphere.
142 #[inline]
143 pub fn to_point3d(self) -> Option<Point3D<T, U>> {
144 if self.w > T::zero() {
145 Some(Point3D::new(
146 self.x / self.w,
147 self.y / self.w,
148 self.z / self.w,
149 ))
150 } else {
151 None
152 }
153 }
154}
155
156impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> {
157 #[inline]
158 fn from(v: Vector2D<T, U>) -> Self {
159 HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
160 }
161}
162
163impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> {
164 #[inline]
165 fn from(v: Vector3D<T, U>) -> Self {
166 HomogeneousVector::new(v.x, v.y, v.z, T::zero())
167 }
168}
169
170impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> {
171 #[inline]
172 fn from(p: Point2D<T, U>) -> Self {
173 HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
174 }
175}
176
177impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> {
178 #[inline]
179 fn from(p: Point3D<T, U>) -> Self {
180 HomogeneousVector::new(p.x, p.y, p.z, T::one())
181 }
182}
183
184impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
185 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186 f&mut DebugTuple<'_, '_>.debug_tuple(name:"")
187 .field(&self.x)
188 .field(&self.y)
189 .field(&self.z)
190 .field(&self.w)
191 .finish()
192 }
193}
194
195#[cfg(test)]
196mod homogeneous {
197 use super::HomogeneousVector;
198 use crate::default::{Point2D, Point3D};
199
200 #[test]
201 fn roundtrip() {
202 assert_eq!(
203 Some(Point2D::new(1.0, 2.0)),
204 HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d()
205 );
206 assert_eq!(
207 Some(Point3D::new(1.0, -2.0, 0.1)),
208 HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d()
209 );
210 }
211
212 #[test]
213 fn negative() {
214 assert_eq!(
215 None,
216 HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d()
217 );
218 assert_eq!(
219 None,
220 HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d()
221 );
222 }
223}
224