pub trait Float : num::Float + std::fmt::Debug + num::NumCast + std::ops::AddAssign + std::ops::MulAssign + 'static {
fn deg2rad(self) -> Self {
self.to_radians()
}
fn rad2deg(self) -> Self {
self.to_degrees()
}
fn pi() -> Self;
fn two() -> Self;
fn four() -> Self;
fn half()-> Self;
fn two_pi() -> Self { Self::two() * Self::pi() }
fn half_pi() -> Self { Self::half() * Self::pi() }
fn rad2rad(self) -> Self {
if self < -Self::pi() || self > Self::pi() {
self.rad2similar(Self::zero())
} else {
self
}
}
fn rad2similar(self, similar: Self) -> Self {
let delta = self - similar;
self - Self::two_pi() * (delta / Self::two_pi() + Self::half()).floor()
}
fn rad_diff(self, other: Self) -> Self {
(self - other).rad2rad()
}
fn rad_sum(self, other: Self) -> Self {
(self + other).rad2rad()
}
fn safe_acos(self) -> Self {
if self >= Self::one() {
if self > Self::from(1.01).unwrap() {
warn!("safe_acos: applied on divergent argument {:?}", self);
}
Self::zero()
} else if self <= -Self::one() {
if self < Self::from(-1.01).unwrap() {
warn!("safe_acos: applied on divergent argument {:?}", self)
}
Self::pi()
} else {
self.acos()
}
}
fn fast_atan(self) -> Self {
let half_pi = Self::from(1.5707963267948966e+00).unwrap();
let sixth_pi = Self::from(5.2359877559829882e-01).unwrap();
let tan_sixth_pi = Self::from(5.7735026918962573e-01).unwrap();
let tan_twelfth_pi = Self::from(2.6794919243112270e-01).unwrap();
let c1 = Self::from(1.6867629106).unwrap();
let c2 = Self::from(0.4378497304).unwrap();
let c3 = Self::from(1.6867633134).unwrap();
let mut x = self;
let mut y;
if x < Self::zero() {
x = -x;
if x > Self::one() {
x = x.recip();
if x > tan_twelfth_pi {
x = (x - tan_sixth_pi) / (Self::one() + tan_sixth_pi * x);
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
y = y + sixth_pi;
} else {
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
}
y = half_pi - y;
} else {
if x > tan_twelfth_pi {
x = (x - tan_sixth_pi) / (Self::one() + tan_sixth_pi * x);
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
y = y + sixth_pi;
} else {
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
}
}
return -y;
} else {
if x > Self::one() {
x = x.recip();
if x > tan_twelfth_pi {
x = (x - tan_sixth_pi) / (Self::one() + tan_sixth_pi * x);
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
y = y + sixth_pi;
} else {
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
}
y = half_pi - y;
} else {
if x > tan_twelfth_pi {
x = (x - tan_sixth_pi) / (Self::one() + tan_sixth_pi * x);
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
y = y + sixth_pi;
} else {
let x2 = x * x;
y = x * (c1 + x2 * c2) / (c3 + x2);
}
}
return y;
}
}
fn clamp(self, low: Self, high: Self) -> Self {
if self < low {
return low;
} else if self > high {
return high;
} else {
return self;
}
}
}
impl Float for f32 {
fn pi() -> f32 { std::f32::consts::PI }
fn two() -> f32 { 2.0f32 }
fn four() -> f32 { 4.0f32 }
fn half() -> f32 { 0.5f32 }
}
impl Float for f64 {
fn pi() -> f64 { std::f64::consts::PI }
fn two() -> f64 { 2.0f64 }
fn four() -> f64 { 4.0f64 }
fn half() -> f64 { 0.5f64 }
}
pub fn deg2rad<T: Float>(v: T) -> T { v.deg2rad() }
pub fn rad2deg<T: Float>(v: T) -> T { v.rad2deg() }
pub fn rad2rad<T: Float>(v: T) -> T { v.rad2rad() }
pub fn rad_diff<T: Float>(a: T, b: T) -> T { a.rad_diff(b) }
pub fn rad_sum<T: Float>(a: T, b: T) -> T { a.rad_sum(b) }
pub fn atan2<T: Float>(y: T, x: T) -> T { y.atan2(x) }
pub fn fast_atan2<T: Float>(y: T, x: T) -> T {
if x > T::zero() {
return (y / x).fast_atan();
} else if x == T::zero() {
if y == T::zero() {
return T::zero();
}
return T::half_pi().copysign(y);
} else { if y >= T::zero() {
return (y / x).fast_atan() + T::pi();
} else {
return (y / x).fast_atan() - T::pi();
}
}
}
macro_rules! left_side_scalar_multiplication {
($right_side_type:ty, $scalar_type:ty) => {
impl ops::Mul<$right_side_type> for $scalar_type
{
type Output = $right_side_type;
fn mul(self, _rhs: $right_side_type) -> $right_side_type {
_rhs * self
}
}
}
}
pub(crate) use left_side_scalar_multiplication;
#[cfg(test)]
mod tests {
use super::*;
fn equal<T: Float>(a: T, b: T) -> bool {
(a - b).abs() < T::from(10).unwrap() * (a.abs() + b.abs() + T::epsilon()) * T::epsilon()
}
#[test]
fn test() {
assert!( equal(1.0, 1.000000000000001));
assert!(!equal(1.0, 1.00000000000001));
assert_eq!(deg2rad(180.0), f64::pi());
assert_eq!(deg2rad(-180.0), -f64::pi());
assert_eq!(rad2deg(f64::pi()), 180.0);
assert_eq!(rad2deg(-f64::pi()), -180.0);
assert_eq!(-0.25.rad2similar(0.01), -0.25);
assert_eq!(1.1.rad2similar(100.0), 1.1 + 16.0 * f64::two_pi());
assert_eq!(1.1.rad2similar(-100.0), 1.1 - 16.0 * f64::two_pi());
assert!(equal((2.0*f64::pi() + 0.12).rad2rad(), 0.12));
assert!(equal((-2.0*f64::pi() - 0.12).rad2rad(), -0.12));
assert!(equal(rad2rad(7.0 * f64::pi() + 0.5), -f64::pi() + 0.5));
assert!(equal(rad2rad(0.0), 0.0));
assert!(equal(rad_diff(0.1, 2.0 * f64::pi() + 0.1), 0.0));
assert!(equal(rad_diff(0.1, -0.1), 0.2));
assert!(equal(rad_diff(f64::pi() - 0.1, -f64::pi() + 0.1), -0.2)); assert!(equal(rad_diff(0.0, f64::pi()), -f64::pi()));
assert!(equal(rad_sum(f64::pi() - 0.1, 0.2), -f64::pi() + 0.1)); assert!(equal(rad_sum(-f64::pi() + 0.1, -0.2), f64::pi() - 0.1)); assert!(equal(rad_sum(0.0, 0.0), 0.0));
assert!(equal(rad_sum(1.0, -1.0), 0.0));
assert_eq!(f64::two(), 2.0);
assert_eq!(f64::four(), 4.0);
assert_eq!(f64::half(), 0.5);
assert_eq!(1.1.safe_acos(), 0.0);
assert_eq!(0.0.safe_acos(), f64::half_pi());
assert_eq!((-1.1).safe_acos(), f64::pi());
}
#[test]
fn fast_atan() {
let max_value = 10000.0;
let iters = 100000;
let mut max_err : f64 = 0.0;
let mut worst_x : f64 = 0.0;
for n in -iters..=iters {
let x = n as f64 / iters as f64 * max_value;
let err = x.atan() - x.fast_atan();
if err.abs() > max_err.abs() {
max_err = err;
worst_x = x;
}
}
println!("fast_atan max error: atan({:?}) err={:?} degrees", worst_x, max_err.rad2deg());
assert!(max_err.abs() < 1e-6);
}
#[test]
fn test_fast_atan2() {
let max_value = 10.0;
let iters = 10;
let mut max_err : f64 = 0.0;
let mut worst_x : f64 = 0.0;
let mut worst_y : f64 = 0.0;
for m in -iters..=iters {
let y = m as f64 / iters as f64 * max_value;
for n in -iters..=iters {
if n == 0 && m == 0 {
continue;
}
let x = n as f64 / iters as f64 * max_value;
let err = atan2(y, x) - fast_atan2(y, x);
if err.abs() > max_err.abs() {
max_err = err;
worst_x = x;
worst_y = y;
}
}
}
println!("fast_atan2 max error: atan2({:?}, {:?}) is {:?} degrees", worst_y, worst_x, max_err.rad2deg());
assert!(max_err.abs() < 1e-6);
}
}