irox_units/units/
angle.rsuse core::fmt::{Display, Formatter};
use crate::units::{FromUnits, Unit};
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
#[non_exhaustive]
pub enum AngleUnits {
#[default]
Radians,
Degrees,
Minutes,
Seconds,
Revolutions,
Mils,
}
macro_rules! from_units_angle {
($type:ident) => {
impl crate::units::FromUnits<$type> for AngleUnits {
fn from(&self, value: $type, units: Self) -> $type {
match self {
AngleUnits::Degrees => match units {
AngleUnits::Radians => value * RAD_2_DEG as $type,
AngleUnits::Degrees => value as $type,
AngleUnits::Minutes => value * RAD_2_DEG as $type * DEG_2_MIN as $type,
AngleUnits::Seconds => value * RAD_2_DEG as $type * DEG_2_SEC as $type,
AngleUnits::Revolutions => value / REV_2_RAD as $type,
AngleUnits::Mils => value * RAD_2_MIL as $type,
},
AngleUnits::Radians => match units {
AngleUnits::Degrees => value * DEG_2_RAD as $type,
AngleUnits::Radians => value as $type,
AngleUnits::Minutes => value * DEG_2_MIN as $type,
AngleUnits::Seconds => value * DEG_2_SEC as $type,
AngleUnits::Revolutions => value / REV_2_DEG as $type,
AngleUnits::Mils => value * DEG_2_MIL as $type,
},
_ => todo!(),
}
}
}
};
}
basic_unit!(Angle, AngleUnits, Degrees);
from_units_angle!(f32);
from_units_angle!(f64);
impl Angle {
#[must_use]
pub const fn new_radians(value: f64) -> Angle {
Self::new(value, AngleUnits::Radians)
}
#[must_use]
pub const fn new_degrees(value: f64) -> Angle {
Self::new(value, AngleUnits::Degrees)
}
#[must_use]
pub fn new_dms(degrees: i16, minutes: u8, seconds: f64) -> Angle {
let mult: f64 = match degrees {
..=0 => -1.0,
_ => 1.0,
};
let minutes: f64 = f64::from(minutes) * mult;
let seconds: f64 = seconds * mult;
let value = f64::from(degrees) + minutes / 60. + seconds / 3600.;
Self::new_degrees(value)
}
#[must_use]
pub fn as_degrees(&self) -> Angle {
self.as_unit(AngleUnits::Degrees)
}
#[must_use]
pub fn as_radians(&self) -> Angle {
self.as_unit(AngleUnits::Radians)
}
#[must_use]
pub fn as_dms(&self) -> (i16, u8, f64) {
let (deg, val) = self.as_deg_min();
let min = val as u8;
let sec = (val - min as f64) * 60.;
(deg, min, sec)
}
#[must_use]
#[allow(unused_imports)]
pub fn as_deg_min(&self) -> (i16, f64) {
use irox_tools::f64::FloatExt;
let val = self.as_degrees().value;
let sign = val.signum() as i16;
let val = val.abs();
let deg = val as i16;
let min = (val - deg as f64) * 60.;
(deg * sign, min)
}
#[cfg(feature = "std")]
pub fn sin(&self) -> f64 {
self.as_radians().value.sin()
}
#[cfg(feature = "std")]
pub fn asin(&self) -> f64 {
self.as_radians().value.asin()
}
#[cfg(feature = "std")]
pub fn cos(&self) -> f64 {
self.as_radians().value.cos()
}
#[cfg(feature = "std")]
pub fn acos(&self) -> f64 {
self.as_radians().value.acos()
}
}
impl Display for Angle {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:03.3}\u{00B0}", self.as_degrees().value)
}
}
pub const DEG_2_RAD: f64 = 0.017_453_292_519_943_295;
pub const RAD_2_DEG: f64 = 57.295_779_513_082_32;
pub const REV_2_DEG: f64 = 360.;
pub const REV_2_RAD: f64 = core::f64::consts::TAU;
pub const MIN_2_SEC: f64 = 60.;
pub const MIL_2_REV: f64 = 6400.;
pub const DEG_2_MIN: f64 = 60.;
pub const DEG_2_SEC: f64 = DEG_2_MIN * MIN_2_SEC;
pub const DEG_2_MIL: f64 = MIL_2_REV / REV_2_DEG;
pub const RAD_2_MIL: f64 = MIL_2_REV / REV_2_RAD;