use std::any::Any;
use std::fmt::{Display, Formatter};
use std::ops::Div;
mod acceleration;
mod angular;
mod angular_acceleration;
mod angular_momentum;
mod angular_velocity;
mod area;
mod coef;
mod distance;
mod electric_capacitance;
mod electric_charge;
mod electric_conductance;
mod electric_current;
mod electric_potential;
mod electric_resistance;
mod energy;
mod force;
mod magnetic_angular_velocity;
mod magnetic_induction;
mod magnetic_moment;
pub mod mass;
mod momentum;
mod power;
mod torque;
pub mod vector;
mod velocity;
mod volume;
pub trait VectorQuantity: PhysicalQuantity {}
pub trait PhysicalQuantity: Any {
fn as_any(&self) -> &dyn Any;
fn is_zero(&self) -> bool;
fn default_unit_value(&self) -> f64;
fn set_value(&mut self, value: f64);
fn unit_display_name(&self) -> String;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DistanceType {
M,
KM,
LightYear,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VelocityType {
MPerSecond,
KmPerHour,
KmPerSecond,
LightSpeed,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Distance {
default_type: DistanceType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Velocity {
default_type: VelocityType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Acceleration {
default_type: AccelerationType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum AccelerationType {
MPerSecond2,
KmPerHour2,
G,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum AngularType {
Rad,
Deg,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Angular {
default_type: AngularType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy, Default)]
pub enum AngularVelocityType {
#[default]
RadperSecond,
DegPerSecond,
RadperHour,
DegperHour,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct AngularVelocity {
default_type: AngularVelocityType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum AngularAccelerationType {
RadperSecond2,
DegPerSecond2,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct AngularAcceleration {
default_type: AngularAccelerationType,
v: f64,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Copy)]
pub struct Coef {
v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum MassType {
Kg,
g,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Mass {
default_type: MassType,
pub v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum AngularMomentumType {
KgM2perSecond, KgKm2perSecond, Nms, MillNms, MicroNms, NanoNms, }
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct AngularMomentum {
default_type: AngularMomentumType,
pub v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum MomentumType {
KgMperSecond, KgKmperSecond, }
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Momentum {
default_type: MomentumType,
pub v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct Vector3<T: VectorQuantity + Default> {
pub x: T,
pub y: T,
pub z: T,
}
impl<T: VectorQuantity + Default + Display> Display for Vector3<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {})", self.x, self.y, self.z)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AreaType {
M2,
KM2,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Area {
default_type: AreaType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum MagneticInductionType {
Gauss,
#[default]
Tesla,
MillTesla, MicroTesla, NanoTesla, MillGauss, KiloGauss, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MagneticInduction {
default_type: MagneticInductionType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MagneticMomentType {
AM2, MillAM2, MicroAM2, NanoAM2, JPerTesla, MillJPerTesla, MicroJPerTesla, NanoJPerTesla, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MagneticMoment {
default_type: MagneticMomentType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TorqueType {
NM, MillNM, MicroNM, NanoNM, KNM, MNM, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Torque {
default_type: TorqueType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EnergyType {
Joule, MillJoule, MicroJoule, NanoJoule, KiloJoule, MegaJoule, ElectronVolt, KiloElectronVolt, MegaElectronVolt, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Energy {
default_type: EnergyType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ForceType {
Newton, MillNewton, MicroNewton, NanoNewton, KiloNewton, MegaNewton, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Force {
default_type: ForceType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PowerType {
Watt, MillWatt, MicroWatt, NanoWatt, KiloWatt, MegaWatt, HorsePower, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Power {
default_type: PowerType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MagneticAngularVelocityType {
TeslaRadPerSecond, MillTeslaRadPerSecond, MicroTeslaRadPerSecond, NanoTeslaRadPerSecond, GaussRadPerSecond, MillGaussRadPerSecond, KiloGaussRadPerSecond, }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MagneticAngularVelocity {
default_type: MagneticAngularVelocityType,
pub v: f64,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum VolumeType {
M3,
KM3,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Volume {
default_type: VolumeType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricCurrentType {
A,
MilliA,
MicroA,
KiloA,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricCurrent {
default_type: ElectricCurrentType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricPotentialType {
V,
MilliV,
MicroV,
KiloV,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricPotential {
default_type: ElectricPotentialType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricResistanceType {
Ohm,
MillOhm,
KiloOhm,
MegaOhm,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricResistance {
default_type: ElectricResistanceType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricChargeType {
Coulomb,
MilliCoulomb,
MicroCoulomb,
NanoCoulomb,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricCharge {
default_type: ElectricChargeType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricCapacitanceType {
Farad,
MilliFarad,
MicroFarad,
NanoFarad,
PicoFarad,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricCapacitance {
default_type: ElectricCapacitanceType,
pub v: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElectricConductanceType {
Siemens,
MilliSiemens,
MicroSiemens,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ElectricConductance {
default_type: ElectricConductanceType,
pub v: f64,
}
impl Default for Distance {
fn default() -> Self {
Distance::from_m(0.0)
}
}
impl PhysicalQuantity for Distance {
fn as_any(&self) -> &dyn Any {
self
}
fn is_zero(&self) -> bool {
self.v == 0.0
}
fn default_unit_value(&self) -> f64 {
self.as_m()
}
fn set_value(&mut self, value: f64) {
self.v = value;
}
fn unit_display_name(&self) -> String {
"Distance (m)".to_string()
}
}
impl Default for Angular {
fn default() -> Self {
Angular::from_rad(0.0)
}
}
impl PhysicalQuantity for Angular {
fn as_any(&self) -> &dyn Any {
self
}
fn is_zero(&self) -> bool {
self.v == 0.0
}
fn default_unit_value(&self) -> f64 {
self.as_rad()
}
fn set_value(&mut self, value: f64) {
self.v = value;
}
fn unit_display_name(&self) -> String {
"Angular (rad)".to_string()
}
}
impl Default for AngularVelocity {
fn default() -> Self {
AngularVelocity::from_rad_per_second(0.0)
}
}
impl Div for AngularMomentum {
type Output = Coef;
fn div(self, rhs: Self) -> Self::Output {
let v = self.default_unit_value() / rhs.default_unit_value();
Coef::new(v)
}
}
impl Default for Coef {
fn default() -> Self {
Coef::new(1.0)
}
}
impl Default for AngularAcceleration {
fn default() -> Self {
AngularAcceleration::from_rad_per_second2(0.0)
}
}
impl<T: VectorQuantity + Default> Default for Vector3<T> {
fn default() -> Self {
Vector3 {
x: T::default(),
y: T::default(),
z: T::default(),
}
}
}
pub fn time_delta_from_secs_f64(secs: f64) -> chrono::TimeDelta {
let nanos = (secs * 1_000_000_000.0) as i64;
chrono::TimeDelta::nanoseconds(nanos)
}
pub fn time_delta_to_secs_f64(td: &chrono::TimeDelta) -> f64 {
td.num_seconds() as f64 + td.subsec_nanos() as f64 * 1e-9
}
macro_rules! impl_display_physics {
($type:ty) => {
impl std::fmt::Display for $type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.default_unit_value())
}
}
};
}
impl_display_physics!(Distance);
impl_display_physics!(Velocity);
impl_display_physics!(Acceleration);
impl_display_physics!(Angular);
impl_display_physics!(AngularVelocity);
impl_display_physics!(AngularAcceleration);
impl_display_physics!(Coef);
impl_display_physics!(Mass);
impl_display_physics!(Momentum);
impl_display_physics!(AngularMomentum);
impl_display_physics!(Area);
impl_display_physics!(Volume);
impl_display_physics!(MagneticInduction);
impl_display_physics!(MagneticMoment);
impl_display_physics!(Torque);
impl_display_physics!(Energy);
impl_display_physics!(Force);
impl_display_physics!(Power);
impl_display_physics!(MagneticAngularVelocity);
impl_display_physics!(ElectricCurrent);
impl_display_physics!(ElectricPotential);
impl_display_physics!(ElectricResistance);
impl_display_physics!(ElectricCharge);
impl_display_physics!(ElectricCapacitance);
impl_display_physics!(ElectricConductance);
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_as_any() {
let g: &dyn PhysicalQuantity = &Acceleration::from_g(1.23);
let _d = g.as_any().downcast_ref::<Acceleration>().unwrap();
let g: &dyn PhysicalQuantity = &Area::from_m2(10.0);
let _d = g.as_any().downcast_ref::<Area>().unwrap();
let g: &dyn PhysicalQuantity = &Distance::from_m(10.0);
let d = g.as_any().downcast_ref::<Distance>().unwrap();
assert_eq!(d.default_type, DistanceType::M);
let g: &dyn PhysicalQuantity = &Velocity::from_m_per_sec(10.0);
let d = g.as_any().downcast_ref::<Velocity>().unwrap();
assert_eq!(d.default_type, VelocityType::MPerSecond);
let g: &dyn PhysicalQuantity = &Acceleration::from_g(1.23);
let d = g.as_any().downcast_ref::<Acceleration>().unwrap();
assert_eq!(d.default_type, AccelerationType::G);
let g: &dyn PhysicalQuantity = &Angular::from_rad(1.23);
let d = g.as_any().downcast_ref::<Angular>().unwrap();
assert_eq!(d.default_type, AngularType::Rad);
let g: &dyn PhysicalQuantity = &AngularVelocity::from_rad_per_hour(1.23);
let d = g.as_any().downcast_ref::<AngularVelocity>().unwrap();
assert_eq!(d.default_type, AngularVelocityType::RadperHour);
let g: &dyn PhysicalQuantity = &Coef::new(1.23);
let d = g.as_any().downcast_ref::<Coef>().unwrap();
assert_eq!(d.v, 1.23);
let g: &dyn PhysicalQuantity = &AngularAcceleration::from_rad_per_second2(1.23);
let d = g.as_any().downcast_ref::<AngularAcceleration>().unwrap();
assert_eq!(d.default_type, AngularAccelerationType::RadperSecond2);
}
#[test]
fn test_acceleration_default() {
let a = Acceleration::default();
assert_eq!(0.0, a.as_m_per_s2());
}
#[test]
fn test_angular_velocity_default() {
let a = AngularVelocity::default();
assert_eq!(0.0, a.as_rad_per_hour());
let a = AngularAcceleration::default();
assert_eq!(0.0, a.as_rad_per_second2());
}
#[test]
fn test_angular_is_zero() {
let a = Angular::from_rad(0.0);
assert_eq!(true, a.is_zero());
let a = Angular::from_rad(1.0);
assert_eq!(false, a.is_zero());
}
#[test]
fn test_volume_clone() {
let a = Volume::from_m3(3.8);
let b = a.clone();
assert_relative_eq!(a.as_km3(), b.as_km3(), epsilon = 1e-8);
}
#[test]
fn test_magnetic_moment_as_any() {
let g: &dyn PhysicalQuantity = &MagneticMoment::from_am2(1.23);
let d = g.as_any().downcast_ref::<MagneticMoment>().unwrap();
assert_eq!(d.default_type, MagneticMomentType::AM2);
}
#[test]
fn test_magnetic_moment_default() {
let mm = MagneticMoment::default();
assert_eq!(0.0, mm.as_am2());
}
#[test]
fn test_magnetic_moment_is_zero() {
let mm = MagneticMoment::from_am2(0.0);
assert_eq!(true, mm.is_zero());
let mm = MagneticMoment::from_am2(1.0);
assert_eq!(false, mm.is_zero());
}
#[test]
fn test_torque_as_any() {
let g: &dyn PhysicalQuantity = &Torque::from_nm(1.23);
let d = g.as_any().downcast_ref::<Torque>().unwrap();
assert_eq!(d.default_type, TorqueType::NM);
}
#[test]
fn test_torque_default() {
let t = Torque::default();
assert_eq!(0.0, t.as_nm());
}
#[test]
fn test_torque_is_zero() {
let t = Torque::from_nm(0.0);
assert_eq!(true, t.is_zero());
let t = Torque::from_nm(1.0);
assert_eq!(false, t.is_zero());
}
#[test]
fn test_energy_as_any() {
let g: &dyn PhysicalQuantity = &Energy::from_joule(1.23);
let d = g.as_any().downcast_ref::<Energy>().unwrap();
assert_eq!(d.default_type, EnergyType::Joule);
}
#[test]
fn test_energy_default() {
let e = Energy::default();
assert_eq!(0.0, e.as_joule());
}
#[test]
fn test_energy_is_zero() {
let e = Energy::from_joule(0.0);
assert_eq!(true, e.is_zero());
let e = Energy::from_joule(1.0);
assert_eq!(false, e.is_zero());
}
#[test]
fn test_angular_default_unit_value_is_rad() {
let a_rad = Angular::from_rad(std::f64::consts::PI);
assert_relative_eq!(a_rad.default_unit_value(), a_rad.as_rad(), epsilon = 1e-12);
let a_deg = Angular::from_deg(180.0);
assert_relative_eq!(a_deg.default_unit_value(), a_deg.as_rad(), epsilon = 1e-12);
let cases = vec![
-720.0, -360.0, -45.5, 0.0, 12.34, 90.0, 123.456, 360.0, 720.0,
];
for deg in cases {
let a = Angular::from_deg(deg);
assert_relative_eq!(a.default_unit_value(), a.as_rad(), epsilon = 1e-12);
}
}
#[test]
fn test_unit_display_name_and_display() {
assert_eq!(Distance::from_m(1.5).unit_display_name(), "Distance (m)");
assert_eq!(format!("{}", Distance::from_m(2.5)), "2.5");
assert_eq!(Velocity::from_m_per_sec(3.0).unit_display_name(), "Velocity (m/s)");
assert_eq!(format!("{}", Velocity::from_km_per_h(36.0)), "10");
assert_eq!(Acceleration::from_m_per_s2(9.8).unit_display_name(), "Acceleration (m/s²)");
assert_eq!(format!("{}", Acceleration::from_g(1.0)), format!("{}", 9.80665));
assert_eq!(Angular::from_rad(1.0).unit_display_name(), "Angular (rad)");
assert_eq!(format!("{}", Angular::from_deg(180.0)), format!("{}", std::f64::consts::PI));
assert_eq!(AngularVelocity::from_rad_per_second(2.0).unit_display_name(), "AngularVelocity (rad/s)");
assert_eq!(format!("{}", AngularVelocity::from_rad_per_second(2.0)), "2");
assert_eq!(AngularAcceleration::from_rad_per_second2(1.0).unit_display_name(), "AngularAcceleration (rad/s²)");
assert_eq!(format!("{}", AngularAcceleration::from_rad_per_second2(3.0)), "3");
assert_eq!(Coef::new(3.14).unit_display_name(), "Coef");
assert_eq!(format!("{}", Coef::new(3.14)), "3.14");
assert_eq!(Mass::from_kg(1.0).unit_display_name(), "Mass (kg)");
assert_eq!(format!("{}", Mass::from_kg(5.0)), "5");
assert_eq!(Momentum::from_kg_m_s(10.0).unit_display_name(), "Momentum (kg·m/s)");
assert_eq!(format!("{}", Momentum::from_kg_m_s(10.0)), "10");
assert_eq!(AngularMomentum::from_kg_m2_per_second(100.0).unit_display_name(), "AngularMomentum (kg·m²/s)");
assert_eq!(format!("{}", AngularMomentum::from_nms(50.0)), "50");
assert_eq!(Area::from_m2(4.0).unit_display_name(), "Area (m²)");
assert_eq!(format!("{}", Area::from_m2(12.0)), "12");
assert_eq!(Volume::from_m3(8.0).unit_display_name(), "Volume (m³)");
assert_eq!(format!("{}", Volume::from_m3(8.0)), "8");
assert_eq!(MagneticInduction::from_tesla(1.0).unit_display_name(), "MagneticInduction (T)");
assert_eq!(format!("{}", MagneticInduction::from_gauss(10000.0)), "1");
assert_eq!(MagneticMoment::from_am2(1.0).unit_display_name(), "MagneticMoment (A·m²)");
assert_eq!(format!("{}", MagneticMoment::from_am2(2.0)), "2");
assert_eq!(Torque::from_nm(10.0).unit_display_name(), "Torque (N·m)");
assert_eq!(format!("{}", Torque::from_nm(0.0)), "0");
assert_eq!(Energy::from_joule(42.0).unit_display_name(), "Energy (J)");
assert_eq!(format!("{}", Energy::from_joule(0.0)), "0");
assert_eq!(Force::from_newton(100.0).unit_display_name(), "Force (N)");
assert_eq!(format!("{}", Force::from_newton(100.0)), "100");
assert_eq!(Power::from_watt(60.0).unit_display_name(), "Power (W)");
assert_eq!(format!("{}", Power::from_watt(60.0)), "60");
assert_eq!(MagneticAngularVelocity::from_tesla_rad_per_second(1.0).unit_display_name(), "MagneticAngularVelocity (T·rad/s)");
assert_eq!(format!("{}", MagneticAngularVelocity::from_tesla_rad_per_second(1.0)), "1");
assert_eq!(ElectricCurrent::from_a(2.5).unit_display_name(), "ElectricCurrent (A)");
assert_eq!(format!("{}", ElectricCurrent::from_a(2.5)), "2.5");
assert_eq!(ElectricPotential::from_v(12.0).unit_display_name(), "ElectricPotential (V)");
assert_eq!(format!("{}", ElectricPotential::from_v(12.0)), "12");
assert_eq!(ElectricResistance::from_ohm(100.0).unit_display_name(), "ElectricResistance (Ohm)");
assert_eq!(format!("{}", ElectricResistance::from_ohm(100.0)), "100");
assert_eq!(ElectricCharge::from_coulomb(5.0).unit_display_name(), "ElectricCharge (C)");
assert_eq!(format!("{}", ElectricCharge::from_coulomb(5.0)), "5");
assert_eq!(ElectricCapacitance::from_farad(0.001).unit_display_name(), "ElectricCapacitance (F)");
assert_eq!(format!("{}", ElectricCapacitance::from_farad(0.001)), "0.001");
assert_eq!(ElectricConductance::from_siemens(0.01).unit_display_name(), "ElectricConductance (S)");
assert_eq!(format!("{}", ElectricConductance::from_siemens(0.01)), "0.01");
}
#[test]
fn test_vector3_display() {
let v: Vector3<Distance> = Vector3::new(
Distance::from_m(1.0),
Distance::from_m(2.0),
Distance::from_m(3.0),
);
assert_eq!(format!("{}", v), "(1, 2, 3)");
}
}
impl VectorQuantity for Distance {}
impl VectorQuantity for Velocity {}
impl VectorQuantity for Acceleration {}
impl VectorQuantity for Angular {}
impl VectorQuantity for AngularVelocity {}
impl VectorQuantity for AngularAcceleration {}
impl VectorQuantity for Coef {}
impl VectorQuantity for AngularMomentum {}
impl VectorQuantity for Momentum {}
impl VectorQuantity for MagneticInduction {}
impl VectorQuantity for MagneticMoment {}
impl VectorQuantity for Torque {}
impl VectorQuantity for Force {}
impl VectorQuantity for MagneticAngularVelocity {}