1use auto_ops::*;
6
7pub use std::f64::consts::PI;
8
9pub const TWO_PI: f64 = 2.0 * PI;
11pub const PI_HALF: f64 = PI / 2.0;
13pub const PI_FOURTH: f64 = PI / 4.0;
15
16#[derive(Copy, Clone, Debug, PartialEq)]
18pub enum Angle {
19 Degree(f64),
21 Radian(f64),
23 Hour(f64),
25}
26
27impl Angle {
28 pub fn to_deg(&self) -> f64 {
30 match self {
31 Self::Degree(deg) => *deg,
32 Self::Hour(hr) => 15.0 * hr,
33 Self::Radian(rad) => {
34 const RAD_TO_DEG: f64 = 180.0 / PI;
35 RAD_TO_DEG * rad
36 }
37 }
38 }
39 pub fn to_rad(&self) -> f64 {
41 match self {
42 Self::Degree(deg) => {
43 const DEG_TO_RAD: f64 = PI / 180.0;
44 DEG_TO_RAD * deg
45 }
46 Self::Hour(hr) => {
47 const HOUR_TO_RAD: f64 = 15.0 * PI / 180.0;
48 HOUR_TO_RAD * hr
49 }
50 Self::Radian(rad) => *rad,
51 }
52 }
53 pub fn to_hr(&self) -> f64 {
55 match self {
56 Self::Degree(deg) => deg / 15.0,
57 Self::Hour(hr) => *hr,
58 Self::Radian(rad) => {
59 const RAD_TO_HOUR: f64 = 180.0 / PI / 15.0;
60 RAD_TO_HOUR * rad
61 }
62 }
63 }
64 pub fn sin(&self) -> f64 {
65 Self::to_rad(self).sin()
66 }
67 pub fn cos(&self) -> f64 {
68 Self::to_rad(self).cos()
69 }
70 pub fn tan(&self) -> f64 {
71 Self::to_rad(self).tan()
72 }
73}
74
75impl_op_ex!(+ |a: &Angle, b: &Angle| -> Angle { match a {
76 Angle::Degree(deg) => Angle::Degree(deg + b.to_deg()),
77 Angle::Radian(rad) => Angle::Degree(rad + b.to_rad()),
78 Angle::Hour(hr) => Angle::Degree(hr + b.to_hr()),
79} });
80
81impl_op_ex!(-|a: &Angle, b: &Angle| -> Angle {
82 match a {
83 Angle::Degree(deg) => Angle::Degree(deg - b.to_deg()),
84 Angle::Radian(rad) => Angle::Degree(rad - b.to_rad()),
85 Angle::Hour(hr) => Angle::Degree(hr - b.to_hr()),
86 }
87});
88
89#[derive(Debug, PartialEq, Eq, Clone)]
91pub enum Sign {
92 Positive,
93 Negative,
94}
95
96#[derive(Debug, PartialEq, Clone)]
98pub struct DegMinSec(
99 pub Sign,
101 pub u32,
103 pub u32,
105 pub f64,
107);
108
109#[derive(Debug, PartialEq, Clone)]
111pub struct HourMinSec(
112 pub Sign,
114 pub u32,
116 pub u32,
118 pub f64,
120);
121
122macro_rules! impl_arc_minute_second {
129 ($T:ty) => {
130 impl $T {
131 pub fn angle_to_ams(decimal_angle: f64) -> Self {
135 let major: u32 = decimal_angle as u32;
136 let min: f64 = (decimal_angle - (major as f64)) * 60.0;
137 let second: f64 = (min - (min as u32 as f64)) * 60.0;
138 Self(
139 if decimal_angle.is_sign_positive() {
140 Sign::Positive
141 } else {
142 Sign::Negative
143 },
144 major,
145 min as u32,
146 second,
147 )
148 }
149 }
150 };
151}
152
153impl_arc_minute_second!(DegMinSec);
154impl_arc_minute_second!(HourMinSec);
155
156impl From<Angle> for DegMinSec {
157 fn from(angle: Angle) -> Self {
158 Self::angle_to_ams(angle.to_deg())
159 }
160}
161
162impl From<Angle> for HourMinSec {
163 fn from(angle: Angle) -> Self {
164 Self::angle_to_ams(angle.to_hr())
165 }
166}
167
168impl From<DegMinSec> for Angle {
169 fn from(angle: DegMinSec) -> Angle {
170 let angle_abs: f64 = angle.1 as f64 + angle.2 as f64 / 60.0 + angle.3 / 3600.0;
171 match angle.0 {
172 Sign::Positive => Angle::Degree(angle_abs),
173 Sign::Negative => Angle::Degree(-angle_abs),
174 }
175 }
176}
177
178impl From<HourMinSec> for Angle {
179 fn from(angle: HourMinSec) -> Angle {
180 let angle_abs: f64 = angle.1 as f64 + angle.2 as f64 / 60.0 + angle.3 / 3600.0;
181 match angle.0 {
182 Sign::Positive => Angle::Hour(angle_abs),
183 Sign::Negative => Angle::Hour(-angle_abs),
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use crate::angle::*;
191
192 const PI_THIRD: f64 = PI / 3.0;
193 const PI_SIXTH: f64 = PI / 6.0;
194
195 #[test]
196 fn angle_enum() {
197 assert_float_absolute_eq!(
199 Angle::Degree(90.0).to_deg(),
200 Angle::Radian(PI_HALF).to_deg()
201 );
202 assert_float_absolute_eq!(Angle::Degree(90.0).to_deg(), Angle::Hour(6.0).to_deg());
203 assert_float_absolute_eq!(Angle::Hour(6.0).to_deg(), Angle::Radian(PI_HALF).to_deg());
204 assert_float_absolute_eq!(Angle::Hour(6.0).to_deg(), Angle::Degree(90.0).to_deg());
205 assert_float_absolute_eq!(Angle::Radian(PI_HALF).to_deg(), Angle::Hour(6.0).to_deg());
206 assert_float_absolute_eq!(
207 Angle::Radian(PI_HALF).to_deg(),
208 Angle::Degree(90.0).to_deg()
209 );
210
211 assert_float_absolute_eq!(1.0, Angle::Degree(0.0).cos());
213 assert_float_absolute_eq!(1.0, Angle::Hour(0.0).cos());
214 assert_float_absolute_eq!(1.0, Angle::Radian(0.0).cos());
215
216 assert_float_absolute_eq!(0.0, Angle::Degree(90.0).cos());
217 assert_float_absolute_eq!(0.0, Angle::Hour(6.0).cos());
218 assert_float_absolute_eq!(0.0, Angle::Radian(PI_HALF).cos());
219
220 assert_float_absolute_eq!(0.5, Angle::Degree(60.0).cos());
221 assert_float_absolute_eq!(0.5, Angle::Hour(4.0).cos());
222 assert_float_absolute_eq!(0.5, Angle::Radian(PI_THIRD).cos());
223
224 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Degree(45.0).cos());
225 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Hour(3.0).cos());
226 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Radian(PI_FOURTH).cos());
227
228 assert_float_absolute_eq!(0.0, Angle::Degree(0.0).sin());
229 assert_float_absolute_eq!(0.0, Angle::Hour(0.0).sin());
230 assert_float_absolute_eq!(0.0, Angle::Radian(0.0).sin());
231
232 assert_float_absolute_eq!(1.0, Angle::Degree(90.0).sin());
233 assert_float_absolute_eq!(1.0, Angle::Hour(6.0).sin());
234 assert_float_absolute_eq!(1.0, Angle::Radian(PI_HALF).sin());
235
236 assert_float_absolute_eq!(3_f64.sqrt() / 2.0, Angle::Degree(60.0).sin());
237 assert_float_absolute_eq!(3_f64.sqrt() / 2.0, Angle::Hour(4.0).sin());
238 assert_float_absolute_eq!(3_f64.sqrt() / 2.0, Angle::Radian(PI_THIRD).sin());
239
240 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Degree(45.0).sin());
241 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Hour(3.0).sin());
242 assert_float_absolute_eq!(2_f64.sqrt() / 2.0, Angle::Radian(PI_FOURTH).sin());
243
244 assert_float_absolute_eq!(0.5, Angle::Degree(30.0).sin());
245 assert_float_absolute_eq!(0.5, Angle::Hour(2.0).sin());
246 assert_float_absolute_eq!(0.5, Angle::Radian(PI_SIXTH).sin());
247
248 assert_float_absolute_eq!(1.0, Angle::Degree(45.0).tan());
249 assert_float_absolute_eq!(1.0, Angle::Hour(3.0).tan());
250 assert_float_absolute_eq!(1.0, Angle::Radian(PI_FOURTH).tan());
251 }
252
253 #[test]
254 fn arc_min_sec() {
255 assert_eq!(
257 DegMinSec::from(Angle::Degree(0.0)),
258 DegMinSec(Sign::Positive, 0, 0, 0.0)
259 );
260 assert_eq!(
261 DegMinSec::from(Angle::Degree(-0.0)),
262 DegMinSec(Sign::Negative, 0, 0, 0.0)
263 );
264 assert_eq!(
265 DegMinSec::from(Angle::Degree(180.0)),
266 DegMinSec(Sign::Positive, 180, 0, 0.0)
267 );
268 assert_eq!(
269 DegMinSec::from(Angle::Radian(0.0)),
270 DegMinSec(Sign::Positive, 0, 0, 0.0)
271 );
272 assert_eq!(
273 DegMinSec::from(Angle::Radian(-0.0)),
274 DegMinSec(Sign::Negative, 0, 0, 0.0)
275 );
276 assert_eq!(
277 DegMinSec::from(Angle::Radian(PI)),
278 DegMinSec(Sign::Positive, 180, 0, 0.0)
279 );
280 assert_eq!(
281 DegMinSec::from(Angle::Hour(12.0)),
282 DegMinSec(Sign::Positive, 180, 0, 0.0)
283 );
284 assert_eq!(
286 HourMinSec::from(Angle::Degree(0.0)),
287 HourMinSec(Sign::Positive, 0, 0, 0.0)
288 );
289 assert_eq!(
290 HourMinSec::from(Angle::Degree(-0.0)),
291 HourMinSec(Sign::Negative, 0, 0, 0.0)
292 );
293 assert_eq!(
294 HourMinSec::from(Angle::Hour(12.0)),
295 HourMinSec(Sign::Positive, 12, 0, 0.0)
296 );
297 assert_eq!(
298 HourMinSec::from(Angle::Radian(0.0)),
299 HourMinSec(Sign::Positive, 0, 0, 0.0)
300 );
301 assert_eq!(
302 HourMinSec::from(Angle::Radian(-0.0)),
303 HourMinSec(Sign::Negative, 0, 0, 0.0)
304 );
305 assert_eq!(
306 HourMinSec::from(Angle::Radian(PI)),
307 HourMinSec(Sign::Positive, 12, 0, 0.0)
308 );
309 assert_eq!(
310 HourMinSec::from(Angle::Degree(180.0)),
311 HourMinSec(Sign::Positive, 12, 0, 0.0)
312 );
313 }
314}