use crate::{Angle, Measurement};
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Length {
metres: f64,
}
impl Length {
const FT_TO_M: f64 = 0.3048;
const NM_TO_M: f64 = 1_852.0;
const KM_TO_M: f64 = 1_000.0;
pub const ZERO: Length = Length { metres: 0.0 };
pub const MAX: Length = Length { metres: f64::MAX };
pub const fn from_metres(metres: f64) -> Self {
Length { metres }
}
pub fn from_kilometres(kilometres: f64) -> Self {
Length::from_metres(kilometres * Self::KM_TO_M)
}
pub fn from_feet(feet: f64) -> Self {
Length::from_metres(feet * Self::FT_TO_M)
}
pub fn from_nautical_miles(nautical_miles: f64) -> Self {
Length::from_metres(nautical_miles * Self::NM_TO_M)
}
#[inline]
pub const fn as_metres(&self) -> f64 {
self.metres
}
pub fn as_kilometres(&self) -> f64 {
self.metres / Self::KM_TO_M
}
pub fn as_feet(&self) -> f64 {
self.metres / Self::FT_TO_M
}
pub fn as_nautical_miles(&self) -> f64 {
self.metres / Self::NM_TO_M
}
pub fn abs(&self) -> Self {
if self.metres >= 0.0 {
*self
} else {
Self::from_metres(-self.metres)
}
}
pub fn round_m(&self) -> Self {
Self {
metres: self.metres.round(),
}
}
pub fn round_dm(&self) -> Self {
let metres = (self.metres * 10.0).round() / 10.0;
Self { metres }
}
pub fn round_cm(&self) -> Self {
let metres = (self.metres * 100.0).round() / 100.0;
Self { metres }
}
pub fn round_mm(&self) -> Self {
let metres = (self.metres * 1000.0).round() / 1000.0;
Self { metres }
}
}
impl Measurement for Length {
fn from_default_unit(amount: f64) -> Self {
Length::from_metres(amount)
}
#[inline]
fn as_default_unit(&self) -> f64 {
self.metres
}
}
impl_measurement! { Length }
impl ::std::ops::Mul<Angle> for Length {
type Output = Length;
fn mul(self, rhs: Angle) -> Length {
Length::from_metres(rhs.as_radians() * self.metres)
}
}
impl ::std::ops::Mul<Length> for Angle {
type Output = Length;
fn mul(self, rhs: Length) -> Length {
Length::from_metres(self.as_radians() * rhs.metres)
}
}
#[cfg(feature = "uom")]
impl From<uom::si::f64::Length> for Length {
fn from(value: uom::si::f64::Length) -> Self {
Self::from_metres(value.get::<uom::si::length::meter>())
}
}
#[cfg(feature = "uom")]
impl From<Length> for uom::si::f64::Length {
fn from(value: Length) -> Self {
Self::new::<uom::si::length::meter>(value.as_metres())
}
}
#[cfg(test)]
mod tests {
use crate::{Angle, Length};
#[test]
fn units() {
assert_eq!(100.0, Length::from_metres(100.0).as_metres());
assert_eq!(3900.0, Length::from_metres(3900.0).as_metres());
assert_eq!(10.0, Length::from_metres(18520.0).as_nautical_miles());
assert_eq!(18520.0, Length::from_nautical_miles(10.0).as_metres());
assert_eq!(2.5, Length::from_metres(2500.0).as_kilometres());
assert_eq!(2500.0, Length::from_kilometres(2.5).as_metres());
assert_eq!(1000.0, Length::from_metres(304.8).as_feet());
assert_eq!(304.8, Length::from_feet(1000.0).as_metres());
}
#[test]
fn mul() {
assert_eq!(
Length::from_metres(100.0),
Length::from_metres(50.0) * Angle::from_radians(2.0)
);
assert_eq!(
Length::from_metres(100.0),
Angle::from_radians(2.0) * Length::from_metres(50.0)
);
}
#[test]
fn round() {
assert_eq!(
Length::from_metres(50.0),
Length::from_metres(50.1).round_m()
);
assert_eq!(
Length::from_metres(50.2),
Length::from_metres(50.15).round_dm()
);
assert_eq!(
Length::from_metres(50.16),
Length::from_metres(50.157).round_cm()
);
assert_eq!(
Length::from_metres(50.157),
Length::from_metres(50.1574).round_mm()
);
}
#[cfg(feature = "uom")]
#[test]
fn uom() {
let length = Length::from_metres(1.0);
let uom = uom::si::f64::Length::from(length);
let roundtrip = Length::from(uom);
assert_eq!(length, roundtrip);
}
}