use std::ops::*; pub const trait LerpUtil { fn lerp(self, from: Self, to: Self) -> Self; fn lerp_inv(self, from: Self, to: Self) -> Self; } pub const trait DivOr { fn div_or(self, rhs: Self, other: Self) -> Self; } impl const DivOr for f32 { fn div_or(self, rhs: Self, other: Self) -> Self { let res = self / rhs; if res.is_nan() { other } else { res } } } impl + const Sub + const Mul + const DivOr + Copy> const LerpUtil for T { /// linear interpolation /// from * (1.0 - self) + to * self fn lerp(self, from: Self, to: Self) -> Self { from + (to - from) * self } /// inverse of lerp fn lerp_inv(self, from: Self, to: Self) -> Self { (self - from).div_or(to - from, from) } } macro_rules! impl_op { ($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => { #[allow(non_snake_case)] mod ${concat($T, _op_, $fn, _impl)} { use super::*; #[allow(unused_imports)] use std::ops::*; impl const $op for $T { type Output = Self; fn $fn(self, rhs: Self) -> Self::Output { Self { $($field: self.$field.$fn(rhs.$field),)* } } } impl const $opa for $T { fn $fna(&mut self, rhs: Self) { $(self.$field.$fna(rhs.$field);)* } } impl const $op for $T { type Output = Self; fn $fn(self, rhs: f32) -> Self::Output { Self { $($field: self.$field.$fn(rhs),)* } } } impl const $op<$T> for f32 { type Output = $T; fn $fn(self, rhs: $T) -> Self::Output { $T { $($field: self.$fn(rhs.$field),)* } } } impl const $opa for $T { fn $fna(&mut self, rhs: f32) { $(self.$field.$fna(rhs);)* } } } }; ($T:ident $op:ident $fn:ident; $($field:ident)*) => { impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); }; (impl $op:ident for $T:ident: $fn:ident $($field:ident)*) => { impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); }; } pub(crate) use impl_op;