use core::ops::{Add, Mul, RangeInclusive, Sub};
#[cfg(any(feature = "std", feature = "libm"))]
use num_traits::Float;
use num_traits::{float::FloatCore, Num, One, Zero};
use crate::{constant, fun, id};
pub trait Fun {
type T;
type V;
fn eval(&self, t: Self::T) -> Self::V;
}
impl<'a, F> Fun for &'a F
where
F: Fun,
{
type T = F::T;
type V = F::V;
fn eval(&self, t: Self::T) -> Self::V {
(*self).eval(t)
}
}
#[derive(Clone, Debug)]
pub struct Anim<F>(pub F);
impl<F> Anim<F>
where
F: Fun,
{
pub fn eval(&self, t: F::T) -> F::V {
self.0.eval(t)
}
pub fn map<W>(self, f: impl Fn(F::V) -> W) -> Anim<impl Fun<T = F::T, V = W>> {
self.map_anim(fun(f))
}
pub fn map_time<S>(self, f: impl Fn(S) -> F::T) -> Anim<impl Fun<T = S, V = F::V>> {
fun(f).map_anim(self)
}
pub fn as_ref(&self) -> Anim<&F> {
Anim(&self.0)
}
pub fn map_anim<W, G, A>(self, anim: A) -> Anim<impl Fun<T = F::T, V = W>>
where
G: Fun<T = F::V, V = W>,
A: Into<Anim<G>>,
{
Anim(MapClosure(self.0, anim.into().0))
}
pub fn map_time_anim<S, G, A>(self, anim: A) -> Anim<impl Fun<T = S, V = F::V>>
where
G: Fun<T = S, V = F::T>,
A: Into<Anim<G>>,
{
anim.into().map_anim(self)
}
pub fn into_fn(self) -> impl Fn(F::T) -> F::V {
move |t| self.eval(t)
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Clone + Mul<Output = F::T>,
{
pub fn scale_time(self, t_scale: F::T) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map_time(move |t| t * t_scale.clone())
}
}
impl<F> Fun for Option<F>
where
F: Fun,
{
type T = F::T;
type V = Option<F::V>;
fn eval(&self, t: F::T) -> Option<F::V> {
self.as_ref().map(|f| f.eval(t))
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct MapClosure<F, G>(F, G);
impl<F, G> Fun for MapClosure<F, G>
where
F: Fun,
G: Fun<T = F::V>,
{
type T = F::T;
type V = G::V;
fn eval(&self, t: F::T) -> G::V {
self.1.eval(self.0.eval(t))
}
}
impl<T, X, Y, F> Anim<F>
where
F: Fun<T = T, V = (X, Y)>,
{
pub fn fst(self) -> Anim<impl Fun<T = F::T, V = X>> {
self.map(|(x, _)| x)
}
pub fn snd(self) -> Anim<impl Fun<T = F::T, V = Y>> {
self.map(|(_, y)| y)
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Clone,
{
pub fn zip<G, A>(self, other: A) -> Anim<impl Fun<T = F::T, V = (F::V, G::V)>>
where
G: Fun<T = F::T>,
A: Into<Anim<G>>,
{
Anim(ZipClosure(self.0, other.into().0))
}
pub fn bind<W, G>(self, f: impl Fn(F::V) -> Anim<G>) -> Anim<impl Fun<T = F::T, V = W>>
where
G: Fun<T = F::T, V = W>,
{
fun(move |t: F::T| f(self.eval(t.clone())).eval(t))
}
}
impl<'a, T, V, F> Anim<F>
where
V: 'a + Copy,
F: Fun<T = T, V = &'a V> + 'a,
{
pub fn copied(self) -> Anim<impl Fun<T = T, V = V> + 'a> {
self.map(|x| *x)
}
}
impl<'a, T, V, F> Anim<F>
where
V: 'a + Clone,
F: Fun<T = T, V = &'a V> + 'a,
{
pub fn cloned(self) -> Anim<impl Fun<T = T, V = V> + 'a> {
self.map(|x| x.clone())
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct ZipClosure<F, G>(F, G);
impl<F, G> Fun for ZipClosure<F, G>
where
F: Fun,
F::T: Clone,
G: Fun<T = F::T>,
{
type T = F::T;
type V = (F::V, G::V);
fn eval(&self, t: F::T) -> Self::V {
(self.0.eval(t.clone()), self.1.eval(t))
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Copy + Sub<Output = F::T>,
{
pub fn shift_time(self, t_delay: F::T) -> Anim<impl Fun<T = F::T, V = F::V>> {
(id::<F::T, F::T>() - t_delay).map_anim(self)
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Clone + PartialOrd,
{
pub fn switch<G, A>(self, self_end: F::T, next: A) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
cond(switch_cond(self_end), self, next)
}
pub fn surround<G, A>(
self,
range: RangeInclusive<F::T>,
surround: A,
) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
cond(surround_cond(range), self, surround)
}
}
fn switch_cond<T: PartialOrd>(self_end: T) -> Anim<impl Fun<T = T, V = bool>> {
fun(move |t| t < self_end)
}
fn surround_cond<T: PartialOrd>(range: RangeInclusive<T>) -> Anim<impl Fun<T = T, V = bool>> {
fun(move |t| range.contains(&t))
}
impl<F> Anim<F>
where
F: Fun,
F::T: Clone + PartialOrd,
F::V: Clone,
{
pub fn hold(self, self_end: F::T) -> Anim<impl Fun<T = F::T, V = F::V>> {
let end_value = self.eval(self_end.clone());
self.switch(self_end, constant(end_value))
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Copy + PartialOrd + Sub<Output = F::T>,
{
pub fn seq<G, A>(self, self_end: F::T, next: A) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
self.switch(self_end.clone(), next.into().shift_time(self_end))
}
pub fn seq_continue<G, A, H>(
self,
self_end: F::T,
next_fn: H,
) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
H: Fn(F::V) -> A,
{
let next = next_fn(self.eval(self_end.clone())).into();
self.seq(self_end, next)
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Clone + Sub<Output = F::T>,
{
pub fn backwards(self, end: F::T) -> Anim<impl Fun<T = F::T, V = F::V>> {
(constant(end) - id()).map_anim(self)
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Copy,
F::V: Copy + Num,
{
pub fn scale_min_max(self, min: F::V, max: F::V) -> Anim<impl Fun<T = F::T, V = F::V>> {
self * (max.clone() - min) + min
}
}
#[cfg(any(feature = "std", feature = "libm"))]
impl<F> Anim<F>
where
F: Fun,
F::V: Float,
{
pub fn sin(self) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map(Float::sin)
}
pub fn cos(self) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map(Float::cos)
}
pub fn powf(self, e: F::V) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map(move |v| v.powf(e))
}
}
impl<F> Anim<F>
where
F: Fun,
F::V: FloatCore,
{
pub fn abs(self) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map(FloatCore::abs)
}
pub fn powi(self, n: i32) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map(move |v| v.powi(n))
}
}
impl<F> Anim<F>
where
F: Fun,
F::T: Copy + FloatCore,
{
pub fn squeeze(self, range: RangeInclusive<F::T>) -> Anim<impl Fun<T = F::T, V = F::V>> {
let time_shift = *range.start();
let time_scale = F::T::one() / (*range.end() - *range.start());
self.map_time(move |t| (t - time_shift) * time_scale)
}
pub fn squeeze_and_surround<G, A>(
self,
range: RangeInclusive<F::T>,
surround: A,
) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
self.squeeze(range.clone()).surround(range, surround)
}
pub fn seq_squeeze<G, A>(self, self_end: F::T, next: A) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
let first = self.squeeze(Zero::zero()..=self_end);
let second = next.into().squeeze(self_end..=One::one());
first.switch(self_end, second)
}
pub fn repeat(self, period: F::T) -> Anim<impl Fun<T = F::T, V = F::V>> {
self.map_time(move |t: F::T| (t * period.recip()).fract() * period)
}
}
impl<W, F> Anim<F>
where
F: Fun,
F::T: Copy + Mul<W, Output = W>,
F::V: Copy + Add<W, Output = F::V> + Sub<Output = W>,
{
#[cfg_attr(any(feature = "std", feature = "libm"), doc = r##"
It is also possible to linearly interpolate between two non-constant
animations:
```
let anim = pareen::circle().sin().lerp(pareen::circle().cos());
let value: f32 = anim.eval(0.5f32);
```
"##)]
pub fn lerp<G, A>(self, other: A) -> Anim<impl Fun<T = F::T, V = F::V>>
where
G: Fun<T = F::T, V = F::V>,
A: Into<Anim<G>>,
{
let other = other.into();
fun(move |t| {
let a = self.eval(t);
let b = other.eval(t);
let delta = b - a;
a + t * delta
})
}
}
impl<V, F> Anim<F>
where
F: Fun<V = Option<V>>,
F::T: Clone,
{
pub fn unwrap_or<G, A>(self, default: A) -> Anim<impl Fun<T = F::T, V = V>>
where
G: Fun<T = F::T, V = V>,
A: Into<Anim<G>>,
{
self.zip(default.into())
.map(|(v, default)| v.unwrap_or(default))
}
pub fn map_or<W, G, H, A>(
self,
default: A,
f: impl Fn(V) -> Anim<H>,
) -> Anim<impl Fun<T = F::T, V = W>>
where
G: Fun<T = F::T, V = W>,
H: Fun<T = F::T, V = W>,
A: Into<Anim<G>>,
{
let default = default.into();
fun(move |t: F::T| {
self.eval(t.clone())
.map_or_else(|| default.eval(t.clone()), |v| f(v).eval(t.clone()))
})
}
}
pub fn cond<F, G, H, Cond, A, B>(cond: Cond, a: A, b: B) -> Anim<impl Fun<T = F::T, V = G::V>>
where
F::T: Clone,
F: Fun<V = bool>,
G: Fun<T = F::T>,
H: Fun<T = F::T, V = G::V>,
Cond: Into<Anim<F>>,
A: Into<Anim<G>>,
B: Into<Anim<H>>,
{
Anim(CondClosure(cond.into().0, a.into().0, b.into().0))
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct CondClosure<F, G, H>(F, G, H);
impl<F, G, H> Fun for CondClosure<F, G, H>
where
F::T: Clone,
F: Fun<V = bool>,
G: Fun<T = F::T>,
H: Fun<T = F::T, V = G::V>,
{
type T = F::T;
type V = G::V;
fn eval(&self, t: F::T) -> G::V {
if self.0.eval(t.clone()) {
self.1.eval(t)
} else {
self.2.eval(t)
}
}
}
pub fn lerp<T, V, W, F, G, A, B>(a: A, b: B) -> Anim<impl Fun<T = T, V = V>>
where
T: Copy + Mul<W, Output = W>,
V: Copy + Add<W, Output = V> + Sub<Output = W>,
F: Fun<T = T, V = V>,
G: Fun<T = T, V = V>,
A: Into<Anim<F>>,
B: Into<Anim<G>>,
{
a.into().lerp(b.into())
}
#[macro_export]
macro_rules! anim_match {
(
$expr:expr;
$($pat:pat => $value:expr $(,)?)*
) => {
$crate::fun(move |t| match $expr {
$(
$pat => ($crate::Anim::from($value)).eval(t),
)*
})
}
}