1use std::{
2 ops::{
3 Add,
4 Mul,
5 Sub,
6 AddAssign,
7 Div,
8 },
9 collections::HashMap,
10 rc::Rc,
11 cell::RefCell,
12};
13use crate::{
14 EventGraph,
15 core::{
16 Id,
17 NULL_ID,
18 },
19 prim::WeakHistPrim,
20 ProcessingContext,
21 HistPrim,
22};
23
24pub trait EaseUnit {
26 fn to_ease_unit(v: f64) -> Self;
27}
28
29impl EaseUnit for f32 {
30 fn to_ease_unit(v: f64) -> Self {
31 return v as Self;
32 }
33}
34
35impl EaseUnit for f64 {
36 fn to_ease_unit(v: f64) -> Self {
37 return v;
38 }
39}
40
41pub trait HistPrimAnimation {
44 fn update(&mut self, pc: &mut ProcessingContext, delta_ms: f64) -> bool;
47
48 fn id(&self) -> Id;
51}
52
53pub struct HistPrimEaseAnimation<
56 S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
57 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
58> {
59 start: T,
60 range: T,
61 duration: S,
63 at: S,
65 f: fn(S) -> S,
66 value: WeakHistPrim<T>,
67}
68
69impl<
70 S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
71 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
72> HistPrimEaseAnimation<S, T> {
73 fn new(prim: &HistPrim<T>, end: T, duration: S, f: fn(S) -> S) -> HistPrimEaseAnimation<S, T> {
74 let start = prim.get();
75 let range = end - start.clone();
76 return HistPrimEaseAnimation {
77 start: start,
78 range: range,
79 duration: duration,
80 f: f,
81 at: S::to_ease_unit(0f64),
82 value: prim.weak(),
83 };
84 }
85}
86
87impl<
88 S: Copy + EaseUnit + PartialOrd + AddAssign + Div<Output = S>,
89 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
90> HistPrimAnimation for HistPrimEaseAnimation<S, T> {
91 fn update(&mut self, pc: &mut ProcessingContext, delta: f64) -> bool {
92 let Some(value) = self.value.upgrade() else {
93 return false;
94 };
95 self.at += S::to_ease_unit(delta) / self.duration;
96 if self.at >= S::to_ease_unit(1f64) {
97 value.set(pc, self.start.clone() + self.range.clone());
98 return false;
99 }
100 value.set(pc, self.start.clone() + self.range.clone() * (self.f)(self.at));
101 return true;
102 }
103
104 fn id(&self) -> Id {
105 return self.value.upgrade().map(|v| v.0.id).unwrap_or(NULL_ID);
106 }
107}
108
109pub trait HistPrimEaseExt<
114 S,
115 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<S, Output = T> + 'static,
116> {
117 fn set_ease(&self, a: &Animator, end: T, duration: S, f: fn(S) -> S);
118}
119
120impl<
121 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<f32, Output = T> + 'static,
122> HistPrimEaseExt<f32, T> for HistPrim<T> {
123 fn set_ease(&self, a: &Animator, end: T, duration: f32, f: fn(f32) -> f32) {
124 a.start(HistPrimEaseAnimation::new(self, end, duration, f));
125 }
126}
127
128impl<
129 T: PartialEq + Clone + Add<T, Output = T> + Sub<T, Output = T> + Mul<f64, Output = T> + 'static,
130> HistPrimEaseExt<f64, T> for HistPrim<T> {
131 fn set_ease(&self, a: &Animator, end: T, duration: f64, f: fn(f64) -> f64) {
132 a.start(HistPrimEaseAnimation::new(self, end, duration, f));
133 }
134}
135
136pub struct Animator_ {
140 interp: HashMap<Id, Box<dyn HistPrimAnimation>>,
141 interp_backbuf: Option<HashMap<Id, Box<dyn HistPrimAnimation>>>,
142 anim_cb: Option<Box<dyn FnMut() -> ()>>,
143}
144
145#[derive(Clone)]
146pub struct Animator(Rc<RefCell<Animator_>>);
147
148impl Animator {
149 pub fn new() -> Animator {
150 return Animator(Rc::new(RefCell::new(Animator_ {
151 interp: Default::default(),
152 interp_backbuf: Some(Default::default()),
153 anim_cb: None,
154 })));
155 }
156
157 pub fn set_start_cb(&self, trigger_cb: impl FnMut() -> () + 'static) {
158 self.0.borrow_mut().anim_cb = Some(Box::new(trigger_cb));
159 }
160
161 pub fn start(&self, animation: impl HistPrimAnimation + 'static) {
163 self.0.borrow_mut().interp.insert(animation.id(), Box::new(animation));
164 if let Some(cb) = &mut self.0.borrow_mut().anim_cb {
165 cb();
166 }
167 }
168
169 pub fn cancel<T: PartialEq + Clone + 'static>(&self, prim: &HistPrim<T>) {
172 self.0.borrow_mut().interp.remove(&prim.0.id);
173 }
174
175 pub fn clear(&self) {
177 self.0.borrow_mut().interp.clear();
178 }
179
180 pub fn update(&self, eg: &EventGraph, delta_ms: f64) -> bool {
184 let mut out = None;
185 eg.event(|pc| {
186 let mut interp = {
187 let mut self2 = self.0.borrow_mut();
188 let mut interp = self2.interp_backbuf.take().unwrap();
189 std::mem::swap(&mut self2.interp, &mut interp);
190 interp
191 };
192 let mut alive = false;
193 for (id, mut l) in interp.drain() {
194 if l.update(pc, delta_ms) {
195 alive = true;
196 self.0.borrow_mut().interp.insert(id, l);
197 }
198 }
199 self.0.borrow_mut().interp_backbuf = Some(interp);
200 out = Some(alive);
201 });
202 return out.expect(
203 "Update should be called at the root of an independent event but was called while another event was in progress",
204 );
205 }
206}