1 | // Copyright 2013 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::approxeq::ApproxEq; |
11 | use crate::trig::Trig; |
12 | |
13 | use core::cmp::{Eq, PartialEq}; |
14 | use core::hash::Hash; |
15 | use core::iter::Sum; |
16 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}; |
17 | |
18 | #[cfg (feature = "bytemuck" )] |
19 | use bytemuck::{Pod, Zeroable}; |
20 | use num_traits::real::Real; |
21 | use num_traits::{Float, FloatConst, NumCast, One, Zero}; |
22 | #[cfg (feature = "serde" )] |
23 | use serde::{Deserialize, Serialize}; |
24 | |
25 | /// An angle in radians |
26 | #[derive (Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)] |
27 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
28 | pub struct Angle<T> { |
29 | pub radians: T, |
30 | } |
31 | |
32 | #[cfg (feature = "bytemuck" )] |
33 | unsafe impl<T: Zeroable> Zeroable for Angle<T> {} |
34 | |
35 | #[cfg (feature = "bytemuck" )] |
36 | unsafe impl<T: Pod> Pod for Angle<T> {} |
37 | |
38 | #[cfg (feature = "arbitrary" )] |
39 | impl<'a, T> arbitrary::Arbitrary<'a> for Angle<T> |
40 | where |
41 | T: arbitrary::Arbitrary<'a>, |
42 | { |
43 | // This implementation could be derived, but the derive would require an `extern crate std`. |
44 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
45 | Ok(Angle { |
46 | radians: arbitrary::Arbitrary::arbitrary(u)?, |
47 | }) |
48 | } |
49 | |
50 | fn size_hint(depth: usize) -> (usize, Option<usize>) { |
51 | <T as arbitrary::Arbitrary>::size_hint(depth) |
52 | } |
53 | } |
54 | |
55 | impl<T> Angle<T> { |
56 | #[inline ] |
57 | pub fn radians(radians: T) -> Self { |
58 | Angle { radians } |
59 | } |
60 | |
61 | #[inline ] |
62 | pub fn get(self) -> T { |
63 | self.radians |
64 | } |
65 | } |
66 | |
67 | impl<T> Angle<T> |
68 | where |
69 | T: Trig, |
70 | { |
71 | #[inline ] |
72 | pub fn degrees(deg: T) -> Self { |
73 | Angle { |
74 | radians: T::degrees_to_radians(deg), |
75 | } |
76 | } |
77 | |
78 | #[inline ] |
79 | pub fn to_degrees(self) -> T { |
80 | T::radians_to_degrees(self.radians) |
81 | } |
82 | } |
83 | |
84 | impl<T> Angle<T> |
85 | where |
86 | T: Rem<Output = T> + Sub<Output = T> + Add<Output = T> + Zero + FloatConst + PartialOrd + Copy, |
87 | { |
88 | /// Returns this angle in the [0..2*PI[ range. |
89 | pub fn positive(&self) -> Self { |
90 | let two_pi: T = T::PI() + T::PI(); |
91 | let mut a: T = self.radians % two_pi; |
92 | if a < T::zero() { |
93 | a = a + two_pi; |
94 | } |
95 | Angle::radians(a) |
96 | } |
97 | |
98 | /// Returns this angle in the ]-PI..PI] range. |
99 | pub fn signed(&self) -> Self { |
100 | Angle::pi() - (Angle::pi() - *self).positive() |
101 | } |
102 | } |
103 | |
104 | impl<T> Angle<T> |
105 | where |
106 | T: Rem<Output = T> |
107 | + Mul<Output = T> |
108 | + Sub<Output = T> |
109 | + Add<Output = T> |
110 | + One |
111 | + FloatConst |
112 | + Copy, |
113 | { |
114 | /// Returns the shortest signed angle between two angles. |
115 | /// |
116 | /// Takes wrapping and signs into account. |
117 | pub fn angle_to(&self, to: Self) -> Self { |
118 | let two: T = T::one() + T::one(); |
119 | let max: T = T::PI() * two; |
120 | let d: T = (to.radians - self.radians) % max; |
121 | |
122 | Angle::radians(two * d % max - d) |
123 | } |
124 | |
125 | /// Linear interpolation between two angles, using the shortest path. |
126 | pub fn lerp(&self, other: Self, t: T) -> Self { |
127 | *self + self.angle_to(other) * t |
128 | } |
129 | } |
130 | |
131 | impl<T> Angle<T> |
132 | where |
133 | T: Float, |
134 | { |
135 | /// Returns `true` if the angle is a finite number. |
136 | #[inline ] |
137 | pub fn is_finite(self) -> bool { |
138 | self.radians.is_finite() |
139 | } |
140 | } |
141 | |
142 | impl<T> Angle<T> |
143 | where |
144 | T: Real, |
145 | { |
146 | /// Returns `(sin(self), cos(self))`. |
147 | pub fn sin_cos(self) -> (T, T) { |
148 | self.radians.sin_cos() |
149 | } |
150 | } |
151 | |
152 | impl<T> Angle<T> |
153 | where |
154 | T: Zero, |
155 | { |
156 | pub fn zero() -> Self { |
157 | Angle::radians(T::zero()) |
158 | } |
159 | } |
160 | |
161 | impl<T> Angle<T> |
162 | where |
163 | T: FloatConst + Add<Output = T>, |
164 | { |
165 | pub fn pi() -> Self { |
166 | Angle::radians(T::PI()) |
167 | } |
168 | |
169 | pub fn two_pi() -> Self { |
170 | Angle::radians(T::PI() + T::PI()) |
171 | } |
172 | |
173 | pub fn frac_pi_2() -> Self { |
174 | Angle::radians(T::FRAC_PI_2()) |
175 | } |
176 | |
177 | pub fn frac_pi_3() -> Self { |
178 | Angle::radians(T::FRAC_PI_3()) |
179 | } |
180 | |
181 | pub fn frac_pi_4() -> Self { |
182 | Angle::radians(T::FRAC_PI_4()) |
183 | } |
184 | } |
185 | |
186 | impl<T> Angle<T> |
187 | where |
188 | T: NumCast + Copy, |
189 | { |
190 | /// Cast from one numeric representation to another. |
191 | #[inline ] |
192 | pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> { |
193 | self.try_cast().unwrap() |
194 | } |
195 | |
196 | /// Fallible cast from one numeric representation to another. |
197 | pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> { |
198 | NumCast::from(self.radians).map(|radians| Angle { radians }) |
199 | } |
200 | |
201 | // Convenience functions for common casts. |
202 | |
203 | /// Cast angle to `f32`. |
204 | #[inline ] |
205 | pub fn to_f32(&self) -> Angle<f32> { |
206 | self.cast() |
207 | } |
208 | |
209 | /// Cast angle `f64`. |
210 | #[inline ] |
211 | pub fn to_f64(&self) -> Angle<f64> { |
212 | self.cast() |
213 | } |
214 | } |
215 | |
216 | impl<T: Add<T, Output = T>> Add for Angle<T> { |
217 | type Output = Self; |
218 | fn add(self, other: Self) -> Self { |
219 | Self::radians(self.radians + other.radians) |
220 | } |
221 | } |
222 | |
223 | impl<T: Copy + Add<T, Output = T>> Add<&Self> for Angle<T> { |
224 | type Output = Self; |
225 | fn add(self, other: &Self) -> Self { |
226 | Self::radians(self.radians + other.radians) |
227 | } |
228 | } |
229 | |
230 | impl<T: Add + Zero> Sum for Angle<T> { |
231 | fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { |
232 | iter.fold(Self::zero(), f:Add::add) |
233 | } |
234 | } |
235 | |
236 | impl<'a, T: 'a + Add + Copy + Zero> Sum<&'a Self> for Angle<T> { |
237 | fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self { |
238 | iter.fold(Self::zero(), f:Add::add) |
239 | } |
240 | } |
241 | |
242 | impl<T: AddAssign<T>> AddAssign for Angle<T> { |
243 | fn add_assign(&mut self, other: Angle<T>) { |
244 | self.radians += other.radians; |
245 | } |
246 | } |
247 | |
248 | impl<T: Sub<T, Output = T>> Sub<Angle<T>> for Angle<T> { |
249 | type Output = Angle<T>; |
250 | fn sub(self, other: Angle<T>) -> <Self as Sub>::Output { |
251 | Angle::radians(self.radians - other.radians) |
252 | } |
253 | } |
254 | |
255 | impl<T: SubAssign<T>> SubAssign for Angle<T> { |
256 | fn sub_assign(&mut self, other: Angle<T>) { |
257 | self.radians -= other.radians; |
258 | } |
259 | } |
260 | |
261 | impl<T: Div<T, Output = T>> Div<Angle<T>> for Angle<T> { |
262 | type Output = T; |
263 | #[inline ] |
264 | fn div(self, other: Angle<T>) -> T { |
265 | self.radians / other.radians |
266 | } |
267 | } |
268 | |
269 | impl<T: Div<T, Output = T>> Div<T> for Angle<T> { |
270 | type Output = Angle<T>; |
271 | #[inline ] |
272 | fn div(self, factor: T) -> Angle<T> { |
273 | Angle::radians(self.radians / factor) |
274 | } |
275 | } |
276 | |
277 | impl<T: DivAssign<T>> DivAssign<T> for Angle<T> { |
278 | fn div_assign(&mut self, factor: T) { |
279 | self.radians /= factor; |
280 | } |
281 | } |
282 | |
283 | impl<T: Mul<T, Output = T>> Mul<T> for Angle<T> { |
284 | type Output = Angle<T>; |
285 | #[inline ] |
286 | fn mul(self, factor: T) -> Angle<T> { |
287 | Angle::radians(self.radians * factor) |
288 | } |
289 | } |
290 | |
291 | impl<T: MulAssign<T>> MulAssign<T> for Angle<T> { |
292 | fn mul_assign(&mut self, factor: T) { |
293 | self.radians *= factor; |
294 | } |
295 | } |
296 | |
297 | impl<T: Neg<Output = T>> Neg for Angle<T> { |
298 | type Output = Self; |
299 | fn neg(self) -> Self { |
300 | Angle::radians(-self.radians) |
301 | } |
302 | } |
303 | |
304 | impl<T: ApproxEq<T>> ApproxEq<T> for Angle<T> { |
305 | #[inline ] |
306 | fn approx_epsilon() -> T { |
307 | T::approx_epsilon() |
308 | } |
309 | |
310 | #[inline ] |
311 | fn approx_eq_eps(&self, other: &Angle<T>, approx_epsilon: &T) -> bool { |
312 | self.radians.approx_eq_eps(&other.radians, approx_epsilon) |
313 | } |
314 | } |
315 | |
316 | #[test ] |
317 | fn wrap_angles() { |
318 | use core::f32::consts::{FRAC_PI_2, PI}; |
319 | |
320 | assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero())); |
321 | assert!(Angle::radians(FRAC_PI_2) |
322 | .positive() |
323 | .approx_eq(&Angle::frac_pi_2())); |
324 | assert!(Angle::radians(-FRAC_PI_2) |
325 | .positive() |
326 | .approx_eq(&Angle::radians(3.0 * FRAC_PI_2))); |
327 | assert!(Angle::radians(3.0 * FRAC_PI_2) |
328 | .positive() |
329 | .approx_eq(&Angle::radians(3.0 * FRAC_PI_2))); |
330 | assert!(Angle::radians(5.0 * FRAC_PI_2) |
331 | .positive() |
332 | .approx_eq(&Angle::frac_pi_2())); |
333 | assert!(Angle::radians(2.0 * PI) |
334 | .positive() |
335 | .approx_eq(&Angle::zero())); |
336 | assert!(Angle::radians(-2.0 * PI) |
337 | .positive() |
338 | .approx_eq(&Angle::zero())); |
339 | assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi())); |
340 | assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi())); |
341 | |
342 | assert!(Angle::radians(FRAC_PI_2) |
343 | .signed() |
344 | .approx_eq(&Angle::frac_pi_2())); |
345 | assert!(Angle::radians(3.0 * FRAC_PI_2) |
346 | .signed() |
347 | .approx_eq(&-Angle::frac_pi_2())); |
348 | assert!(Angle::radians(5.0 * FRAC_PI_2) |
349 | .signed() |
350 | .approx_eq(&Angle::frac_pi_2())); |
351 | assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero())); |
352 | assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero())); |
353 | assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi())); |
354 | assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi())); |
355 | } |
356 | |
357 | #[test ] |
358 | fn lerp() { |
359 | type A = Angle<f32>; |
360 | |
361 | let a = A::radians(1.0); |
362 | let b = A::radians(2.0); |
363 | assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25))); |
364 | assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5))); |
365 | assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75))); |
366 | assert!(a |
367 | .lerp(b + A::two_pi(), 0.75) |
368 | .approx_eq(&Angle::radians(1.75))); |
369 | assert!(a |
370 | .lerp(b - A::two_pi(), 0.75) |
371 | .approx_eq(&Angle::radians(1.75))); |
372 | assert!(a |
373 | .lerp(b + A::two_pi() * 5.0, 0.75) |
374 | .approx_eq(&Angle::radians(1.75))); |
375 | } |
376 | |
377 | #[test ] |
378 | fn sum() { |
379 | type A = Angle<f32>; |
380 | let angles = [A::radians(1.0), A::radians(2.0), A::radians(3.0)]; |
381 | let sum = A::radians(6.0); |
382 | assert_eq!(angles.iter().sum::<A>(), sum); |
383 | } |
384 | |