Skip to main content

use_angle/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::f64::consts::{PI, TAU};
5
6/// An angle stored in radians.
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Angle {
9    radians: f64,
10}
11
12impl Angle {
13    /// A zero angle.
14    pub const ZERO: Self = Self { radians: 0.0 };
15    /// A half turn.
16    pub const HALF_TURN: Self = Self { radians: PI };
17    /// A full turn.
18    pub const FULL_TURN: Self = Self { radians: TAU };
19
20    /// Creates an angle from radians.
21    #[must_use]
22    pub const fn from_radians(radians: f64) -> Self {
23        Self { radians }
24    }
25
26    /// Creates an angle from degrees.
27    #[must_use]
28    pub fn from_degrees(degrees: f64) -> Self {
29        Self::from_radians(degrees.to_radians())
30    }
31
32    /// Returns the angle in radians.
33    #[must_use]
34    pub const fn radians(self) -> f64 {
35        self.radians
36    }
37
38    /// Returns the angle in degrees.
39    #[must_use]
40    pub fn degrees(self) -> f64 {
41        self.radians.to_degrees()
42    }
43
44    /// Returns the equivalent angle in `[0, 2pi)`.
45    #[must_use]
46    pub fn normalized(self) -> Self {
47        Self::from_radians(self.radians.rem_euclid(TAU))
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use core::f64::consts::PI;
54
55    use super::Angle;
56
57    #[test]
58    fn converts_degrees_and_radians() {
59        assert_eq!(Angle::from_degrees(180.0).radians(), PI);
60        assert_eq!(Angle::from_radians(PI).degrees(), 180.0);
61        assert_eq!(Angle::ZERO.radians(), 0.0);
62        assert_eq!(Angle::HALF_TURN.radians(), PI);
63    }
64
65    #[test]
66    fn normalizes_angles() {
67        assert_eq!(Angle::from_degrees(450.0).normalized().degrees(), 90.0);
68    }
69}