zng_unit/
angle.rs

1use super::{EQ_EPSILON, EQ_EPSILON_100, Factor, about_eq};
2
3use std::{
4    f32::consts::{PI, TAU},
5    fmt, ops,
6};
7
8/// Angle in radians.
9///
10/// See [`AngleUnits`] for more details.
11///
12/// # Equality
13///
14/// Equality is determined using [`about_eq`] with `0.00001` epsilon.
15#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
16#[serde(transparent)]
17pub struct AngleRadian(pub f32);
18impl ops::Add for AngleRadian {
19    type Output = Self;
20
21    fn add(self, rhs: Self) -> Self::Output {
22        Self(self.0 + rhs.0)
23    }
24}
25impl ops::AddAssign for AngleRadian {
26    fn add_assign(&mut self, rhs: Self) {
27        self.0 += rhs.0;
28    }
29}
30impl ops::Sub for AngleRadian {
31    type Output = Self;
32
33    fn sub(self, rhs: Self) -> Self::Output {
34        Self(self.0 - rhs.0)
35    }
36}
37impl ops::SubAssign for AngleRadian {
38    fn sub_assign(&mut self, rhs: Self) {
39        self.0 -= rhs.0;
40    }
41}
42impl ops::Neg for AngleRadian {
43    type Output = Self;
44
45    fn neg(self) -> Self::Output {
46        Self(-self.0)
47    }
48}
49impl AngleRadian {
50    /// Radians in `[0.0 ..= TAU]`.
51    pub fn modulo(self) -> Self {
52        AngleRadian(self.0.rem_euclid(TAU))
53    }
54
55    /// Linear interpolation.
56    pub fn lerp(self, to: Self, factor: Factor) -> Self {
57        Self(lerp(self.0, to.0, factor))
58    }
59
60    /// Spherical linear interpolation.
61    ///
62    /// Always uses the shortest path from `self` to `to`.
63    ///
64    /// The [`lerp`] linear interpolation always covers the numeric range between angles, so a transition from 358º to 1º
65    /// iterates over almost a full counterclockwise turn to reach the final value, `slerp` simply goes from 358º to 361º modulo
66    /// normalized.
67    ///
68    /// [`lerp`]: Self::lerp
69    pub fn slerp(self, to: Self, factor: Factor) -> Self {
70        Self(slerp(self.0, to.0, TAU, factor))
71    }
72}
73
74impl PartialEq for AngleRadian {
75    fn eq(&self, other: &Self) -> bool {
76        about_eq(self.0, other.0, EQ_EPSILON)
77    }
78}
79
80impl From<AngleGradian> for AngleRadian {
81    fn from(grad: AngleGradian) -> Self {
82        AngleRadian(grad.0 * PI / 200.0)
83    }
84}
85impl From<AngleDegree> for AngleRadian {
86    fn from(deg: AngleDegree) -> Self {
87        AngleRadian(deg.0.to_radians())
88    }
89}
90impl From<AngleTurn> for AngleRadian {
91    fn from(turn: AngleTurn) -> Self {
92        AngleRadian(turn.0 * TAU)
93    }
94}
95impl From<AngleRadian> for euclid::Angle<f32> {
96    fn from(rad: AngleRadian) -> Self {
97        euclid::Angle::radians(rad.0)
98    }
99}
100
101impl fmt::Debug for AngleRadian {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        if f.alternate() {
104            f.debug_tuple("AngleRadian").field(&self.0).finish()
105        } else {
106            write!(f, "{}.rad()", self.0)
107        }
108    }
109}
110impl fmt::Display for AngleRadian {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        write!(f, "{} rad", self.0)
113    }
114}
115
116/// Angle in gradians.
117///
118/// See [`AngleUnits`] for more details.
119///
120/// # Equality
121///
122/// Equality is determined using [`about_eq`] with `0.001` epsilon.
123#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
124#[serde(transparent)]
125pub struct AngleGradian(pub f32);
126impl AngleGradian {
127    /// Gradians in `[0.0 ..= 400.0]`.
128    pub fn modulo(self) -> Self {
129        AngleGradian(self.0.rem_euclid(400.0))
130    }
131
132    /// Linear interpolation.
133    pub fn lerp(self, to: Self, factor: Factor) -> Self {
134        Self(lerp(self.0, to.0, factor))
135    }
136
137    /// Spherical linear interpolation.
138    ///
139    /// Always uses the shortest path from `self` to `to`.
140    ///
141    /// The [`lerp`] linear interpolation always covers the numeric range between angles, so a transition from 358º to 1º
142    /// iterates over almost a full counterclockwise turn to reach the final value, `slerp` simply goes from 358º to 361º modulo
143    /// normalized.
144    ///
145    /// [`lerp`]: Self::lerp
146    pub fn slerp(self, to: Self, factor: Factor) -> Self {
147        Self(slerp(self.0, to.0, 400.0, factor))
148    }
149}
150impl ops::Add for AngleGradian {
151    type Output = Self;
152
153    fn add(self, rhs: Self) -> Self::Output {
154        Self(self.0 + rhs.0)
155    }
156}
157impl ops::AddAssign for AngleGradian {
158    fn add_assign(&mut self, rhs: Self) {
159        self.0 += rhs.0;
160    }
161}
162impl ops::Sub for AngleGradian {
163    type Output = Self;
164
165    fn sub(self, rhs: Self) -> Self::Output {
166        Self(self.0 - rhs.0)
167    }
168}
169impl ops::SubAssign for AngleGradian {
170    fn sub_assign(&mut self, rhs: Self) {
171        self.0 -= rhs.0;
172    }
173}
174impl ops::Neg for AngleGradian {
175    type Output = Self;
176
177    fn neg(self) -> Self::Output {
178        Self(-self.0)
179    }
180}
181
182impl PartialEq for AngleGradian {
183    fn eq(&self, other: &Self) -> bool {
184        about_eq(self.0, other.0, EQ_EPSILON_100)
185    }
186}
187impl From<AngleRadian> for AngleGradian {
188    fn from(rad: AngleRadian) -> Self {
189        AngleGradian(rad.0 * 200.0 / PI)
190    }
191}
192impl From<AngleDegree> for AngleGradian {
193    fn from(deg: AngleDegree) -> Self {
194        AngleGradian(deg.0 * 10.0 / 9.0)
195    }
196}
197impl From<AngleTurn> for AngleGradian {
198    fn from(turn: AngleTurn) -> Self {
199        AngleGradian(turn.0 * 400.0)
200    }
201}
202impl fmt::Debug for AngleGradian {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        if f.alternate() {
205            f.debug_tuple("AngleGradian").field(&self.0).finish()
206        } else {
207            write!(f, "{}.grad()", self.0)
208        }
209    }
210}
211impl fmt::Display for AngleGradian {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        write!(f, "{} gon", self.0)
214    }
215}
216
217/// Angle in degrees.
218///
219/// See [`AngleUnits`] for more details.
220///
221/// # Equality
222///
223/// Equality is determined using [`about_eq`] with `0.001` epsilon.
224#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
225#[serde(transparent)]
226pub struct AngleDegree(pub f32);
227impl AngleDegree {
228    /// Degrees in `[0.0 ..= 360.0]`.
229    pub fn modulo(self) -> Self {
230        AngleDegree(self.0.rem_euclid(360.0))
231    }
232
233    /// Linear interpolation.
234    pub fn lerp(self, to: Self, factor: Factor) -> Self {
235        Self(lerp(self.0, to.0, factor))
236    }
237
238    /// Spherical linear interpolation.
239    ///
240    /// Always uses the shortest path from `self` to `to`.
241    ///
242    /// The [`lerp`] linear interpolation always covers the numeric range between angles, so a transition from 358º to 1º
243    /// iterates over almost a full counterclockwise turn to reach the final value, `slerp` simply goes from 358º to 361º modulo
244    /// normalized.
245    ///
246    /// [`lerp`]: Self::lerp
247    pub fn slerp(self, to: Self, factor: Factor) -> Self {
248        Self(slerp(self.0, to.0, 360.0, factor))
249    }
250}
251impl ops::Add for AngleDegree {
252    type Output = Self;
253
254    fn add(self, rhs: Self) -> Self::Output {
255        Self(self.0 + rhs.0)
256    }
257}
258impl ops::AddAssign for AngleDegree {
259    fn add_assign(&mut self, rhs: Self) {
260        self.0 += rhs.0;
261    }
262}
263impl ops::Sub for AngleDegree {
264    type Output = Self;
265
266    fn sub(self, rhs: Self) -> Self::Output {
267        Self(self.0 - rhs.0)
268    }
269}
270impl ops::SubAssign for AngleDegree {
271    fn sub_assign(&mut self, rhs: Self) {
272        self.0 -= rhs.0;
273    }
274}
275impl ops::Neg for AngleDegree {
276    type Output = Self;
277
278    fn neg(self) -> Self::Output {
279        Self(-self.0)
280    }
281}
282
283impl PartialEq for AngleDegree {
284    fn eq(&self, other: &Self) -> bool {
285        about_eq(self.0, other.0, EQ_EPSILON_100)
286    }
287}
288impl From<AngleRadian> for AngleDegree {
289    fn from(rad: AngleRadian) -> Self {
290        AngleDegree(rad.0.to_degrees())
291    }
292}
293impl From<AngleGradian> for AngleDegree {
294    fn from(grad: AngleGradian) -> Self {
295        AngleDegree(grad.0 * 9.0 / 10.0)
296    }
297}
298impl From<AngleTurn> for AngleDegree {
299    fn from(turn: AngleTurn) -> Self {
300        AngleDegree(turn.0 * 360.0)
301    }
302}
303impl fmt::Debug for AngleDegree {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        if f.alternate() {
306            f.debug_tuple("AngleDegree").field(&self.0).finish()
307        } else {
308            write!(f, "{}.deg()", self.0)
309        }
310    }
311}
312impl fmt::Display for AngleDegree {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "{}º", self.0)
315    }
316}
317
318/// Angle in turns (complete rotations).
319///
320/// See [`AngleUnits`] for more details.
321///
322/// # Equality
323///
324/// Equality is determined using [`about_eq`] with `0.00001` epsilon.
325#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
326#[serde(transparent)]
327pub struct AngleTurn(pub f32);
328impl AngleTurn {
329    /// Turns in `[0.0 ..= 1.0]`.
330    pub fn modulo(self) -> Self {
331        AngleTurn(self.0.rem_euclid(1.0))
332    }
333
334    /// Linear interpolation.
335    pub fn lerp(self, to: Self, factor: Factor) -> Self {
336        Self(lerp(self.0, to.0, factor))
337    }
338
339    /// Spherical linear interpolation.
340    ///
341    /// Always uses the shortest path from `self` to `to`.
342    ///
343    /// The [`lerp`] linear interpolation always covers the numeric range between angles, so a transition from 358º to 1º
344    /// iterates over almost a full counterclockwise turn to reach the final value, `slerp` simply goes from 358º to 361º modulo
345    /// normalized.
346    ///
347    /// [`lerp`]: Self::lerp
348    pub fn slerp(self, to: Self, factor: Factor) -> Self {
349        Self(slerp(self.0, to.0, 1.0, factor))
350    }
351}
352impl ops::Add for AngleTurn {
353    type Output = Self;
354
355    fn add(self, rhs: Self) -> Self::Output {
356        Self(self.0 + rhs.0)
357    }
358}
359impl ops::AddAssign for AngleTurn {
360    fn add_assign(&mut self, rhs: Self) {
361        self.0 += rhs.0;
362    }
363}
364impl ops::Sub for AngleTurn {
365    type Output = Self;
366
367    fn sub(self, rhs: Self) -> Self::Output {
368        Self(self.0 - rhs.0)
369    }
370}
371impl ops::SubAssign for AngleTurn {
372    fn sub_assign(&mut self, rhs: Self) {
373        self.0 -= rhs.0;
374    }
375}
376impl ops::Neg for AngleTurn {
377    type Output = Self;
378
379    fn neg(self) -> Self::Output {
380        Self(-self.0)
381    }
382}
383
384impl fmt::Debug for AngleTurn {
385    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386        if f.alternate() {
387            f.debug_tuple("AngleTurn").field(&self.0).finish()
388        } else {
389            write!(f, "{}.turn()", self.0)
390        }
391    }
392}
393impl fmt::Display for AngleTurn {
394    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395        if (self.0 - 1.0).abs() < 0.0001 {
396            write!(f, "1 turn")
397        } else {
398            write!(f, "{} turns", self.0)
399        }
400    }
401}
402impl PartialEq for AngleTurn {
403    fn eq(&self, other: &Self) -> bool {
404        about_eq(self.0, other.0, EQ_EPSILON)
405    }
406}
407
408impl From<AngleRadian> for AngleTurn {
409    fn from(rad: AngleRadian) -> Self {
410        AngleTurn(rad.0 / TAU)
411    }
412}
413impl From<AngleGradian> for AngleTurn {
414    fn from(grad: AngleGradian) -> Self {
415        AngleTurn(grad.0 / 400.0)
416    }
417}
418impl From<AngleDegree> for AngleTurn {
419    fn from(deg: AngleDegree) -> Self {
420        AngleTurn(deg.0 / 360.0)
421    }
422}
423
424/// Extension methods for initializing angle units.
425///
426/// This trait is implemented for [`f32`] and [`u32`] allowing initialization of angle unit types using the `<number>.<unit>()` syntax.
427///
428/// # Examples
429///
430/// ```
431/// # use zng_unit::*;
432/// let radians = 6.28318.rad();
433/// let gradians = 400.grad();
434/// let degrees = 360.deg();
435/// let turns = 1.turn();
436/// ```
437pub trait AngleUnits {
438    /// Radians
439    fn rad(self) -> AngleRadian;
440    /// Gradians
441    fn grad(self) -> AngleGradian;
442    /// Degrees
443    fn deg(self) -> AngleDegree;
444    /// Turns
445    fn turn(self) -> AngleTurn;
446}
447impl AngleUnits for f32 {
448    fn rad(self) -> AngleRadian {
449        AngleRadian(self)
450    }
451
452    fn grad(self) -> AngleGradian {
453        AngleGradian(self)
454    }
455
456    fn deg(self) -> AngleDegree {
457        AngleDegree(self)
458    }
459
460    fn turn(self) -> AngleTurn {
461        AngleTurn(self)
462    }
463}
464impl AngleUnits for i32 {
465    fn rad(self) -> AngleRadian {
466        AngleRadian(self as f32)
467    }
468
469    fn grad(self) -> AngleGradian {
470        AngleGradian(self as f32)
471    }
472
473    fn deg(self) -> AngleDegree {
474        AngleDegree(self as f32)
475    }
476
477    fn turn(self) -> AngleTurn {
478        AngleTurn(self as f32)
479    }
480}
481
482fn lerp(from: f32, to: f32, factor: Factor) -> f32 {
483    from + (to - from) * factor.0
484}
485
486fn slerp(from: f32, to: f32, turn: f32, factor: Factor) -> f32 {
487    let angle_to = {
488        let d = (to - from) % turn;
489        2.0 * d % turn - d
490    };
491    from + angle_to * factor.0
492}