1use std::{f64::consts::PI, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}};
2
3use nuit_derive::ApproxEq;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Default, Clone, Copy, PartialEq, ApproxEq, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct Angle {
10 radians: f64,
11}
12
13impl Angle {
14 pub const ZERO: Self = Self::with_radians(0.0);
15 pub const QUARTER: Self = Self::with_radians(PI / 2.0);
16 pub const HALF: Self = Self::with_radians(PI);
17 pub const FULL: Self = Self::with_radians(2.0 * PI);
18
19 pub const fn with_radians(radians: f64) -> Self {
21 Self { radians }
22 }
23
24 pub const fn with_degrees(degrees: f64) -> Self {
26 Self::with_radians(degrees * PI / 180.0)
27 }
28
29 pub const fn with_fractional(fractional: f64) -> Self {
31 Self::with_radians(fractional * 2.0 * PI)
32 }
33
34 pub const fn radians(self) -> f64 {
36 self.radians
37 }
38
39 pub const fn degrees(self) -> f64 {
41 self.radians * 180.0 / PI
42 }
43
44 pub const fn fractional(self) -> f64 {
46 self.radians / (2.0 * PI)
47 }
48}
49
50impl Add for Angle {
51 type Output = Self;
52
53 fn add(self, rhs: Self) -> Self {
54 Self { radians: self.radians + rhs.radians }
55 }
56}
57
58impl Sub for Angle {
59 type Output = Self;
60
61 fn sub(self, rhs: Self) -> Self {
62 Self { radians: self.radians - rhs.radians }
63 }
64}
65
66impl Mul<f64> for Angle {
67 type Output = Self;
68
69 fn mul(self, rhs: f64) -> Self {
70 Self { radians: self.radians * rhs }
71 }
72}
73
74impl Div<f64> for Angle {
75 type Output = Self;
76
77 fn div(self, rhs: f64) -> Self {
78 Self { radians: self.radians / rhs }
79 }
80}
81
82impl AddAssign for Angle {
83 fn add_assign(&mut self, rhs: Self) {
84 self.radians += rhs.radians;
85 }
86}
87
88impl SubAssign for Angle {
89 fn sub_assign(&mut self, rhs: Self) {
90 self.radians -= rhs.radians;
91 }
92}
93
94impl MulAssign<f64> for Angle {
95 fn mul_assign(&mut self, rhs: f64) {
96 self.radians *= rhs;
97 }
98}
99
100impl DivAssign<f64> for Angle {
101 fn div_assign(&mut self, rhs: f64) {
102 self.radians /= rhs;
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use std::f64::consts::PI;
109
110 use crate::{assert_approx_eq, Angle};
111
112 #[test]
113 fn conversions() {
114 assert_approx_eq!(Angle::HALF.fractional(), 0.5);
115 assert_approx_eq!(Angle::HALF.degrees(), 180.0);
116 assert_approx_eq!(Angle::HALF.radians(), PI);
117
118 assert_approx_eq!(Angle::with_fractional(1.0), Angle::FULL);
119 assert_approx_eq!(Angle::with_degrees(360.0), Angle::FULL);
120 assert_approx_eq!(Angle::with_radians(2.0 * PI), Angle::FULL);
121 }
122}