// -*- coding: utf-8 -*- // // Copyright 2021 Michael Büsch // // Licensed under the Apache License version 2.0 // or the MIT license, at your option. // SPDX-License-Identifier: Apache-2.0 OR MIT // #![allow(clippy::unnecessary_cast)] use paste::paste; use std::f64::consts::PI; use std::ops::{ Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; #[derive(Copy, Clone, PartialEq, Debug)] pub enum Axis { X = 0, Y = 1, Z = 2, } pub const NR_AXES: usize = 3; pub fn all_axes() -> [Axis; NR_AXES] { [Axis::X, Axis::Y, Axis::Z] } impl std::fmt::Display for Axis { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Axis::X => write!(f, "X"), Axis::Y => write!(f, "Y"), Axis::Z => write!(f, "Z"), } } } #[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction { Neg = 0, Pos = 1, } pub const NR_DIRS: usize = 2; impl std::fmt::Display for Direction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Direction::Neg => write!(f, "negative direction"), Direction::Pos => write!(f, "positive direction"), } } } #[derive(Copy, Clone, PartialEq, Debug)] pub enum AccelMode { Accelerate = 0, Decelerate = 1, } impl std::fmt::Display for AccelMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AccelMode::Accelerate => write!(f, "accelerate"), AccelMode::Decelerate => write!(f, "decelerate"), } } } type PhyBaseType = f64; const PHY_EPSILON: PhyBaseType = (f32::EPSILON * 2.0) as PhyBaseType; const PHY_INFINITE: PhyBaseType = PhyBaseType::INFINITY; #[derive(Copy, Clone, Debug)] pub struct PhyType(PhyBaseType); impl PhyType { pub const EPSILON: PhyType = PhyType(PHY_EPSILON); pub const INFINITE: PhyType = PhyType(PHY_INFINITE); pub const UNLIMITED: PhyType = PhyType(PHY_INFINITE); pub const fn new(value: PhyBaseType) -> PhyType { PhyType(value) } pub fn phy_eq(&self, other: PhyType) -> bool { if self.is_finite() && other.is_finite() { (**self - *other).abs() < PHY_EPSILON } else { **self == *other } } pub fn phy_ne(&self, other: PhyType) -> bool { !self.phy_eq(other) } pub fn rot_eq(&self, other: PhyType) -> bool { if self.is_finite() && other.is_finite() { let maxval = *radians(360.into()); let absdiff = (*self - other).abs(); (absdiff < PHY_EPSILON) || (absdiff > (maxval - PHY_EPSILON)) } else { **self == *other } } pub fn rot_ne(&self, other: PhyType) -> bool { !self.rot_eq(other) } pub fn is_unlimited(&self) -> bool { (*self).is_infinite() } } impl std::fmt::Display for PhyType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(precision) = f.precision() { write!(f, "{:.*}", precision, self.0) } else { write!(f, "{:.3}", self.0) } } } impl Deref for PhyType { type Target = PhyBaseType; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for PhyType { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl PartialEq for PhyType { fn eq(&self, other: &Self) -> bool { self.phy_eq(*other) } } impl PartialOrd for PhyType { fn partial_cmp(&self, other: &Self) -> Option { if self.eq(other) { Some(std::cmp::Ordering::Equal) } else if self.0 < other.0 { Some(std::cmp::Ordering::Less) } else if self.0 > other.0 { Some(std::cmp::Ordering::Greater) } else { None } } } impl Ord for PhyType { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.partial_cmp(other).expect("Unable to compare PhyType.") } } impl Eq for PhyType {} impl Neg for PhyType { type Output = Self; fn neg(self) -> Self::Output { (-*self).into() } } impl Add for PhyType { type Output = Self; fn add(self, other: Self) -> Self::Output { (*self + *other).into() } } impl AddAssign for PhyType { fn add_assign(&mut self, other: Self) { *self = PhyType::new(**self + *other); } } impl Sub for PhyType { type Output = Self; fn sub(self, other: Self) -> Self::Output { (*self - *other).into() } } impl SubAssign for PhyType { fn sub_assign(&mut self, other: Self) { *self = PhyType::new(**self - *other); } } impl Mul for PhyType { type Output = Self; fn mul(self, other: Self) -> Self::Output { (*self * *other).into() } } impl MulAssign for PhyType { fn mul_assign(&mut self, other: Self) { *self = PhyType::new(**self * *other); } } impl Div for PhyType { type Output = Self; fn div(self, other: Self) -> Self::Output { (*self / *other).into() } } impl DivAssign for PhyType { fn div_assign(&mut self, other: Self) { *self = PhyType::new(**self / *other); } } impl Rem for PhyType { type Output = Self; fn rem(self, other: Self) -> Self::Output { (*self % *other).into() } } impl RemAssign for PhyType { fn rem_assign(&mut self, other: Self) { *self = PhyType::new(**self % *other); } } macro_rules! gen_constructors { ($($x:ty),*) => { paste! { $( impl PhyType { pub const fn [](value: $x) -> PhyType { PhyType::new(value as PhyBaseType) } } impl From<$x> for PhyType { fn from(value: $x) -> Self { PhyType::[](value) } } )* } } } gen_constructors!(f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); pub fn radians(deg: PhyType) -> PhyType { deg * PhyType::new(PI as PhyBaseType / 180.0 as PhyBaseType) } pub fn degrees(rad: PhyType) -> PhyType { rad * PhyType::new(180.0 as PhyBaseType / PI as PhyBaseType) } pub fn kmph_mps(kmph: PhyType) -> PhyType { kmph * PhyType::new_f64(1000.0 / (60.0 * 60.0)) } pub fn mps_kmph(mps: PhyType) -> PhyType { mps * PhyType::new_f64((60.0 * 60.0) / 1000.0) } #[cfg(test)] mod tests { use super::*; #[test] fn test_phytype() { // Eq let a: PhyType = 1.0.into(); let b: PhyType = 1.0.into(); assert!(a == b); assert!(!(a != b)); assert_eq!(a.0, 1.0); assert_eq!(b.0, 1.0); // inf let a: PhyType = 1.0.into(); let b: PhyType = PhyType::UNLIMITED; assert!(!a.is_unlimited()); assert!(b.is_unlimited()); assert!(a != b); assert!(!(a == b)); let a: PhyType = 1.0.into(); let b: PhyType = -PhyType::UNLIMITED; assert!(!a.is_unlimited()); assert!(b.is_unlimited()); assert!(a != b); assert!(!(a == b)); let a: PhyType = PhyType::UNLIMITED; let b: PhyType = -PhyType::UNLIMITED; assert!(a.is_unlimited()); assert!(b.is_unlimited()); assert!(a != b); assert!(!(a == b)); let a: PhyType = -PhyType::UNLIMITED; let b: PhyType = PhyType::UNLIMITED; assert!(a.is_unlimited()); assert!(b.is_unlimited()); assert!(a != b); assert!(!(a == b)); let a: PhyType = PhyType::UNLIMITED; let b: PhyType = PhyType::UNLIMITED; assert!(a.is_unlimited()); assert!(b.is_unlimited()); assert!(a == b); assert!(!(a != b)); let a: PhyType = -PhyType::UNLIMITED; let b: PhyType = -PhyType::UNLIMITED; assert!(a.is_unlimited()); assert!(b.is_unlimited()); assert!(a == b); assert!(!(a != b)); // Not eq let a: PhyType = 1.0.into(); let b: PhyType = (1.0 + PHY_EPSILON).into(); assert!(a != b); assert!(!(a == b)); assert_eq!(a.0, 1.0); assert_eq!(b.0, 1.0 + PHY_EPSILON); // Less let a: PhyType = 1.0.into(); let b: PhyType = (1.0 + PHY_EPSILON).into(); assert!(a < b); assert!(!(a >= b)); // Greater let a: PhyType = (1.0 + PHY_EPSILON).into(); let b: PhyType = 1.0.into(); assert!(a > b); assert!(!(a <= b)); // Neg let a: PhyType = 1.0.into(); let b: PhyType = -a; assert_eq!(b, (-1.0).into()); // Add let a: PhyType = 1.0.into(); let b: PhyType = 2.0.into(); assert_eq!(a + b, 3.0.into()); // AddAssign let mut a: PhyType = 1.0.into(); a += 2.0.into(); assert_eq!(a, 3.0.into()); // Sub let a: PhyType = 1.0.into(); let b: PhyType = 3.0.into(); assert_eq!(a - b, (-2.0).into()); // SubAssign let mut a: PhyType = 1.0.into(); a -= 3.0.into(); assert_eq!(a, (-2.0).into()); // Mul let a: PhyType = 2.0.into(); let b: PhyType = 3.0.into(); assert_eq!(a * b, 6.0.into()); // MulAssign let mut a: PhyType = 2.0.into(); a *= 3.0.into(); assert_eq!(a, 6.0.into()); // Div let a: PhyType = 1.0.into(); let b: PhyType = 2.0.into(); assert_eq!(a / b, 0.5.into()); // DivAssign let mut a: PhyType = 1.0.into(); a /= 2.0.into(); assert_eq!(a, 0.5.into()); // Rem let a: PhyType = 5.0.into(); let b: PhyType = 2.0.into(); assert_eq!(a % b, 1.0.into()); // RemAssign let mut a: PhyType = 5.0.into(); a %= 2.0.into(); assert_eq!(a, 1.0.into()); // From f32 let a: PhyType = 1.0f32.into(); let b: PhyType = (-1.0f32).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From f64 let a: PhyType = 1.0f64.into(); let b: PhyType = (-1.0f64).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From i8 let a: PhyType = 1i8.into(); let b: PhyType = (-1i8).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From i16 let a: PhyType = 1i16.into(); let b: PhyType = (-1i16).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From i32 let a: PhyType = 1i32.into(); let b: PhyType = (-1i32).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From i64 let a: PhyType = 1i64.into(); let b: PhyType = (-1i64).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From i128 let a: PhyType = 1i128.into(); let b: PhyType = (-1i128).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From isize let a: PhyType = 1isize.into(); let b: PhyType = (-1isize).into(); assert_eq!(a.0, 1.0); assert_eq!(b.0, -1.0); // From u8 let a: PhyType = 1u8.into(); assert_eq!(a.0, 1.0); // From u16 let a: PhyType = 1u16.into(); assert_eq!(a.0, 1.0); // From u32 let a: PhyType = 1u32.into(); assert_eq!(a.0, 1.0); // From u64 let a: PhyType = 1u64.into(); assert_eq!(a.0, 1.0); // From u128 let a: PhyType = 1u128.into(); assert_eq!(a.0, 1.0); // From usize let a: PhyType = 1usize.into(); assert_eq!(a.0, 1.0); } #[test] fn test_degrees() { assert_eq!(radians(0.into()), 0.into()); assert_eq!(radians(90.into()), (PI / 2.0).into()); assert_eq!(radians(180.into()), PI.into()); assert_eq!(radians((-90).into()), (-PI / 2.0).into()); assert_eq!(radians((-180).into()), (-PI).into()); assert_eq!(degrees(0.into()), 0.into()); assert_eq!(degrees((PI / 2.0).into()), 90.into()); assert_eq!(degrees(PI.into()), 180.into()); assert_eq!(degrees((-PI / 2.0).into()), (-90).into()); assert_eq!(degrees((-PI).into()), (-180).into()); } #[test] fn test_mps() { assert_eq!(kmph_mps(36.into()), 10.into()); assert_eq!(kmph_mps(PhyType::INFINITE), PhyType::INFINITE); assert_eq!(kmph_mps(-PhyType::INFINITE), -PhyType::INFINITE); assert_eq!(mps_kmph(10.into()), 36.into()); assert_eq!(mps_kmph(PhyType::INFINITE), PhyType::INFINITE); assert_eq!(mps_kmph(-PhyType::INFINITE), -PhyType::INFINITE); } } // vim: ts=4 sw=4 expandtab