use std::{
ops::{
Add,
Mul,
Sub,
AddAssign,
Div,
},
collections::HashMap,
rc::Rc,
cell::RefCell,
};
use crate::{
EventGraph,
core::{
Id,
NULL_ID,
},
prim::WeakHistPrim,
ProcessingContext,
HistPrim,
};
pub trait EaseUnit {
fn to_ease_unit(v: f64) -> Self;
}
impl EaseUnit for f32 {
fn to_ease_unit(v: f64) -> Self {
return v as Self;
}
}
impl EaseUnit for f64 {
fn to_ease_unit(v: f64) -> Self {
return v;
}
}
pub trait HistPrimAnimation {
fn update(&mut self, pc: &mut ProcessingContext, delta_ms: f64) -> bool;
fn id(&self) -> Id;
}
pub struct HistPrimEaseAnimation<
S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
> {
start: T,
range: T,
duration: S,
at: S,
f: fn(S) -> S,
value: WeakHistPrim<T>,
}
impl<
S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
> HistPrimEaseAnimation<S, T> {
fn new(prim: &HistPrim<T>, end: T, duration: S, f: fn(S) -> S) -> HistPrimEaseAnimation<S, T> {
let start = prim.get();
let range = end - start.clone();
return HistPrimEaseAnimation {
start: start,
range: range,
duration: duration,
f: f,
at: S::to_ease_unit(0f64),
value: prim.weak(),
};
}
}
impl<
S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
> HistPrimAnimation for HistPrimEaseAnimation<S, T> {
fn update(&mut self, pc: &mut ProcessingContext, delta: f64) -> bool {
let Some(value) = self.value.upgrade() else {
return false;
};
self.at += S::to_ease_unit(delta) / self.duration;
if self.at >= S::to_ease_unit(1f64) {
value.set(pc, self.start.clone() + self.range.clone());
return false;
}
value.set(pc, self.start.clone() + self.range.clone() * (self.f)(self.at));
return true;
}
fn id(&self) -> Id {
return self.value.upgrade().map(|v| v.0.id).unwrap_or(NULL_ID);
}
}
pub trait HistPrimEaseExt<
S,
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
> {
fn set_ease(&self, a: &Animator, end: T, duration: S, f: fn(S) -> S);
}
impl<
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<f32, Output = T> + 'static,
> HistPrimEaseExt<f32, T> for HistPrim<T> {
fn set_ease(&self, a: &Animator, end: T, duration: f32, f: fn(f32) -> f32) {
a.start(HistPrimEaseAnimation::new(self, end, duration, f));
}
}
impl<
T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<f64, Output = T> + 'static,
> HistPrimEaseExt<f64, T> for HistPrim<T> {
fn set_ease(&self, a: &Animator, end: T, duration: f64, f: fn(f64) -> f64) {
a.start(HistPrimEaseAnimation::new(self, end, duration, f));
}
}
pub struct Animator_ {
interp: HashMap<Id, Box<dyn HistPrimAnimation>>,
interp_backbuf: Option<HashMap<Id, Box<dyn HistPrimAnimation>>>,
anim_cb: Option<Box<dyn FnMut() -> ()>>,
}
#[derive(Clone)]
pub struct Animator(Rc<RefCell<Animator_>>);
impl Animator {
pub fn new() -> Animator {
return Animator(Rc::new(RefCell::new(Animator_ {
interp: Default::default(),
interp_backbuf: Some(Default::default()),
anim_cb: None,
})));
}
pub fn set_start_cb(&self, trigger_cb: impl FnMut() -> () + 'static) {
self.0.borrow_mut().anim_cb = Some(Box::new(trigger_cb));
}
pub fn start(&self, animation: impl HistPrimAnimation + 'static) {
self.0.borrow_mut().interp.insert(animation.id(), Box::new(animation));
if let Some(cb) = &mut self.0.borrow_mut().anim_cb {
cb();
}
}
pub fn cancel<T: PartialEq + Clone + 'static>(&self, prim: &HistPrim<T>) {
self.0.borrow_mut().interp.remove(&prim.0.id);
}
pub fn clear(&self) {
self.0.borrow_mut().interp.clear();
}
pub fn update(&self, eg: &EventGraph, delta_ms: f64) -> bool {
let mut out = None;
eg.event(|pc| {
let mut interp = {
let mut self2 = self.0.borrow_mut();
let mut interp = self2.interp_backbuf.take().unwrap();
std::mem::swap(&mut self2.interp, &mut interp);
interp
};
let mut alive = false;
for (id, mut l) in interp.drain() {
if l.update(pc, delta_ms) {
alive = true;
self.0.borrow_mut().interp.insert(id, l);
}
}
self.0.borrow_mut().interp_backbuf = Some(interp);
out = Some(alive);
});
return out.expect(
"Update should be called at the root of an independent event but was called while another event was in progress",
);
}
}