tge/math/
angle.rs

1use super::Float;
2use std::ops::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign};
3use std::cmp::Ordering;
4
5#[derive(Debug, Copy, Clone)]
6pub enum Angle<F: Float = f32> {
7    Radians(F),
8    Degrees(F),
9}
10
11impl<F: Float> Angle<F> {
12    pub fn radians(value: F) -> Self {
13        Angle::Radians(value)
14    }
15
16    pub fn degrees(value: F) -> Self {
17        Angle::Degrees(value)
18    }
19
20    pub fn n_pi(n: F) -> Self {
21        Self::radians(n * F::pi())
22    }
23
24    pub fn zero() -> Self {
25        Self::radians(F::zero())
26    }
27
28    pub fn value(&self) -> F {
29        match self {
30            Self::Radians(value) => *value,
31            Self::Degrees(value) => *value,
32        }
33    }
34
35    pub fn set_value(&mut self, new_value: F) {
36        match self {
37            Self::Radians(value) => *value = new_value,
38            Self::Degrees(value) => *value = new_value,
39        }
40    }
41
42    pub fn radians_value(&self) -> F {
43        match self {
44            Self::Radians(value) => *value,
45            Self::Degrees(value) => value.to_radians(),
46        }
47    }
48
49    pub fn degrees_value(&self) -> F {
50        match self {
51            Self::Radians(value) => value.to_degrees(),
52            Self::Degrees(value) => *value,
53        }
54    }
55
56    pub fn to_degrees(&self) -> Self {
57        Angle::Degrees(self.degrees_value())
58    }
59
60    pub fn to_radians(&self) -> Self {
61        Angle::Radians(self.radians_value())
62    }
63}
64
65impl<F: Float> Add for Angle<F> {
66    type Output = Self;
67
68    fn add(self, other: Self) -> Self::Output {
69        match self {
70            Self::Radians(value) => Self::Radians(value + other.radians_value()),
71            Self::Degrees(value) => Self::Degrees(value + other.degrees_value()),
72        }
73    }
74}
75
76impl<F: Float> Sub for Angle<F> {
77    type Output = Self;
78
79    fn sub(self, other: Self) -> Self::Output {
80        match self {
81            Self::Radians(value) => Self::Radians(value - other.radians_value()),
82            Self::Degrees(value) => Self::Degrees(value - other.degrees_value()),
83        }
84    }
85}
86
87impl<F: Float> AddAssign for Angle<F> {
88    fn add_assign(&mut self, other: Self) {
89        match self {
90            Self::Radians(value) => *value += other.radians_value(),
91            Self::Degrees(value) => *value += other.degrees_value(),
92        }
93    }
94}
95
96impl<F: Float> SubAssign for Angle<F> {
97    fn sub_assign(&mut self, other: Self) {
98        match self {
99            Self::Radians(value) => *value -= other.radians_value(),
100            Self::Degrees(value) => *value -= other.degrees_value(),
101        }
102    }
103}
104
105impl<F: Float> Mul<F> for Angle<F> {
106    type Output = Self;
107
108    fn mul(self, rhs: F) -> Self::Output {
109        match self {
110            Self::Radians(value) => Self::Radians(value * rhs),
111            Self::Degrees(value) => Self::Degrees(value * rhs),
112        }
113    }
114}
115
116impl<F: Float> Div<F> for Angle<F> {
117    type Output = Self;
118
119    fn div(self, rhs: F) -> Self::Output {
120        match self {
121            Self::Radians(value) => Self::Radians(value / rhs),
122            Self::Degrees(value) => Self::Degrees(value / rhs),
123        }
124    }
125}
126
127impl<F: Float> MulAssign<F> for Angle<F> {
128    fn mul_assign(&mut self, rhs: F) {
129        match self {
130            Self::Radians(value) => *value *= rhs,
131            Self::Degrees(value) => *value *= rhs,
132        }
133    }
134}
135
136impl<F: Float> DivAssign<F> for Angle<F> {
137    fn div_assign(&mut self, rhs: F) {
138        match self {
139            Self::Radians(value) => *value /= rhs,
140            Self::Degrees(value) => *value /= rhs,
141        }
142    }
143}
144
145impl<F: Float> PartialEq for Angle<F> {
146    fn eq(&self, other: &Self) -> bool {
147        self.radians_value() == other.radians_value()
148    }
149}
150
151impl<F: Float> PartialOrd for Angle<F> {
152    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
153        self.radians_value().partial_cmp(&other.radians_value())
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::Angle;
160
161    #[test]
162    fn test_create() {
163        assert_eq!(Angle::<f32>::radians(std::f32::consts::PI * 2.0), Angle::<f32>::n_pi(2.0));
164        assert_eq!(Angle::<f32>::n_pi(1.0), Angle::<f32>::degrees(180.0));
165        assert_eq!(Angle::<f32>::n_pi(2.0).value(), std::f32::consts::PI * 2.0);
166        assert_eq!(Angle::<f32>::degrees(90.0).value(), 90.0f32);
167        assert_eq!(Angle::<f32>::zero(), Angle::<f32>::radians(0.0));
168        assert_eq!(Angle::<f32>::zero(), Angle::<f32>::degrees(0.0));
169        assert_eq!(Angle::<f32>::zero().value(), 0.0f32);
170    }
171
172    #[test]
173    fn test_convert() {
174        assert_eq!(Angle::<f32>::n_pi(1.0).to_degrees().value(), 180.0f32);
175        assert_eq!(Angle::<f32>::degrees(90.0).to_radians().value(), std::f32::consts::PI * 0.5);
176        assert_eq!(Angle::<f32>::n_pi(2.0).to_degrees().value(), Angle::<f32>::n_pi(2.0).degrees_value());
177        assert_eq!(Angle::<f32>::degrees(180.0).to_radians().value(), Angle::<f32>::degrees(180.0).radians_value());
178    }
179
180    #[test]
181    fn test_operator() {
182        assert_eq!(Angle::<f32>::n_pi(1.0) + Angle::<f32>::n_pi(1.0), Angle::<f32>::n_pi(2.0));
183        assert_eq!(Angle::<f32>::degrees(90.0) + Angle::<f32>::degrees(90.0), Angle::<f32>::degrees(180.0));
184        assert_eq!(Angle::<f32>::n_pi(1.0) + Angle::<f32>::degrees(180.0), Angle::<f32>::n_pi(2.0));
185        assert_eq!(Angle::<f32>::degrees(90.0) + Angle::<f32>::n_pi(0.5), Angle::<f32>::degrees(180.0));
186        assert_eq!(Angle::<f32>::n_pi(2.0) - Angle::<f32>::n_pi(1.0), Angle::<f32>::n_pi(1.0));
187        assert_eq!(Angle::<f32>::degrees(180.0) - Angle::<f32>::degrees(90.0), Angle::<f32>::degrees(90.0));
188        assert_eq!(Angle::<f32>::n_pi(2.0) - Angle::<f32>::degrees(180.0), Angle::<f32>::n_pi(1.0));
189        assert_eq!(Angle::<f32>::degrees(180.0) - Angle::<f32>::n_pi(0.5), Angle::<f32>::degrees(90.0));
190        assert_eq!(Angle::<f32>::n_pi(1.0) * 2.0, Angle::<f32>::n_pi(2.0));
191        assert_eq!(Angle::<f32>::degrees(90.0) * 2.0, Angle::<f32>::degrees(180.0));
192        assert_eq!(Angle::<f32>::n_pi(2.0) / 2.0, Angle::<f32>::n_pi(1.0));
193        assert_eq!(Angle::<f32>::degrees(90.0) / 2.0, Angle::<f32>::degrees(45.0));
194
195        let mut angle = Angle::<f32>::n_pi(2.0);
196        angle += Angle::<f32>::n_pi(2.0);
197        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
198        angle += Angle::<f32>::degrees(180.0);
199        assert_eq!(angle, Angle::<f32>::n_pi(5.0));
200        angle -= Angle::<f32>::n_pi(1.0);
201        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
202        angle -= Angle::<f32>::degrees(180.0);
203        assert_eq!(angle, Angle::<f32>::n_pi(3.0));
204
205        let mut angle = Angle::<f32>::degrees(180.0);
206        angle += Angle::<f32>::degrees(180.0);
207        assert_eq!(angle, Angle::<f32>::degrees(360.0));
208        angle += Angle::<f32>::n_pi(2.0);
209        assert_eq!(angle, Angle::<f32>::degrees(720.0));
210        angle -= Angle::<f32>::degrees(360.0);
211        assert_eq!(angle, Angle::<f32>::degrees(360.0));
212        angle -= Angle::<f32>::n_pi(1.0);
213        assert_eq!(angle, Angle::<f32>::degrees(180.0));
214
215        let mut angle = Angle::<f32>::n_pi(1.0);
216        angle *= 4.0;
217        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
218        angle /= 2.0;
219        assert_eq!(angle, Angle::<f32>::n_pi(2.0));
220
221        let mut angle = Angle::<f32>::degrees(180.0);
222        angle *= 4.0;
223        assert_eq!(angle, Angle::<f32>::degrees(720.0));
224        angle /= 2.0;
225        assert_eq!(angle, Angle::<f32>::degrees(360.0));
226    }
227
228    #[test]
229    fn test_compare() {
230        assert!(Angle::<f32>::n_pi(1.5) > Angle::<f32>::n_pi(0.5));
231        assert!(Angle::<f32>::n_pi(1.0) < Angle::<f32>::n_pi(2.0));
232        assert!(Angle::<f32>::degrees(90.0) > Angle::<f32>::degrees(45.0));
233        assert!(Angle::<f32>::degrees(30.0) < Angle::<f32>::degrees(60.0));
234        assert!(Angle::<f32>::n_pi(2.0) > Angle::<f32>::degrees(300.0));
235        assert!(Angle::<f32>::n_pi(0.5) < Angle::<f32>::degrees(135.0));
236    }
237}