pub mod creation;
pub mod fading;
pub mod transform;
use crate::{
items::{Rabject, group::Group},
timeline::EntityTimelineStaticState,
utils::rate_functions::linear,
};
#[allow(unused)]
use log::trace;
use std::{iter::Once, rc::Rc};
pub trait EvalDynamic<T> {
fn eval_alpha(&self, alpha: f32) -> T;
}
pub trait ToEvaluator<T> {
fn to_evaluator(self) -> Evaluator<T>
where
Self: Sized + 'static;
}
impl<T: EvalDynamic<E>, E: 'static> ToEvaluator<E> for T {
fn to_evaluator(self) -> Evaluator<E>
where
Self: Sized + 'static,
{
Evaluator::new_dynamic(self)
}
}
pub enum Evaluator<T> {
Dynamic(Box<dyn EvalDynamic<T>>),
Static(Rc<T>),
}
impl<T> From<Box<dyn EvalDynamic<T>>> for Evaluator<T> {
fn from(value: Box<dyn EvalDynamic<T>>) -> Self {
Self::Dynamic(value)
}
}
impl<T> Evaluator<T> {
pub fn new_dynamic<F: EvalDynamic<T> + 'static>(func: F) -> Self {
Self::Dynamic(Box::new(func))
}
}
pub enum EvalResult<T> {
Dynamic(T),
Static(Rc<T>),
}
impl<T: Clone> EvalResult<T> {
pub fn into_owned(self) -> T {
match self {
Self::Dynamic(t) => t,
Self::Static(rc) => (*rc).clone(),
}
}
}
pub trait Eval<T> {
fn eval_alpha(&self, alpha: f32) -> EvalResult<T>;
}
impl<T> Eval<T> for Evaluator<T> {
fn eval_alpha(&self, alpha: f32) -> EvalResult<T> {
match self {
Self::Dynamic(e) => EvalResult::Dynamic(e.eval_alpha(alpha)),
Self::Static(e) => EvalResult::Static(e.clone()),
}
}
}
pub struct EvalAdapter<T> {
pub inner: Box<dyn Eval<T>>,
}
impl<T: EntityTimelineStaticState + Clone + 'static> Eval<T::StateType> for EvalAdapter<T> {
fn eval_alpha(&self, alpha: f32) -> EvalResult<T::StateType> {
match self.inner.eval_alpha(alpha) {
EvalResult::Dynamic(res) => EvalResult::Dynamic(res.into_state_type()),
EvalResult::Static(res) => EvalResult::Static(res.into_rc_state_type()),
}
}
}
pub struct ChainedAnimation<T> {
anims: Vec<Animation<T>>,
end_secs: Vec<f32>,
}
impl<T> ChainedAnimation<T> {
pub fn new(anims: Vec<Animation<T>>) -> Self {
let mut end_secs = Vec::with_capacity(anims.len());
let mut sum = 0.0;
anims
.iter()
.map(|anim| anim.span_len())
.for_each(|duration| {
sum += duration;
end_secs.push(sum);
});
Self { anims, end_secs }
}
}
impl<T> Eval<T> for ChainedAnimation<T> {
fn eval_alpha(&self, alpha: f32) -> EvalResult<T> {
let inner_global_sec = self.end_secs.last().unwrap() * alpha;
let (anim, end_sec) = self
.anims
.iter()
.zip(self.end_secs.iter())
.find(|&(_, end_sec)| *end_sec >= inner_global_sec)
.unwrap();
anim.eval_sec(inner_global_sec - end_sec + anim.span_len())
}
}
impl<T: 'static> From<ChainedAnimation<T>> for Animation<T> {
fn from(value: ChainedAnimation<T>) -> Self {
Self {
duration_secs: value.end_secs.last().cloned().unwrap(),
evaluator: Box::new(value),
padding: (0.0, 0.0),
rate_func: linear,
}
}
}
pub struct Animation<T> {
evaluator: Box<dyn Eval<T>>,
pub(crate) rate_func: fn(f32) -> f32,
pub(crate) duration_secs: f32,
pub(crate) padding: (f32, f32),
}
impl<T: EntityTimelineStaticState + Clone + 'static> Animation<T> {
pub fn into_state_type(self) -> Animation<T::StateType> {
Animation {
evaluator: Box::new(EvalAdapter {
inner: self.evaluator,
}),
rate_func: self.rate_func,
duration_secs: self.duration_secs,
padding: self.padding,
}
}
}
impl<T: 'static> Animation<T> {
pub fn from_evaluator(evaluator: Evaluator<T>) -> Self {
Self {
evaluator: Box::new(evaluator),
rate_func: linear,
duration_secs: 1.0,
padding: (0.0, 0.0),
}
}
}
impl<T> Animation<T> {
pub fn span_len(&self) -> f32 {
self.duration_secs + self.padding.0 + self.padding.1
}
pub fn with_rate_func(mut self, rate_func: fn(f32) -> f32) -> Self {
self.rate_func = rate_func;
self
}
pub fn with_duration(mut self, secs: f32) -> Self {
self.duration_secs = secs;
self
}
pub fn padding(mut self, start_sec: f32, end_sec: f32) -> Self {
self.padding = (start_sec, end_sec);
self
}
pub fn padding_start(mut self, sec: f32) -> Self {
self.padding.0 = sec;
self
}
pub fn padding_end(mut self, sec: f32) -> Self {
self.padding.1 = sec;
self
}
pub fn eval_alpha(&self, alpha: f32) -> EvalResult<T> {
self.eval_sec(alpha * self.span_len())
}
pub fn eval_sec(&self, local_sec: f32) -> EvalResult<T> {
self.evaluator.eval_alpha((self.rate_func)(
((local_sec - self.padding.0) / self.duration_secs).clamp(0.0, 1.0),
))
}
}
pub struct AnimSchedule<'r, 't, T> {
pub(crate) rabject: &'r mut Rabject<'t, T>,
pub(crate) anim: Animation<T>,
}
impl<'r, 't, T: 'static> AnimSchedule<'r, 't, T> {
pub fn new(rabject: &'r mut Rabject<'t, T>, anim: Animation<T>) -> Self {
Self { rabject, anim }
}
}
impl<T> AnimSchedule<'_, '_, T> {
pub fn with_padding(mut self, start_sec: f32, end_sec: f32) -> Self {
self.anim.padding = (start_sec, end_sec);
self
}
pub fn with_rate_func(mut self, rate_func: fn(f32) -> f32) -> Self {
self.anim.rate_func = rate_func;
self
}
pub fn with_duration(mut self, secs: f32) -> Self {
self.anim.duration_secs = secs;
self
}
}
impl<T: 'static> IntoIterator for AnimSchedule<'_, '_, T> {
type IntoIter = Once<Self>;
type Item = Self;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
}
}
impl<T: 'static> Group<AnimSchedule<'_, '_, T>> {
pub fn with_rate_func(mut self, rate_func: fn(f32) -> f32) -> Self {
self.iter_mut().for_each(|schedule| {
schedule.anim.rate_func = rate_func;
});
self
}
pub fn with_duration(mut self, secs: f32) -> Self {
self.iter_mut().for_each(|schedule| {
schedule.anim.duration_secs = secs;
});
self
}
pub fn with_total_duration(mut self, secs: f32) -> Self {
let total_secs = self
.iter()
.map(|schedule| schedule.anim.span_len())
.reduce(|acc, e| acc.max(e))
.unwrap_or(secs);
let ratio = secs / total_secs;
self.iter_mut().for_each(|schedule| {
let (duration, (padding_start, padding_end)) =
(&mut schedule.anim.duration_secs, &mut schedule.anim.padding);
*duration *= ratio;
*padding_start *= ratio;
*padding_end *= ratio;
});
self
}
}
impl<T: Clone + 'static> AnimSchedule<'_, '_, T> {
#[deprecated]
pub fn chain(self, anim_builder: impl FnOnce(T) -> Animation<T>) -> Self {
let AnimSchedule { rabject, anim } = self;
let data = anim.eval_alpha(1.0).into_owned();
let next_anim = (anim_builder)(data);
let chained_anim = ChainedAnimation::new(vec![anim, next_anim]);
Self {
rabject,
anim: chained_anim.into(),
}
}
}
impl<T: EntityTimelineStaticState + Clone + 'static> AnimSchedule<'_, '_, T> {
pub fn apply(self) -> Self {
if let EvalResult::Dynamic(res) = self.anim.eval_alpha(1.0) {
self.rabject.data = res;
}
self.rabject.timeline.update(self.rabject);
self
}
pub fn play(self) {
self.rabject.timeline.play(self);
}
}
impl<T: EntityTimelineStaticState + Clone + 'static> Group<AnimSchedule<'_, '_, T>> {
pub fn apply(mut self) -> Self {
self.iter_mut().for_each(|schedule| {
if let EvalResult::Dynamic(res) = schedule.anim.eval_alpha(1.0) {
schedule.rabject.data = res;
}
schedule.rabject.timeline.update(schedule.rabject);
});
self
}
}