irox_units/units/
angle.rs1use core::fmt::{Display, Formatter};
9
10use crate::units::{FromUnits, Unit};
11
12#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
15#[non_exhaustive]
16pub enum AngleUnits {
17 #[default]
20 Radians,
21
22 Degrees,
25
26 Minutes,
29
30 Seconds,
33
34 Revolutions,
37
38 Mils,
41}
42
43macro_rules! from_units_angle {
44 ($type:ident) => {
45 impl crate::units::FromUnits<$type> for AngleUnits {
46 fn from(&self, value: $type, units: Self) -> $type {
47 match self {
48 AngleUnits::Degrees => match units {
49 AngleUnits::Radians => value * RAD_2_DEG as $type,
50 AngleUnits::Degrees => value as $type,
51
52 AngleUnits::Minutes => value * RAD_2_DEG as $type * DEG_2_MIN as $type,
53 AngleUnits::Seconds => value * RAD_2_DEG as $type * DEG_2_SEC as $type,
54 AngleUnits::Revolutions => value / REV_2_RAD as $type,
55 AngleUnits::Mils => value * RAD_2_MIL as $type,
56 },
57 AngleUnits::Radians => match units {
58 AngleUnits::Degrees => value * DEG_2_RAD as $type,
59 AngleUnits::Radians => value as $type,
60
61 AngleUnits::Minutes => value * DEG_2_MIN as $type,
62 AngleUnits::Seconds => value * DEG_2_SEC as $type,
63 AngleUnits::Revolutions => value / REV_2_DEG as $type,
64 AngleUnits::Mils => value * DEG_2_MIL as $type,
65 },
66 _ => todo!(),
67 }
68 }
69 }
70 };
71}
72
73basic_unit!(Angle, AngleUnits, Degrees);
74from_units_angle!(f32);
75from_units_angle!(f64);
76
77impl Angle {
78 #[must_use]
79 pub const fn new_radians(value: f64) -> Angle {
80 Self::new(value, AngleUnits::Radians)
81 }
82
83 #[must_use]
84 pub const fn new_degrees(value: f64) -> Angle {
85 Self::new(value, AngleUnits::Degrees)
86 }
87
88 #[must_use]
89 pub fn new_dms(degrees: i16, minutes: u8, seconds: f64) -> Angle {
90 let mult: f64 = match degrees {
91 ..=0 => -1.0,
92 _ => 1.0,
93 };
94 let minutes: f64 = f64::from(minutes) * mult;
95 let seconds: f64 = seconds * mult;
96 let value = f64::from(degrees) + minutes / 60. + seconds / 3600.;
97 Self::new_degrees(value)
98 }
99
100 #[must_use]
101 #[allow(unused_imports)]
102 pub fn new_dm(degrees: i16, minutes: f64) -> Angle {
103 use irox_tools::f64::FloatExt;
104 let seconds = minutes.fract() * 60.;
105 Self::new_dms(degrees, minutes.trunc() as u8, seconds)
106 }
107
108 #[must_use]
109 pub fn as_degrees(&self) -> Angle {
110 self.as_unit(AngleUnits::Degrees)
111 }
112
113 #[must_use]
114 pub fn as_radians(&self) -> Angle {
115 self.as_unit(AngleUnits::Radians)
116 }
117
118 #[must_use]
119 pub fn as_dms(&self) -> (i16, u8, f64) {
120 let (deg, val) = self.as_deg_min();
121
122 let min = val as u8;
123 let sec = (val - min as f64) * 60.;
124 (deg, min, sec)
125 }
126
127 #[must_use]
128 #[allow(unused_imports)]
129 pub fn as_deg_min(&self) -> (i16, f64) {
130 use irox_tools::f64::FloatExt;
131 let val = self.as_degrees().value;
132 let sign = val.signum() as i16;
133 let val = val.abs();
134
135 let deg = val as i16;
136 let min = (val - deg as f64) * 60.;
137 (deg * sign, min)
138 }
139
140 pub fn sin(&self) -> f64 {
141 use irox_tools::f64::FloatExt;
142 let v = self.as_radians().value;
143 FloatExt::sin(v)
144 }
145 #[cfg(feature = "std")]
146 pub fn asin(&self) -> f64 {
147 self.as_radians().value.asin()
148 }
149 pub fn cos(&self) -> f64 {
150 use irox_tools::f64::FloatExt;
151 let v = self.as_radians().value;
152 FloatExt::cos(v)
153 }
154 #[cfg(feature = "std")]
155 pub fn acos(&self) -> f64 {
156 self.as_radians().value.acos()
157 }
158
159 #[cfg(feature = "std")]
160 pub fn tan(&self) -> f64 {
161 self.as_radians().value.tan()
162 }
163
164 #[must_use]
165 pub const fn min_value() -> Self {
166 Angle::new_degrees(0.0)
167 }
168}
169
170impl Display for Angle {
171 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
172 write!(f, "{:03.3}\u{00B0}", self.as_degrees().value)
173 }
174}
175
176pub const DEG_2_RAD: f64 = 0.017_453_292_519_943_295;
178pub const RAD_2_DEG: f64 = 57.295_779_513_082_32;
180pub const REV_2_DEG: f64 = 360.;
182pub const REV_2_RAD: f64 = core::f64::consts::TAU;
184pub const MIN_2_SEC: f64 = 60.;
186pub const MIL_2_REV: f64 = 6400.;
188pub const DEG_2_MIN: f64 = 60.;
190pub const DEG_2_SEC: f64 = DEG_2_MIN * MIN_2_SEC;
192pub const DEG_2_MIL: f64 = MIL_2_REV / REV_2_DEG;
194pub const RAD_2_MIL: f64 = MIL_2_REV / REV_2_RAD;