devela/num/geom/metric/angle/impl/
int.rs

1// devela::num::geom::metric::angle::impl::int
2//
3//!
4//
5// IMPROVE IDEAS
6// - make alternative methods that doesn't depnd on floating point operations,
7//   and instead use integer scaling functions Int::scale.
8// - maybe use NonExtreme for the signed representation.
9
10#[cfg(all(not(feature = "std"), _float··))]
11use crate::ExtFloat;
12#[cfg(_float··)]
13#[allow(unused_imports)]
14use crate::{fsize, ExtFloatConst};
15use crate::{Angle, AngleDirection, AngleKind};
16
17/// impl `Angle` methods with an integer representation:
18///
19/// # TOC
20/// - integer common methods
21/// - signed integers specific methods
22/// - unsigned integers specific methods
23///
24/// # Macro arguments
25/// $t: the inner integer primitive type
26/// $f: the associated floating point type
27/// $tcap: the capability feature that enables the given integer implementation. E.g "_int_i8".
28/// $fcap: the capability feature that enables the given floating implementation. E.g "_float_f32".
29macro_rules! impl_angle {
30    () => {
31        impl_angle![sint
32            i8:f32;"_int_i8":"_float_f32",
33            i16:f32;"_int_i16":"_float_f32",
34            i32:f32;"_int_i32":"_float_f32",
35            i64:f64;"_int_i64":"_float_f64",
36            i128:f64;"_int_i128":"_float_f64"
37        ];
38        #[cfg(target_pointer_width = "32")]
39        impl_angle![sint isize:fsize;"_int_isize":"_float_f32"];
40        #[cfg(target_pointer_width = "64")]
41        impl_angle![sint isize:fsize;"_int_isize":"_float_f64"];
42
43        impl_angle![uint
44            u8:f32;"_int_u8":"_float_f32",
45            u16:f32;"_int_u16":"_float_f32",
46            u32:f32;"_int_u32":"_float_f32",
47            u64:f64;"_int_u64":"_float_f64",
48            u128:f64;"_int_u128":"_float_f64"
49        ];
50        #[cfg(target_pointer_width = "32")]
51        impl_angle![uint usize:fsize;"_int_usize":"_float_f32"];
52        #[cfg(target_pointer_width = "64")]
53        impl_angle![uint usize:fsize;"_int_usize":"_float_f64"];
54    };
55
56    // integers common methods
57    (int $($t:ty : $f:ty ; $tcap:literal : $fcap:literal),+) => {
58        $( impl_angle![@int $t:$f ; $tcap:$fcap]; )+
59    };
60    (@int $t:ty : $f:ty ; $tcap:literal : $fcap:literal) => {
61        #[doc = concat!("# Methods for angles represented using `", stringify!($t), "`.")]
62        #[cfg(feature = $tcap )]
63        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $tcap)))]
64        impl Angle<$t> {
65            /* private helpers */
66
67            // Returns the inner value normalized as a float between -1 and 1
68            const fn to_float_normalized(self) -> $f { self.turn as $f / <$t>::MAX as $f }
69            // Returns the `value` associated to the full turn `unit`, scaled to the full $t range.
70            #[cfg(any(feature = "std", feature = $fcap))]
71            fn from_float_normalized(value: $f, unit: $f) -> $t {
72                ((value / unit) * <$t>::MAX as $f).round() as $t
73            }
74
75            /* construct */
76
77            /// Creates a normalized full positive angle at 0 degrees.
78            pub const fn new_full() -> Self { Self::new(0) }
79
80            /// Creates a normalized right positive angle at 90 degrees.
81            pub const fn new_right() -> Self { Self::new(<$t>::MAX / 4) }
82
83            /// Creates a normalized straight positive angle at 180 degrees.
84            pub const fn new_straight() -> Self { Self::new(<$t>::MAX / 2) }
85
86            /// Creates a new angle from a floating-point `radians` value.
87            #[cfg(any(feature = "std", feature = $fcap))]
88            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = $fcap))))]
89            pub fn from_rad(radians: $f) -> Self {
90                Self::new(Self::from_float_normalized(radians, <$f>::TAU))
91            }
92
93            /// Creates a new angle from a floating-point `degrees` value.
94            #[cfg(any(feature = "std", feature = $fcap))]
95            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = $fcap))))]
96            pub fn from_deg(degrees: $f) -> Self {
97                Self::new(Self::from_float_normalized(degrees, 360.0))
98            }
99
100            /// Creates a new angle from a `value` in a `custom_unit` which represents a full turn.
101            #[cfg(any(feature = "std", feature = $fcap))]
102            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = $fcap))))]
103            pub fn from_custom(value: $f, custom_unit: $f) -> Self {
104                Self::new(Self::from_float_normalized(value, custom_unit))
105            }
106
107            /* convert */
108
109            /// Converts the angle to radians.
110            #[must_use]
111            #[cfg(any(feature = "std", _float··))]
112            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", _float··))))]
113            pub const fn to_rad(self) -> $f { self.to_float_normalized() * <$f>::TAU }
114
115            /// Converts the angle to degrees.
116            #[must_use]
117            pub const fn to_deg(self) -> $f { self.to_float_normalized() * 360.0 }
118
119            /// Converts the angle to a `custom_unit` which represents a full turn.
120            #[must_use]
121            pub const fn to_custom(self, custom_unit: $f) -> $f {
122                self.to_float_normalized() * custom_unit
123            }
124
125            /* normalize */
126
127            /// Always returns `true` since integer representations are always normalized.
128            #[must_use]
129            pub const fn is_normalized(self) -> bool { true }
130
131            /// Returns the angle normalized (no-op for integer representation).
132            pub const fn normalize(self) -> Self { self }
133
134            /// Sets the angle normalized (no-op for integer representation).
135            pub fn set_normalized(&mut self) {}
136
137            /// Returns `true` if the angle has the given `direction`.
138            #[must_use ]
139            pub const fn has_direction(self, direction: AngleDirection) -> bool {
140                direction as i8 == self.direction() as i8
141            }
142
143            /* kind */
144
145            /// Returns the kind of the normalized angle.
146            pub const fn kind(self) -> AngleKind {
147                let angle = self.positive().turn;
148                let right = <$t>::MAX / 4;
149                let straight = <$t>::MAX / 2;
150                use AngleKind as K;
151                if angle == 0 { // 1 turn (0' or 360º)
152                    K::Full
153                } else if angle == right { // 1/4 turn (90º)
154                    K::Right
155                } else if angle == straight { // 1/2 turn (180º)
156                    K::Straight
157                //
158                } else if angle < right { // < 1/4 turn (< 90º)
159                    K::Acute
160                } else if angle < straight { // < 1/2 turn (< 180º)
161                    K::Obtuse
162                } else { // < 1 turn (< 360º)
163                    K::Reflex
164                }
165            }
166
167            /// Returns `true` if the angle is of the given `kind`.
168            #[must_use]
169            pub const fn is_kind(self, kind: AngleKind) -> bool {
170                let angle = self.positive().turn;
171                let right = <$t>::MAX / 4;
172                let straight = <$t>::MAX / 2;
173
174                use AngleKind as K;
175                match kind {
176                    K::Full => angle == 0,
177                    K::Right => angle == right,
178                    K::Straight => angle == straight,
179                    //
180                    K::Acute => angle > 0 && angle < right,
181                    K::Obtuse => angle < right && angle < straight,
182                    K::Reflex => angle > right,
183                }
184            }
185        }
186    };
187
188    // signed integers specific methods
189    (sint $($t:ty : $f:ty ; $tcap:literal : $fcap:literal),+) => {
190        $( impl_angle![@sint $t:$f ; $tcap:$fcap]; )+
191    };
192    (@sint $t:ty : $f:ty ; $tcap:literal : $fcap:literal) => {
193        impl_angle![int $t:$f ; $tcap:$fcap];
194
195        #[doc = concat!("# Methods for angles represented using `", stringify!($t), "`, signed.")]
196        #[cfg(feature = $tcap )]
197        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $tcap)))]
198        impl Angle<$t> {
199            /* direction */
200
201            /// Returns the angle direction.
202            ///
203            /// The direction will be `Undefined` if the angle kind is [`Full`][AngleKind::Full].
204            pub const fn direction(self) -> AngleDirection {
205                use AngleDirection as D;
206                if self.turn == 0 {
207                    D::Undefined
208                } else if self.turn > 0 {
209                    D::Positive
210                } else {
211                    D::Negative
212                }
213            }
214
215            /// Returns a version of the angle with the given `direction`.
216            ///
217            /// An `Undefined` direction will be interpreted as counter-clockwise (positive).
218            pub const fn with_direction(self, direction: AngleDirection) -> Self {
219                use AngleDirection as D;
220                match direction {
221                    D::Positive | D::Undefined => Self::new(self.turn.saturating_abs()),
222                    D::Negative => Self::new(-self.turn.saturating_abs()),
223                }
224            }
225
226            /// Returns a version of the angle with the given `direction`.
227            ///
228            /// An `Undefined` direction will be interpreted as counter-clockwise (positive).
229            pub fn set_direction(&mut self, direction: AngleDirection) {
230                use AngleDirection as D;
231                match direction {
232                    D::Positive | D::Undefined => self.turn = self.turn.saturating_abs(),
233                    D::Negative => self.turn = -self.turn.saturating_abs(),
234                }
235            }
236
237            /// Returns a version of the angle with inverted direction.
238            pub const fn invert_direction(self) -> Self {
239                Self::new(self.turn.saturating_neg())
240            }
241
242            /// Returns the negative version of the angle.
243            pub const fn negative(self) -> Self { Self::new(-self.turn.saturating_abs()) }
244
245            /// Sets the angle as negative.
246            pub fn set_negative(&mut self) { self.turn = -self.turn.saturating_abs(); }
247
248            /// Returns the positive version of the angle.
249            pub const fn positive(self) -> Self { Self::new(self.turn.saturating_abs()) }
250
251            /// Sets the angle as positive.
252            pub fn set_positive(&mut self) { self.turn = self.turn.saturating_abs(); }
253        }
254    };
255
256    // unsigned integers specific methods
257    (uint $($t:ty : $f:ty ; $tcap:literal : $fcap:literal),+) => {
258        $( impl_angle![@uint $t:$f ; $tcap:$fcap]; )+
259    };
260    (@uint $t:ty : $f:ty ; $tcap:literal : $fcap:literal) => {
261        impl_angle![int $t:$f ; $tcap:$fcap];
262
263        #[doc = concat!("# Methods for angles represented using `", stringify!($t), "`, unsigned.")]
264        #[cfg(feature = $tcap )]
265        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $tcap)))]
266        impl Angle<$t> {
267            /* direction */
268
269            /// Returns the angle direction.
270            ///
271            /// For unsigned integers the direction is always `Positive`.
272            pub const fn direction(self) -> AngleDirection { AngleDirection::Positive }
273
274            /// Returns a version of the angle with the given `direction` (no-op for unsigned).
275            ///
276            /// Unsigned integers can only have `Positive` direction.
277            pub const fn with_direction(self, _direction: AngleDirection) -> Self { self }
278
279            /// Returns a version of the angle with the given `direction` (no-op for unsigned).
280            ///
281            /// Unsigned integers can only have `Positive` direction.
282            pub const fn set_direction(self, _direction: AngleDirection) {}
283
284            /// Returns a version of the angle with inverted direction (no-op for unsigned).
285            ///
286            /// Unsigned integers can only have `Positive` direction.
287            pub const fn invert_direction(self) -> Self { self }
288
289            /// Returns the negative version of the angle (no-op for unsigned).
290            pub const fn negative(self) -> Self { self }
291
292            /// Sets the angle as negative (no-op for unsigned).
293            pub fn set_negative(&mut self) {}
294
295            /// Returns the positive version of the angle (no-op for unsigned).
296            pub const fn positive(self) -> Self { self }
297
298            /// Sets the angle as positive (no-op for unsigned).
299            pub fn set_positive(&mut self) {}
300
301        }
302    };
303}
304impl_angle!();