1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::f64::consts::{PI, TAU};
5
6#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Angle {
9 radians: f64,
10}
11
12impl Angle {
13 pub const ZERO: Self = Self { radians: 0.0 };
15 pub const HALF_TURN: Self = Self { radians: PI };
17 pub const FULL_TURN: Self = Self { radians: TAU };
19
20 #[must_use]
22 pub const fn from_radians(radians: f64) -> Self {
23 Self { radians }
24 }
25
26 #[must_use]
28 pub fn from_degrees(degrees: f64) -> Self {
29 Self::from_radians(degrees.to_radians())
30 }
31
32 #[must_use]
34 pub const fn radians(self) -> f64 {
35 self.radians
36 }
37
38 #[must_use]
40 pub fn degrees(self) -> f64 {
41 self.radians.to_degrees()
42 }
43
44 #[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}