evian_math/
angle.rs

1use core::{
2    f64::{
3        self,
4        consts::{FRAC_PI_2, PI, TAU},
5    },
6    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
7};
8use vexide::{devices::position::Position, float::Float};
9
10/// Angular position.
11#[derive(Default, Debug, Clone, Copy, PartialEq)]
12pub struct Angle(f64);
13
14impl Angle {
15    // MARK: Constants
16
17    /// Angle representing zero rotation.
18    pub const ZERO: Self = Self(0.0);
19
20    /// Angle representing a quarter turn around a full circle.
21    pub const QUARTER_TURN: Self = Self(FRAC_PI_2);
22
23    /// Angle representing a half turn around a full circle.
24    pub const HALF_TURN: Self = Self(PI);
25
26    /// Angle representing a full turn around a circle.
27    pub const FULL_TURN: Self = Self(TAU);
28
29    /// Smallest value that can be represented by the `Angle` type.
30    ///
31    /// Equivalent to [`f64::MIN`] radians.
32    pub const MIN: Self = Self(f64::MIN);
33
34    /// Largest value that can be represented by the `Angle` type.
35    ///
36    /// Equivalent to [`f64::MAX`] radians.
37    pub const MAX: Self = Self(f64::MAX);
38
39    /// [Machine epsilon] value for `Angle`.
40    ///
41    /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
42    pub const EPSILON: Self = Self(f64::EPSILON);
43
44    // MARK: Creation
45
46    /// Creates a new `Angle` from a value in radians.
47    #[inline]
48    #[must_use]
49    pub const fn from_radians(radians: f64) -> Self {
50        Self(radians)
51    }
52
53    /// Creates a new `Angle` from a value in gradians.
54    #[must_use]
55    pub const fn from_gradians(gradians: f64) -> Self {
56        Self(gradians * (PI / 200.0))
57    }
58
59    /// Creates a new `Angle` from a value in degrees.
60    #[inline]
61    #[must_use]
62    pub const fn from_degrees(degrees: f64) -> Self {
63        Self(degrees.to_radians())
64    }
65
66    /// Creates a new `Angle` from a value in turns (revolutions).
67    #[inline]
68    #[must_use]
69    pub const fn from_turns(turns: f64) -> Self {
70        Self(turns * TAU)
71    }
72
73    // MARK: Math
74
75    /// Computes the arcsine of a number. Return value is in the range
76    /// [-pi/2, pi/2] or NaN if the angle is outside the range [-1, 1].
77    #[inline]
78    #[must_use]
79    pub fn asin(y: f64) -> Self {
80        Self(y.asin())
81    }
82
83    /// Computes the arccosine of a number. Return value is in the range
84    /// [0, pi] or NaN if the number is outside the range [-1, 1].
85    #[inline]
86    #[must_use]
87    pub fn acos(x: f64) -> Self {
88        Self(x.acos())
89    }
90
91    /// Computes the arctangent of an angle. Return value is in radians in the
92    /// range [-pi/2, pi/2];
93    #[inline]
94    #[must_use]
95    pub fn atan(tan: f64) -> Self {
96        Self(tan.atan())
97    }
98
99    /// Computes the four quadrant arctangent angle of `y` and `x`.
100    #[inline]
101    #[must_use]
102    pub fn atan2(y: f64, x: f64) -> Self {
103        Self(y.atan2(x))
104    }
105
106    /// Returns this angle's value in degrees.
107    #[inline]
108    #[must_use]
109    pub const fn as_degrees(&self) -> f64 {
110        self.0.to_degrees()
111    }
112
113    /// Returns this angle's value in turns (revolution).
114    #[inline]
115    #[must_use]
116    pub fn as_turns(&self) -> f64 {
117        self.0 / TAU
118    }
119
120    /// Returns this angle's value in radians.
121    #[inline]
122    #[must_use]
123    pub const fn as_radians(&self) -> f64 {
124        self.0
125    }
126
127    /// Returns this angle's value in gradians.
128    #[inline]
129    #[must_use]
130    pub const fn as_gradians(&self) -> f64 {
131        self.0 * (200.0 / PI)
132    }
133
134    /// Normalizes an angle to the bounds [-pi, pi].
135    #[inline]
136    #[must_use]
137    pub fn wrapped(&self) -> Self {
138        Self((-self.0 + PI).rem_euclid(TAU) - PI)
139    }
140
141    /// Normalizes an angle to the bounds [0, 2pi].
142    #[inline]
143    #[must_use]
144    pub fn wrapped_positive(&self) -> Self {
145        Self(self.0.rem_euclid(TAU))
146    }
147
148    /// Computes the absolute value of `self`.
149    #[inline]
150    #[must_use = "this returns the result of the operation, without modifying the original"]
151    pub const fn abs(self) -> Self {
152        Self(self.0.abs())
153    }
154
155    /// Returns a number that represents the sign of `self`.
156    ///
157    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
158    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
159    /// - NaN if the number is NaN
160    #[inline]
161    #[must_use = "this returns the result of the operation, without modifying the original"]
162    pub const fn signum(self) -> f64 {
163        self.0.signum()
164    }
165
166    /// Returns an angle composed of the magnitude of `self` and the sign of
167    /// `sign`.
168    ///
169    /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`.
170    /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is
171    /// returned.
172    ///
173    /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note
174    /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust
175    /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the
176    /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable
177    /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more
178    /// info.
179    #[inline]
180    #[must_use = "this returns the result of the operation, without modifying the original"]
181    pub const fn copysign(self, sign: Self) -> Self {
182        Self(self.0.copysign(sign.0))
183    }
184
185    /// Fused multiply-add. Computes `(self * a) + b` with only one rounding
186    /// error, yielding a more accurate result than an unfused multiply-add.
187    ///
188    /// Using `mul_add` *may* be more performant than an unfused multiply-add if
189    /// the target architecture has a dedicated `fma` CPU instruction. However,
190    /// this is not always true, and will be heavily dependant on designing
191    /// algorithms with specific target hardware in mind.
192    #[inline]
193    #[must_use = "this returns the result of the operation, without modifying the original"]
194    pub fn mul_add(self, a: Self, b: Self) -> Self {
195        Self(self.0.mul_add(a.0, b.0))
196    }
197
198    /// Calculates Euclidean division.
199    #[inline]
200    #[must_use = "this returns the result of the operation, without modifying the original"]
201    pub fn div_euclid(self, rhs: Self) -> Self {
202        Self(self.0.div_euclid(rhs.0))
203    }
204
205    /// The positive difference of two numbers.
206    ///
207    /// * If `self <= other`: `0.0`
208    /// * Else: `self - other`
209    #[inline]
210    #[must_use = "this returns the result of the operation, without modifying the original"]
211    pub fn abs_sub(self, other: Self) -> Self {
212        #[allow(deprecated)]
213        Self(self.0.abs_sub(other.0))
214    }
215
216    /// Computes the sine of an angle.
217    #[inline]
218    #[must_use = "this returns the result of the operation, without modifying the original"]
219    pub fn sin(self) -> f64 {
220        self.0.sin()
221    }
222
223    /// Computes the cosine of an angle.
224    #[inline]
225    #[must_use = "this returns the result of the operation, without modifying the original"]
226    pub fn cos(self) -> f64 {
227        self.0.cos()
228    }
229
230    /// Computes the tangent of an angle.
231    #[inline]
232    #[must_use = "this returns the result of the operation, without modifying the original"]
233    pub fn tan(self) -> f64 {
234        self.0.tan()
235    }
236
237    /// Simultaneously computes the sine and cosine of the number, `x`. Returns
238    /// `(sin(x), cos(x))`.
239    #[inline]
240    #[must_use = "this returns the result of the operation, without modifying the original"]
241    pub fn sin_cos(self) -> (f64, f64) {
242        self.0.sin_cos()
243    }
244}
245
246// MARK: Conversion
247
248impl From<Position> for Angle {
249    fn from(value: Position) -> Self {
250        Self::from_degrees(value.as_degrees())
251    }
252}
253
254// MARK: Operators
255
256impl Add<Angle> for Angle {
257    type Output = Self;
258
259    #[inline]
260    fn add(self, rhs: Self) -> Self::Output {
261        Self(self.0 + rhs.0)
262    }
263}
264
265impl Sub<Angle> for Angle {
266    type Output = Self;
267
268    #[inline]
269    fn sub(self, rhs: Self) -> Self::Output {
270        Self(self.0 - rhs.0)
271    }
272}
273
274impl Mul<f64> for Angle {
275    type Output = Self;
276
277    #[inline]
278    fn mul(self, rhs: f64) -> Self::Output {
279        Self(self.0 * rhs)
280    }
281}
282
283impl Div<f64> for Angle {
284    type Output = Self;
285
286    #[inline]
287    fn div(self, rhs: f64) -> Self::Output {
288        Self(self.0 / rhs)
289    }
290}
291
292impl AddAssign<Angle> for Angle {
293    #[inline]
294    fn add_assign(&mut self, rhs: Self) {
295        self.0 += rhs.0;
296    }
297}
298
299impl SubAssign<Angle> for Angle {
300    #[inline]
301    fn sub_assign(&mut self, rhs: Self) {
302        self.0 -= rhs.0;
303    }
304}
305
306impl MulAssign<f64> for Angle {
307    #[inline]
308    fn mul_assign(&mut self, rhs: f64) {
309        self.0 *= rhs;
310    }
311}
312
313impl DivAssign<f64> for Angle {
314    #[inline]
315    fn div_assign(&mut self, rhs: f64) {
316        self.0 /= rhs;
317    }
318}
319
320impl Neg for Angle {
321    type Output = Self;
322
323    #[inline]
324    fn neg(self) -> Self::Output {
325        Self(-self.0)
326    }
327}
328
329// MARK: Extension Trait
330
331/// Extension trait for easily creating [`Angle`]s from floating-point
332/// number literals.
333pub trait IntoAngle {
334    /// Creates an [`Angle`] of `self` degrees.
335    fn deg(self) -> Angle;
336
337    /// Creates an [`Angle`] of `self` gradians.
338    fn grad(self) -> Angle;
339
340    /// Creates an [`Angle`] of `self` radians.
341    fn rad(self) -> Angle;
342
343    /// Creates an [`Angle`] of `self` turns (revolutions).
344    fn turns(self) -> Angle;
345}
346
347impl IntoAngle for f64 {
348    fn deg(self) -> Angle {
349        Angle::from_degrees(self)
350    }
351
352    fn rad(self) -> Angle {
353        Angle::from_radians(self)
354    }
355
356    fn grad(self) -> Angle {
357        Angle::from_gradians(self)
358    }
359
360    fn turns(self) -> Angle {
361        Angle::from_turns(self)
362    }
363}