fastanim_core/
timeline.rs1use std::sync::Arc;
4
5use crate::anim::{Animation, RateFn};
6use crate::mobject::{MobjectId, SceneState, VState};
7
8struct Clip {
10 start: f32,
11 end: f32,
12 before: SceneState,
13 anim: Option<(Box<dyn Animation>, RateFn)>,
14}
15
16type UpdaterFn = Arc<dyn Fn(&SceneState, f32) -> VState + Send + Sync>;
17
18#[derive(Clone)]
20struct Updater {
21 start: f32,
22 id: MobjectId,
23 f: UpdaterFn,
24}
25
26#[derive(Default)]
28pub struct Scene {
29 state: SceneState,
30 clips: Vec<Clip>,
31 cursor: f32,
32 next_id: u32,
33 markers: Vec<(String, f32)>,
34 updaters: Vec<Updater>,
35}
36
37impl Scene {
38 pub fn new() -> Self {
40 Self::default()
41 }
42
43 pub fn add(&mut self, m: VState) -> MobjectId {
45 let id = MobjectId(self.next_id);
46 self.next_id += 1;
47 self.state.insert(id, m);
48 id
49 }
50
51 pub fn remove(&mut self, id: MobjectId) {
53 self.state.remove(&id);
54 }
55
56 pub fn state(&self) -> &SceneState {
58 &self.state
59 }
60
61 pub fn time(&self) -> f32 {
63 self.cursor
64 }
65
66 pub fn play(&mut self, mut anim: impl Animation + 'static) {
68 anim.plan(&self.state);
69 let rate = anim.rate_fn();
70 let before = self.state.clone();
71 anim.sample(rate.apply(1.0), &mut self.state);
72 let start = self.cursor;
73 self.cursor += anim.duration().max(0.0);
74 self.clips.push(Clip {
75 start,
76 end: self.cursor,
77 before,
78 anim: Some((Box::new(anim), rate)),
79 });
80 }
81
82 pub fn wait(&mut self, secs: f32) {
84 if secs <= 0.0 {
85 return;
86 }
87 let start = self.cursor;
89 self.cursor += secs;
90 self.clips.push(Clip {
91 start,
92 end: self.cursor,
93 before: self.state.clone(),
94 anim: None,
95 });
96 }
97
98 pub fn always(
103 &mut self,
104 id: MobjectId,
105 f: impl Fn(&SceneState, f32) -> VState + Send + Sync + 'static,
106 ) {
107 self.updaters.push(Updater {
108 start: self.cursor,
109 id,
110 f: Arc::new(f),
111 });
112 }
113
114 pub fn marker(&mut self, name: &str) {
116 self.markers.push((name.to_owned(), self.cursor));
117 }
118
119 pub fn bake(mut self) -> BakedTimeline {
121 let tail = Clip {
122 start: self.cursor,
123 end: self.cursor,
124 before: self.state,
125 anim: None,
126 };
127 self.clips.push(tail);
128 BakedTimeline {
129 clips: self.clips,
130 duration: self.cursor,
131 markers: self.markers,
132 updaters: self.updaters,
133 }
134 }
135}
136
137pub struct BakedTimeline {
139 clips: Vec<Clip>,
140 duration: f32,
141 markers: Vec<(String, f32)>,
142 updaters: Vec<Updater>,
143}
144
145impl BakedTimeline {
146 pub fn duration(&self) -> f32 {
148 self.duration
149 }
150
151 pub fn markers(&self) -> &[(String, f32)] {
153 &self.markers
154 }
155
156 pub fn marker(&self, name: &str) -> Option<f32> {
158 self.markers
159 .iter()
160 .find(|(n, _)| n == name)
161 .map(|&(_, t)| t)
162 }
163
164 pub fn eval(&self, t: f32) -> SceneState {
167 let i = self
169 .clips
170 .partition_point(|c| c.start <= t)
171 .saturating_sub(1);
172 let clip = &self.clips[i];
173 let mut out = clip.before.clone();
174 if let Some((anim, rate)) = &clip.anim {
175 let len = clip.end - clip.start;
176 let p = if len > 0.0 {
177 (t - clip.start) / len
178 } else {
179 1.0
180 };
181 anim.sample(rate.apply(p), &mut out);
182 }
183 for u in self.updaters.iter().filter(|u| u.start <= t) {
184 if out.contains_key(&u.id) {
185 let m = (u.f)(&out, t - u.start);
186 out.insert(u.id, m);
187 }
188 }
189 out
190 }
191}