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 | |
10 | use crate::point::{Point2D, Point3D}; |
11 | use crate::vector::{Vector2D, Vector3D}; |
12 | |
13 | use crate::num::{One, Zero}; |
14 | |
15 | use core::cmp::{Eq, PartialEq}; |
16 | use core::fmt; |
17 | use core::hash::Hash; |
18 | use core::marker::PhantomData; |
19 | use core::ops::Div; |
20 | #[cfg (feature = "serde" )] |
21 | use serde; |
22 | #[cfg (feature = "bytemuck" )] |
23 | use bytemuck::{Zeroable, Pod}; |
24 | |
25 | /// Homogeneous vector in 3D space. |
26 | #[repr (C)] |
27 | pub 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 | |
36 | impl<T: Copy, U> Copy for HomogeneousVector<T, U> {} |
37 | |
38 | impl<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" )] |
51 | impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U> |
52 | where |
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" )] |
71 | impl<T, U> serde::Serialize for HomogeneousVector<T, U> |
72 | where |
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" )] |
84 | unsafe impl<T: Zeroable, U> Zeroable for HomogeneousVector<T, U> {} |
85 | |
86 | #[cfg (feature = "bytemuck" )] |
87 | unsafe impl<T: Pod, U: 'static> Pod for HomogeneousVector<T, U> {} |
88 | |
89 | impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {} |
90 | |
91 | impl<T, U> PartialEq for HomogeneousVector<T, U> |
92 | where |
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 | |
100 | impl<T, U> Hash for HomogeneousVector<T, U> |
101 | where |
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 | |
112 | impl<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 | |
126 | impl<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 | |
156 | impl<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 | |
163 | impl<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 | |
170 | impl<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 | |
177 | impl<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 | |
184 | impl<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)] |
196 | mod 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 | |