#![allow(non_snake_case)]
use std::marker::PhantomData;
pub trait Animatable: Sized + Clone {
fn animate(&self, to: &Self, time: f64) -> Self;
}
macro_rules! impl_primitive {
($ty:ident) => {
impl Animatable for $ty {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
if time == 0.0 {
return *self;
}
if (1.0 - time).abs() < f64::EPSILON {
return *to;
}
if self == to {
return *self;
}
crate::utils::check_time(time);
let v = (*self as f64) * (1.0 - time) + (*to as f64) * time;
if *to >= *self {
(v + 0.5) as Self
} else {
(v - 0.5) as Self
}
}
}
};
($ty:ident, float) => {
impl Animatable for $ty {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
if time == 0.0 {
return *self;
}
if (1.0 - time).abs() < f64::EPSILON {
return *to;
}
if (self - to).abs() < $ty::EPSILON {
return *self;
}
crate::utils::check_time(time);
let v = (*self as f64) * (1.0 - time) + (*to as f64) * time;
v as Self
}
}
};
}
impl_primitive!(u8);
impl_primitive!(u16);
impl_primitive!(u32);
impl_primitive!(u64);
impl_primitive!(u128);
impl_primitive!(usize);
impl_primitive!(i8);
impl_primitive!(i16);
impl_primitive!(i32);
impl_primitive!(i64);
impl_primitive!(i128);
impl_primitive!(isize);
impl_primitive!(f32, float);
impl_primitive!(f64, float);
impl Animatable for bool {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
if time < 1.0 {
*self
} else {
*to
}
}
}
impl Animatable for char {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
if self == to {
return *self;
}
let from_idx = *self as u32;
let to_idx = *to as u32;
let idx = from_idx.animate(&to_idx, time);
let n = if from_idx > to_idx {
from_idx - idx
} else {
idx - from_idx
};
let mut rng = *self..=*to;
match rng.nth(n as usize) {
Some(c) => c,
None => *self,
}
}
}
impl Animatable for () {
#[inline]
fn animate(&self, _to: &Self, _time: f64) -> Self {}
}
impl<T> Animatable for PhantomData<T> {
#[inline]
fn animate(&self, _to: &Self, _time: f64) -> Self {
Default::default()
}
}
impl<T: Animatable> Animatable for Option<T> {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
match (self, to) {
(Some(a), Some(b)) => Some(a.animate(b, time)),
_ => None,
}
}
}
impl<T: Animatable, const N: usize> Animatable for [T; N] {
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self {
let mut res = self.clone();
self.iter()
.zip(to.iter())
.zip(res.iter_mut())
.for_each(|((a, b), c)| *c = a.animate(b, time));
res
}
}
macro_rules! impl_tuple {
($($n:tt $name:ident)+) => {
impl<'de, $($name,)+> Animatable for ($($name,)+)
where
$($name: Animatable,)+
{
#[inline]
fn animate(&self, to: &Self, time: f64) -> Self
{
$(
let $name = Animatable::animate(&self.$n, &to.$n, time);
)+
($($name,)+)
}
}
}
}
impl_tuple!(0 T0);
impl_tuple!(0 T0 1 T1);
impl_tuple!(0 T0 1 T1 2 T2);
impl_tuple!(0 T0 1 T1 2 T2 3 T3);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15);
#[cfg(test)]
mod test {
use crate::Animatable;
#[test]
fn test_bool() {
let v = false.animate(&true, 0.0);
assert!(v == false);
let v = false.animate(&true, 0.5);
assert!(v == false);
let v = false.animate(&true, 1.0);
assert!(v == true);
let v = true.animate(&true, 0.3);
assert!(v == true);
let v = false.animate(&false, 0.2);
assert!(v == false);
}
#[test]
fn test_char() {
let v = 'a'.animate(&'e', 0.0);
assert_eq!(v, 'a');
let v = 'a'.animate(&'e', 0.5);
assert_eq!(v, 'c');
let v = 'a'.animate(&'e', 0.555);
assert_eq!(v, 'c');
let v = 'a'.animate(&'e', 1.0);
assert_eq!(v, 'e');
}
}