use crate::{
Acceleration, DynamicUnits, Length, LengthUnit, Mass, MassUnit, Real, Scalar, TimeUnit, Torque,
Unit, 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},
};
pub trait ForceUnit: Unit + Copy + Debug + Eq + PartialEq + 'static {
const NEWTONS_IN_UNIT: f64;
type UnitMass: MassUnit;
type UnitLength: LengthUnit;
type UnitTime: TimeUnit;
}
#[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 Force<Unit: ForceUnit> {
v: Real,
#[cfg_attr(feature = "serde", serde(skip))]
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
phantom_1: PhantomData<Unit>,
}
supports_absdiffeq!(Force<A>, ForceUnit);
supports_quantity_ops!(Force<A>, ForceUnit);
supports_scalar_ops!(Force<A>, ForceUnit);
supports_cancellation!(Force<A1>, Force<A2>, ForceUnit);
supports_shift_ops!(Force<A1>, Force<A2>, ForceUnit);
supports_value_type_conversion!(Force<A>, ForceUnit, impl_value_type_conversions);
impl<F> Force<F>
where
F: ForceUnit,
{
pub fn as_dyn(&self) -> DynamicUnits {
DynamicUnits::new2o2::<F::UnitMass, F::UnitLength, F::UnitTime, F::UnitTime>(self.v)
}
}
impl<Unit> fmt::Display for Force<Unit>
where
Unit: ForceUnit,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.v.0, f)?;
write!(f, "{}", Unit::UNIT_SHORT_NAME)
}
}
impl<'a, UnitA, UnitB> From<&'a Force<UnitA>> for Force<UnitB>
where
UnitA: ForceUnit,
UnitB: ForceUnit,
{
fn from(v: &'a Force<UnitA>) -> Self {
Self {
v: v.v * Real(UnitA::NEWTONS_IN_UNIT / UnitB::NEWTONS_IN_UNIT),
phantom_1: PhantomData,
}
}
}
impl<F> From<DynamicUnits> for Force<F>
where
F: ForceUnit,
{
fn from(v: DynamicUnits) -> Self {
let f = v.real();
v.assert_units_equal(DynamicUnits::new2o2::<
F::UnitMass,
F::UnitLength,
F::UnitTime,
F::UnitTime,
>(Real::ZERO));
Self {
v: f,
phantom_1: PhantomData,
}
}
}
impl<F, M> Div<Mass<M>> for Force<F>
where
F: ForceUnit, M: MassUnit,
{
type Output = Acceleration<F::UnitLength, F::UnitTime>;
fn div(self, rhs: Mass<M>) -> Self::Output {
let mass = Mass::<F::UnitMass>::from(&rhs);
Self::Output::from(self.v.0 / mass.f64())
}
}
impl<F, L, T> Div<Acceleration<L, T>> for Force<F>
where
F: ForceUnit,
L: LengthUnit,
T: TimeUnit,
{
type Output = Mass<F::UnitMass>;
fn div(self, rhs: Acceleration<L, T>) -> Self::Output {
let acc = Acceleration::<F::UnitLength, F::UnitTime>::from(&rhs);
Self::Output::from(self.v.0 / acc.f64())
}
}
impl<F, L> Mul<Length<L>> for Force<F>
where
F: ForceUnit, L: LengthUnit,
{
type Output = Torque<F, L>;
fn mul(self, rhs: Length<L>) -> Self::Output {
Self::Output::from(self.v.0 * rhs.f64())
}
}
#[cfg(test)]
mod test {
use crate::{kilograms, newtons, pounds_force, pounds_mass, scalar};
use approx::assert_abs_diff_eq;
#[test]
fn test_force() {
let lbf = pounds_force!(35_000_f64);
let n = newtons!(155_687.7_f64);
assert_abs_diff_eq!(newtons!(lbf), n, epsilon = 0.1);
let lbf = pounds_force!(n);
assert_abs_diff_eq!(lbf, pounds_force!(35_000_f64), epsilon = 0.1);
}
#[test]
fn test_force_scalar() {
assert_abs_diff_eq!(newtons!(2) * scalar!(2), newtons!(4));
}
#[test]
fn test_force_to_acceleration() {
let lbf = pounds_force!(70_000_f64);
let lb = pounds_mass!(55_000_f64);
let imperial_units_should_be_a_capital_offense = (lbf / lb).f64() / 32.1742;
assert!(imperial_units_should_be_a_capital_offense < 1_f64);
assert!((newtons!(lbf) / kilograms!(lb)).f64() / 9.80665 > 1_f64);
}
}