absolute_unit 0.11.4

A unit system for Rust's type system to catch unit errors in your physical calculations.
Documentation
use crate::{
    DynamicUnits, Length, LengthUnit, Real, Scalar, Volume, impl_value_type_conversions,
    supports_absdiffeq, supports_cancellation, supports_quantity_ops, supports_scalar_ops,
    supports_shift_ops, supports_value_type_conversion,
};
use std::{
    fmt,
    fmt::Debug,
    marker::PhantomData,
    ops::{Div, Mul},
};

#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct Area<Unit: LengthUnit> {
    v: Real,
    #[cfg_attr(feature = "serde", serde(skip))]
    #[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
    phantom_1: PhantomData<Unit>,
}
supports_quantity_ops!(Area<A>, LengthUnit);
supports_shift_ops!(Area<A1>, Area<A2>, LengthUnit);
supports_scalar_ops!(Area<A>, LengthUnit);
supports_cancellation!(Area<A1>, Area<A2>, LengthUnit);
supports_absdiffeq!(Area<A>, LengthUnit);
supports_value_type_conversion!(Area<A>, LengthUnit, impl_value_type_conversions);

impl<Unit> fmt::Display for Area<Unit>
where
    Unit: LengthUnit,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.v.0, f)?;
        write!(f, "{}^2", Unit::UNIT_SHORT_NAME)
    }
}

impl<'a, UnitA, UnitB> From<&'a Area<UnitA>> for Area<UnitB>
where
    UnitA: LengthUnit,
    UnitB: LengthUnit,
{
    fn from(v: &'a Area<UnitA>) -> Self {
        let ratio = Real(UnitA::METERS_IN_UNIT / UnitB::METERS_IN_UNIT);
        Self {
            v: v.v * ratio * ratio,
            phantom_1: PhantomData,
        }
    }
}

impl<LA> Area<LA>
where
    LA: LengthUnit,
{
    pub fn as_dyn(&self) -> DynamicUnits {
        DynamicUnits::new2o0::<LA, LA>(self.v)
    }

    pub fn sqrt(&self) -> Length<LA> {
        Length::<LA>::from(self.v.sqrt())
    }
}

impl<UnitA, UnitB> Div<Length<UnitA>> for Area<UnitB>
where
    UnitA: LengthUnit,
    UnitB: LengthUnit,
{
    type Output = Length<UnitB>;

    fn div(self, other: Length<UnitA>) -> Self::Output {
        Length::<UnitB>::from(self.v.0 / Length::<UnitB>::from(&other).f64())
    }
}

impl<UnitA, UnitB> Mul<Length<UnitA>> for Area<UnitB>
where
    UnitA: LengthUnit,
    UnitB: LengthUnit,
{
    type Output = Volume<UnitB>;

    fn mul(self, other: Length<UnitA>) -> Self::Output {
        Volume::<UnitB>::from(self.v.0 * Length::<UnitB>::from(&other).f64())
    }
}

#[cfg(test)]
mod test {
    use crate::{feet, feet2, meters, meters2, scalar};
    use approx::assert_abs_diff_eq;

    #[test]
    fn test_meters_to_feet() {
        let ft = feet2!(1);
        println!("ft^2: {ft}");
        println!("m^2 : {}", meters2!(ft));
        assert_abs_diff_eq!(meters2!(ft), meters2!(0.092_903), epsilon = 0.000_001);
    }

    #[test]
    fn test_scalar_area() {
        assert_abs_diff_eq!(meters2!(2) * scalar!(2), meters2!(4));
    }

    #[test]
    fn test_derived_area() {
        let ft2 = feet!(2) * meters!(1);
        println!("ft2: {ft2}");
        assert_abs_diff_eq!(ft2, feet2!(6.561_679), epsilon = 0.000_001);
    }

    #[test]
    fn test_derived_length() {
        let m = meters2!(4) / feet!(10);
        println!("m: {m}");
        assert_abs_diff_eq!(m, meters!(1.312_335), epsilon = 0.000_001);
    }
}