| 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 | |