use core::fmt::{ Display, Debug };
use num_traits::{ Num, Signed, Float, FloatConst, PrimInt, NumCast };
pub const EPSILON: f64 = 0.00001;
pub trait ScalarConstants {
const ZERO: Self;
const ONE: Self;
}
pub trait SignedScalarConstants {
const NEG_ONE: Self;
}
pub trait Scalar: Clone + Copy + Num + ScalarConstants + Default + PartialOrd + Display + Debug + NumCast {
fn min(self, other: Self) -> Self {
if self < other {
self
} else {
other
}
}
fn max(self, other: Self) -> Self {
if self > other {
self
} else {
other
}
}
fn clamp(self, min: Self, max: Self) -> Self {
self.min(max).max(min)
}
fn lerp(self, other: Self, t: Self) -> Self {
self + (other - self) * t
}
}
pub trait IntScalar<T: IntScalar<T>>: Scalar + Ord + PrimInt + IntUnique<T> {}
pub trait SignedScalar: Scalar + Signed + SignedScalarConstants {
fn bezier_derivative(self, control_1: Self, control_2: Self, terminal: Self, t: Self) -> Self {
let t_3: Self = Self::from(3).unwrap();
let t_6: Self = Self::from(6).unwrap();
let omt: Self = Self::one() - t;
let omt2: Self = omt * omt;
let t2: Self = t * t;
(control_1 - self) * t_3 * omt2 + (control_2 - control_1) * t_6 * omt * t + (terminal - control_2) * t_3 * t2
}
fn bezier_sample(self, control_1: Self, control_2: Self, terminal: Self, t: Self) -> Self {
let t_3: Self = Self::from(3).unwrap();
let omt: Self = Self::one() - t;
let omt2: Self = omt * omt;
let omt3: Self = omt2 * omt;
let t2: Self = t * t;
let t3: Self = t2 * t;
self * omt3 + control_1 * omt2 * t * t_3 + control_2 * omt * t2 * t_3 + terminal * t3
}
fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: Self) -> Self {
let t_05: Self = Self::from(0.5).unwrap();
let t_2: Self = Self::from(2.0).unwrap();
let t_3: Self = Self::from(3.0).unwrap();
let t_4: Self = Self::from(4.0).unwrap();
let t_5: Self = Self::from(5.0).unwrap();
t_05 * (
(self * t_2) +
(-pre_a + b) * weight +
(t_2 * pre_a - t_5 * self + t_4 * b - post_b) * (weight * weight) +
(-pre_a + t_3 * self - t_3 * b + post_b) * (weight * weight * weight)
)
}
fn cubic_interpolate_in_time(self, b: Self, pre_a: Self, post_b: Self, weight: Self, b_t: Self, pre_a_t: Self, post_b_t: Self) -> Self {
let t_0: Self = Self::zero();
let t_05: Self = Self::from(0.5).unwrap();
let t_1: Self = Self::one();
let t: Self = t_0.lerp(b_t, weight);
let a1: Self = pre_a.lerp(self, if pre_a_t == t_0 { t_0 } else { (t - pre_a_t) / -pre_a_t });
let a2: Self = self.lerp(b, if b_t == t_0 { t_05 } else { t / b_t });
let a3: Self = b.lerp(post_b, if post_b_t - b_t == t_0 { t_1 } else { (t - b_t) / (post_b_t - b_t) });
let b1: Self = a1.lerp(a2, if b_t - pre_a_t == t_0 { t_0 } else { (t - pre_a_t) / (b_t - pre_a_t) });
let b2: Self = a2.lerp(a3, if post_b_t == t_0 { t_1 } else { t / post_b_t });
b1.lerp(b2, if b_t == t_0 { t_05 } else { t / b_t })
}
}
pub trait FloatScalar: SignedScalar + Float + FloatConst {
fn fract(self) -> Self {
self - self.floor()
}
fn inv_sqrt(self) -> Self {
Self::one() / self.sqrt()
}
fn smoothstep(self, a: Self, b: Self) -> Self {
let t_2: Self = Self::from(2).unwrap();
let t_3: Self = Self::from(3).unwrap();
let y: Self = (self - a) / Scalar::clamp(b - a, Self::zero(), Self::one());
y * y * (t_3 - (t_2 * y))
}
fn approx_eq(self, other: Self) -> bool {
if self == other {
return true;
}
let epsilon: Self = Self::from(EPSILON).unwrap();
let mut tolerance: Self = epsilon * self.abs();
if tolerance < epsilon {
tolerance = epsilon;
}
(self - other).abs() < tolerance
}
fn approx_zero(self) -> bool {
self.approx_eq(Self::zero())
}
}
pub trait IntUnique<T: IntScalar<T>> {
fn ilog(self, base: T) -> T;
}
impl <T: Clone + Copy + Num + ScalarConstants + Default + PartialOrd + Display + Debug + NumCast> Scalar for T {}
impl <T: Scalar + Ord + PrimInt + IntUnique<T>> IntScalar<T> for T {}
impl <T: Scalar + Signed + SignedScalarConstants> SignedScalar for T {}
impl <T: SignedScalar + Float + FloatConst> FloatScalar for T {}
impl ScalarConstants for u8 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<u8> for u8 {
fn ilog(self, base: u8) -> u8 {
self.ilog(base) as u8
}
}
impl ScalarConstants for u16 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<u16> for u16 {
fn ilog(self, base: u16) -> u16 {
self.ilog(base) as u16
}
}
impl ScalarConstants for u32 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<u32> for u32 {
fn ilog(self, base: u32) -> u32 {
self.ilog(base) as u32
}
}
impl ScalarConstants for u64 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<u64> for u64 {
fn ilog(self, base: u64) -> u64 {
self.ilog(base) as u64
}
}
impl ScalarConstants for u128 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<u128> for u128 {
fn ilog(self, base: u128) -> u128 {
self.ilog(base) as u128
}
}
impl ScalarConstants for usize {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl IntUnique<usize> for usize {
fn ilog(self, base: usize) -> usize {
self.ilog(base) as usize
}
}
impl ScalarConstants for isize {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for isize {
const NEG_ONE: Self = -1;
}
impl IntUnique<isize> for isize {
fn ilog(self, base: isize) -> isize {
self.ilog(base) as isize
}
}
impl ScalarConstants for i8 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for i8 {
const NEG_ONE: Self = -1;
}
impl IntUnique<i8> for i8 {
fn ilog(self, base: i8) -> i8 {
self.ilog(base) as i8
}
}
impl ScalarConstants for i16 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for i16 {
const NEG_ONE: Self = -1;
}
impl IntUnique<i16> for i16 {
fn ilog(self, base: i16) -> i16 {
self.ilog(base) as i16
}
}
impl ScalarConstants for i32 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for i32 {
const NEG_ONE: Self = -1;
}
impl IntUnique<i32> for i32 {
fn ilog(self, base: i32) -> i32 {
self.ilog(base) as i32
}
}
impl ScalarConstants for i64 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for i64 {
const NEG_ONE: Self = -1;
}
impl IntUnique<i64> for i64 {
fn ilog(self, base: i64) -> i64 {
self.ilog(base) as i64
}
}
impl ScalarConstants for i128 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
impl SignedScalarConstants for i128 {
const NEG_ONE: Self = -1;
}
impl IntUnique<i128> for i128 {
fn ilog(self, base: i128) -> i128 {
self.ilog(base) as i128
}
}
impl ScalarConstants for f32 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
}
impl SignedScalarConstants for f32 {
const NEG_ONE: Self = -1.0;
}
impl ScalarConstants for f64 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
}
impl SignedScalarConstants for f64 {
const NEG_ONE: Self = -1.0;
}