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 | #[cfg (feature = "bytemuck" )] |
16 | use bytemuck::{Pod, Zeroable}; |
17 | use core::cmp::{Eq, PartialEq}; |
18 | use core::fmt; |
19 | use core::hash::Hash; |
20 | use core::marker::PhantomData; |
21 | use core::ops::Div; |
22 | #[cfg (feature = "serde" )] |
23 | use serde; |
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 = "arbitrary" )] |
84 | impl<'a, T, U> arbitrary::Arbitrary<'a> for HomogeneousVector<T, U> |
85 | where |
86 | T: arbitrary::Arbitrary<'a>, |
87 | { |
88 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
89 | let (x, y, z, w) = arbitrary::Arbitrary::arbitrary(u)?; |
90 | Ok(HomogeneousVector { |
91 | x, |
92 | y, |
93 | z, |
94 | w, |
95 | _unit: PhantomData, |
96 | }) |
97 | } |
98 | } |
99 | |
100 | #[cfg (feature = "bytemuck" )] |
101 | unsafe impl<T: Zeroable, U> Zeroable for HomogeneousVector<T, U> {} |
102 | |
103 | #[cfg (feature = "bytemuck" )] |
104 | unsafe impl<T: Pod, U: 'static> Pod for HomogeneousVector<T, U> {} |
105 | |
106 | impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {} |
107 | |
108 | impl<T, U> PartialEq for HomogeneousVector<T, U> |
109 | where |
110 | T: PartialEq, |
111 | { |
112 | fn eq(&self, other: &Self) -> bool { |
113 | self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w |
114 | } |
115 | } |
116 | |
117 | impl<T, U> Hash for HomogeneousVector<T, U> |
118 | where |
119 | T: Hash, |
120 | { |
121 | fn hash<H: core::hash::Hasher>(&self, h: &mut H) { |
122 | self.x.hash(state:h); |
123 | self.y.hash(state:h); |
124 | self.z.hash(state:h); |
125 | self.w.hash(state:h); |
126 | } |
127 | } |
128 | |
129 | impl<T, U> HomogeneousVector<T, U> { |
130 | /// Constructor taking scalar values directly. |
131 | #[inline ] |
132 | pub const fn new(x: T, y: T, z: T, w: T) -> Self { |
133 | HomogeneousVector { |
134 | x, |
135 | y, |
136 | z, |
137 | w, |
138 | _unit: PhantomData, |
139 | } |
140 | } |
141 | } |
142 | |
143 | impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> { |
144 | /// Convert into Cartesian 2D point. |
145 | /// |
146 | /// Returns `None` if the point is on or behind the W=0 hemisphere. |
147 | #[inline ] |
148 | pub fn to_point2d(self) -> Option<Point2D<T, U>> { |
149 | if self.w > T::zero() { |
150 | Some(Point2D::new(self.x / self.w, self.y / self.w)) |
151 | } else { |
152 | None |
153 | } |
154 | } |
155 | |
156 | /// Convert into Cartesian 3D point. |
157 | /// |
158 | /// Returns `None` if the point is on or behind the W=0 hemisphere. |
159 | #[inline ] |
160 | pub fn to_point3d(self) -> Option<Point3D<T, U>> { |
161 | if self.w > T::zero() { |
162 | Some(Point3D::new( |
163 | self.x / self.w, |
164 | self.y / self.w, |
165 | self.z / self.w, |
166 | )) |
167 | } else { |
168 | None |
169 | } |
170 | } |
171 | } |
172 | |
173 | impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> { |
174 | #[inline ] |
175 | fn from(v: Vector2D<T, U>) -> Self { |
176 | HomogeneousVector::new(v.x, v.y, T::zero(), T::zero()) |
177 | } |
178 | } |
179 | |
180 | impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> { |
181 | #[inline ] |
182 | fn from(v: Vector3D<T, U>) -> Self { |
183 | HomogeneousVector::new(v.x, v.y, v.z, T::zero()) |
184 | } |
185 | } |
186 | |
187 | impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> { |
188 | #[inline ] |
189 | fn from(p: Point2D<T, U>) -> Self { |
190 | HomogeneousVector::new(p.x, p.y, T::zero(), T::one()) |
191 | } |
192 | } |
193 | |
194 | impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> { |
195 | #[inline ] |
196 | fn from(p: Point3D<T, U>) -> Self { |
197 | HomogeneousVector::new(p.x, p.y, p.z, T::one()) |
198 | } |
199 | } |
200 | |
201 | impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> { |
202 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
203 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"" ) |
204 | .field(&self.x) |
205 | .field(&self.y) |
206 | .field(&self.z) |
207 | .field(&self.w) |
208 | .finish() |
209 | } |
210 | } |
211 | |
212 | #[cfg (test)] |
213 | mod homogeneous { |
214 | use super::HomogeneousVector; |
215 | use crate::default::{Point2D, Point3D}; |
216 | |
217 | #[test ] |
218 | fn roundtrip() { |
219 | assert_eq!( |
220 | Some(Point2D::new(1.0, 2.0)), |
221 | HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d() |
222 | ); |
223 | assert_eq!( |
224 | Some(Point3D::new(1.0, -2.0, 0.1)), |
225 | HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d() |
226 | ); |
227 | } |
228 | |
229 | #[test ] |
230 | fn negative() { |
231 | assert_eq!( |
232 | None, |
233 | HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d() |
234 | ); |
235 | assert_eq!( |
236 | None, |
237 | HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d() |
238 | ); |
239 | } |
240 | } |
241 | |