devela/num/geom/metric/angle/impl/
float.rs
1#[cfg(_float··)]
7#[allow(unused_imports)]
8use crate::{Angle, AngleDirection, AngleKind, ExtFloat, ExtFloatConst, Float};
9
10macro_rules! impl_angle {
17 () => {
18 impl_angle![float
19 f32:"_float_f32":"_cmp_f32",
20 f64:"_float_f64":"_cmp_f64"
21 ];
22 };
23 (float $($f:ty : $cap:literal : $cmp:literal),+) => {
24 $( impl_angle![@float $f:$cap:$cmp]; )+
25 };
26 (@float $f:ty : $cap:literal : $cmp:literal) => {
27 #[doc = concat!("# Methods for angles represented using `", stringify!($f), "`.")]
28 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
29 #[cfg(feature = $cap )]
30 impl Angle<$f> {
31 pub const fn new_full() -> Self { Self::new(0.0) }
35
36 pub const fn new_right() -> Self { Self::new(0.25) }
38
39 pub const fn new_straight() -> Self { Self::new(0.5) }
41
42 pub const fn from_rad(radians: $f) -> Self { Self::new(radians / <$f>::TAU) }
44
45 pub const fn from_deg(degrees: $f) -> Self { Self::new(degrees / 360.0) }
47
48 pub const fn from_custom(value: $f, custom_unit: $f) -> Self {
50 Self::new(value / custom_unit)
51 }
52
53 #[must_use]
57 pub const fn to_rad(self) -> $f { self.turn * <$f>::TAU }
58
59 #[must_use]
61 pub const fn to_deg(self) -> $f { self.turn * 360.0 }
62
63 #[must_use]
65 pub const fn to_custom(self, custom_unit: $f) -> $f { self.turn * custom_unit }
66
67 #[cfg(feature = $cmp)]
71 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cmp)))]
72 pub const fn is_normalized(self) -> bool {
73 crate::Compare(self.turn).gt(-1.0) && crate::Compare(self.turn).lt(1.0)
74 }
75
76 pub fn normalize(self) -> Self { Self::new(self.turn.fract()) }
79
80 pub fn set_normalized(&mut self) { self.turn = self.turn.fract(); }
83
84 pub const fn direction(self) -> AngleDirection {
91 use AngleDirection::{Negative, Positive};
92 if Float(self.turn).is_sign_negative() { Negative } else { Positive }
93 }
94
95 #[must_use]
100 pub const fn has_direction(self, direction: AngleDirection) -> bool {
101 direction as i8 == self.direction() as i8
102 }
103
104 pub const fn with_direction(self, direction: AngleDirection) -> Self {
108 use AngleDirection as D;
109 match direction {
110 D::Positive | D::Undefined => Self::new(Float(self.turn).abs().0),
111 D::Negative => Self::new(Float(self.turn).neg_abs().0),
112 }
113 }
114
115 pub const fn set_direction(&mut self, direction: AngleDirection) {
119 use AngleDirection as D;
120 match direction {
121 D::Positive | D::Undefined => self.turn = Float(self.turn).abs().0,
122 D::Negative => self.turn = Float(self.turn).neg_abs().0,
123 }
124 }
125
126 pub const fn invert_direction(self) -> Self {
128 Self::new(Float(self.turn).flip_sign().0)
129 }
130
131 pub const fn negative(self) -> Self { Self::new(Float(self.turn).neg_abs().0) }
133
134 pub const fn set_negative(&mut self) { { self.turn = Float(self.turn).neg_abs().0; } }
136
137 pub const fn positive(self) -> Self { Self::new(Float(self.turn).abs().0) }
139
140 pub const fn set_positive(&mut self) { self.turn = Float(self.turn).abs().0; }
142
143 #[cfg(feature = $cmp)]
148 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cmp)))]
149 pub fn kind(self) -> AngleKind {
150 let angle = crate::Compare(self.normalize().positive().turn);
151 use AngleKind::{Full, Acute, Right, Obtuse, Straight, Reflex};
152 if angle.eq(0.0) { Full
154 } else if angle.eq(0.25) { Right
156 } else if angle.eq(0.5) { Straight
158 } else if angle.lt(0.25) { Acute
160 } else if angle.lt(0.5) { Obtuse
162 } else { Reflex
164 }
165 }
166 pub fn kind_approx(self, tolerance: $f) -> AngleKind {
169 let angle = self.normalize().positive().turn;
170 use AngleKind::{Full, Acute, Right, Obtuse, Straight, Reflex};
171 if (angle - 0.0).abs() <= tolerance {
172 Full
173 } else if (angle - 0.25).abs() <= tolerance {
174 Right
175 } else if (angle - 0.5).abs() <= tolerance {
176 Straight
177 } else if angle < 0.25 {
178 Acute
179 } else if angle < 0.5 {
180 Obtuse
181 } else {
182 Reflex
183 }
184 }
185
186 #[must_use]
188 #[cfg(feature = $cmp)]
189 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cmp)))]
190 pub fn is_kind(self, kind: AngleKind) -> bool {
192 let angle = crate::Compare(self.normalize().positive().turn);
193 use AngleKind::{Full, Acute, Right, Obtuse, Straight, Reflex};
194 match kind {
195 Full => angle.eq(0.0),
196 Right => angle.eq(0.25),
197 Straight => angle.eq(0.5),
198 Acute => angle.gt(0.0) && angle.lt(0.25),
199 Obtuse => angle.gt(0.25) && angle.lt(0.5),
200 Reflex => angle.gt(0.5) && angle.lt(1.0),
201 }
202 }
203
204 #[must_use]
206 pub fn is_kind_approx(self, kind: AngleKind, tolerance: $f) -> bool {
208 let angle = self.normalize().positive().turn;
209 match kind {
210 AngleKind::Full => (angle - 0.0).abs() <= tolerance,
211 AngleKind::Right => (angle - 0.25).abs() <= tolerance,
212 AngleKind::Straight => (angle - 0.5).abs() <= tolerance,
213 AngleKind::Acute => angle > 0.0 && angle < 0.25,
214 AngleKind::Obtuse => angle > 0.25 && angle < 0.5,
215 AngleKind::Reflex => angle > 0.5 && angle < 1.0,
216 }
217 }
218 }
219 };
220}
221impl_angle!();