#[cfg(feature = "glam")]
use glam::{vec2, vec3, Vec2, Vec3};
use core::option::Option;
pub use lerpable_derive::Lerpable;
pub fn step<T: Clone, LerpMethod>(this: &T, other: &T, pct: &LerpMethod) -> T
where
LerpMethod: IsLerpingMethod,
{
if pct.has_lerp_stepped() {
other.clone()
} else {
this.clone()
}
}
pub fn lerp<T, LerpMethod>(start: T, end: T, pct: &LerpMethod) -> T
where
T: std::ops::Mul<f64, Output = T> + std::ops::Add<Output = T>,
f64: std::ops::Mul<T, Output = T>,
LerpMethod: IsLerpingMethod,
{
let pct = pct.lerp_pct();
(1.0 - pct) * start + pct * end
}
pub fn lerp_vecs<T, LerpMethod>(this: &[T], other: &[T], pct: &LerpMethod) -> Vec<T>
where
T: Clone + Lerpable,
LerpMethod: IsLerpingMethod,
{
let mut v = vec![];
let this_len = this.len();
let other_len = other.len();
let count = if this_len == other_len {
this_len
} else {
lerp(this_len as f64, other_len as f64, pct).round() as usize
};
for i in 0..count {
let result = match (i >= this_len, i >= other_len) {
(true, true) => unreachable!(),
(true, false) => {
let emerge_pct = pct.partial_lerp_pct(i, count);
other[i].lerp_partial(emerge_pct)
}
(false, true) => {
let emerge_pct = pct.partial_lerp_pct(i, count);
this[i].lerp_partial(emerge_pct)
}
(false, false) => this[i].lerpify(&other[i], pct),
};
v.push(result);
}
v
}
pub trait IsLerpingMethod: Clone {
fn has_lerp_stepped(&self) -> bool;
fn partial_lerp_pct(&self, i: usize, total: usize) -> f64;
fn lerp_pct(&self) -> f64;
fn with_lerp_pct(&self, pct: f64) -> Self; }
impl IsLerpingMethod for f64 {
fn has_lerp_stepped(&self) -> bool {
*self > 0.5
}
fn lerp_pct(&self) -> f64 {
*self
}
fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
*self * total as f64 - i as f64
}
fn with_lerp_pct(&self, pct: f64) -> Self {
pct
}
}
impl IsLerpingMethod for f32 {
fn has_lerp_stepped(&self) -> bool {
*self > 0.5
}
fn lerp_pct(&self) -> f64 {
*self as f64
}
fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
*self as f64 * total as f64 - i as f64
}
fn with_lerp_pct(&self, pct: f64) -> Self {
pct as f32
}
}
pub trait Lerpable: Sized + Clone {
fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self;
fn lerp_partial<T: IsLerpingMethod>(&self, _pct: T) -> Self {
self.clone()
}
}
macro_rules! impl_lerpable {
($t:ty) => {
impl Lerpable for $t {
fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self {
lerp(*self as f64, *other as f64, pct) as $t
}
}
};
}
impl_lerpable!(usize);
impl_lerpable!(u8);
impl_lerpable!(u16);
impl_lerpable!(u64);
impl_lerpable!(i32);
impl_lerpable!(i64);
impl_lerpable!(f32);
impl_lerpable!(f64);
impl<T: Lerpable + Clone> Lerpable for Vec<T> {
fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
if self.is_empty() || other.is_empty() {
return self.clone();
}
lerp_vecs(self, other, method)
}
}
impl<T: Lerpable + Clone> Lerpable for Option<T> {
fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
match (self, other) {
(Option::None, Option::Some(_)) => {
if method.lerp_pct() < 0.5 {
None
} else {
self.clone()
}
}
(Option::Some(_), Option::None) => {
if method.lerp_pct() < 0.5 {
self.clone()
} else {
None
}
}
(Option::Some(a), Option::Some(b)) => Some(a.lerpify(b, method)),
(Option::None, Option::None) => None,
}
}
}
impl Lerpable for bool {
fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
step(self, other, method)
}
}
impl Lerpable for String {
fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
step(self, other, method)
}
}
#[cfg(feature = "glam")]
impl Lerpable for Vec2 {
fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
vec2(
self.x.lerpify(&other.x, method),
self.y.lerpify(&other.y, method),
)
.into()
}
}
#[cfg(feature = "glam")]
impl Lerpable for Vec3 {
fn lerpify<T: IsLerpingMethod>(&self, other: &Self, method: &T) -> Self {
vec3(
self.x.lerpify(&other.x, method),
self.y.lerpify(&other.y, method),
self.z.lerpify(&other.z, method),
)
.into()
}
}