1use core::cell::Cell;
22use core::fmt::Debug;
23use core::time::Duration;
24
25pub const ANIM_SCALE: i32 = 1024;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub enum Easing {
39 Linear,
41 EaseIn,
43 #[default]
45 EaseOut,
46 EaseInOut,
48}
49
50impl Easing {
51 pub fn calc(&self, progress: i32) -> i32 {
62 let t = progress.clamp(0, ANIM_SCALE);
63 match self {
64 Easing::Linear => t,
65
66 Easing::EaseIn => mul_div(t, t, ANIM_SCALE),
67
68 Easing::EaseOut => {
69 let inv_t = ANIM_SCALE - t;
70 ANIM_SCALE - mul_div(inv_t, inv_t, ANIM_SCALE)
71 }
72
73 Easing::EaseInOut => {
74 if t < ANIM_SCALE / 2 {
75 2 * mul_div(t, t, ANIM_SCALE)
76 } else {
77 let inv_t = ANIM_SCALE - t;
78 ANIM_SCALE - 2 * mul_div(inv_t, inv_t, ANIM_SCALE)
79 }
80 }
81 }
82 }
83}
84
85#[inline]
88const fn mul_div(a: i32, b: i32, c: i32) -> i32 {
89 (a * b) / c
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
94pub enum AnimState {
95 #[default]
97 Stopped,
98 Playing,
100 Paused,
102}
103
104pub type AnimId = u16;
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct AnimOptions {
110 pub repeat_count: u16,
112 pub reverse: bool,
114 pub start_delay: Duration,
116 pub play_backward: bool,
118}
119
120impl Default for AnimOptions {
121 fn default() -> Self {
122 Self {
123 repeat_count: 1,
124 reverse: false,
125 start_delay: Duration::ZERO,
126 play_backward: false,
127 }
128 }
129}
130
131impl AnimOptions {
132 pub const fn new() -> Self {
134 Self {
135 repeat_count: 1,
136 reverse: false,
137 start_delay: Duration::ZERO,
138 play_backward: false,
139 }
140 }
141
142 pub const fn with_repeat(mut self, count: u16) -> Self {
144 self.repeat_count = count;
145 self
146 }
147
148 pub const fn with_reverse(mut self, reverse: bool) -> Self {
150 self.reverse = reverse;
151 self
152 }
153
154 pub const fn with_start_delay(mut self, delay: Duration) -> Self {
156 self.start_delay = delay;
157 self
158 }
159
160 pub const fn with_play_backward(mut self, backward: bool) -> Self {
162 self.play_backward = backward;
163 self
164 }
165}
166
167#[derive(Debug, Clone)]
172pub struct Anim {
173 pub start_value: i32,
175 pub end_value: i32,
177 pub duration: Duration,
179 pub easing: Easing,
181 pub options: AnimOptions,
183}
184
185impl Anim {
186 pub const fn new(start_value: i32, end_value: i32, duration: Duration) -> Self {
195 Self {
196 start_value,
197 end_value,
198 duration,
199 easing: Easing::Linear,
200 options: AnimOptions::new(),
201 }
202 }
203
204 pub const fn with_easing(mut self, easing: Easing) -> Self {
206 self.easing = easing;
207 self
208 }
209
210 pub const fn with_options(mut self, options: AnimOptions) -> Self {
212 self.options = options;
213 self
214 }
215
216 pub const fn with_reverse(mut self, reverse: bool) -> Self {
218 self.options.reverse = reverse;
219 self
220 }
221
222 pub const fn with_repeat(mut self, count: u16) -> Self {
224 self.options.repeat_count = count;
225 self
226 }
227
228 pub const fn with_start_delay(mut self, delay: Duration) -> Self {
230 self.options.start_delay = delay;
231 self
232 }
233
234 pub fn calc_value(&self, progress: i32) -> i32 {
244 let eased_progress = self.easing.calc(progress);
245 let range = self.end_value - self.start_value;
246 self.start_value + mul_div(range, eased_progress, ANIM_SCALE)
247 }
248}
249
250const INVALID_ANIM_ID: AnimId = AnimId::MAX;
251
252#[derive(Debug, Clone)]
254pub struct AnimInstance {
255 id: AnimId,
257 state: AnimState,
259 anim: Anim,
261 elapsed: Duration,
263 current_repeat: u16,
265 is_reversed: bool,
267 delay_passed: bool,
269}
270
271impl AnimInstance {
272 const fn new() -> Self {
273 Self {
274 id: INVALID_ANIM_ID,
275 state: AnimState::Playing,
276 anim: Anim::new(0, 0, Duration::ZERO),
277 elapsed: Duration::ZERO,
278 current_repeat: 0,
279 is_reversed: false,
280 delay_passed: false,
281 }
282 }
283}
284
285#[derive(Debug)]
286pub struct AnimStatus(Cell<Option<i32>>);
287impl AnimStatus {
288 pub fn new() -> Self {
289 Self(Cell::new(None))
290 }
291 pub fn set(&self, value: i32) {
292 self.0.set(Some(value));
293 }
294 pub fn take(&self) -> Option<i32> {
295 self.0.take()
296 }
297 pub fn get(&self) -> Option<i32> {
298 self.0.get()
299 }
300}
301
302impl Default for AnimStatus {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308pub struct Animations<const N: usize> {
309 animations: [AnimInstance; N],
310 anim_status: [AnimStatus; N],
311}
312
313impl<const N: usize> Animations<N> {
314 pub fn new() -> Self {
315 let animations = core::array::from_fn(|_| AnimInstance::new());
316 let anim_status = core::array::from_fn(|_| AnimStatus::new());
317 Self {
318 animations,
319 anim_status,
320 }
321 }
322
323 pub fn split(self) -> ([AnimInstance; N], [AnimStatus; N]) {
324 (self.animations, self.anim_status)
325 }
326
327 pub fn as_mut(&mut self) -> (&mut [AnimInstance; N], &[AnimStatus; N]) {
328 (&mut self.animations, &self.anim_status)
329 }
330}
331
332impl<const N: usize> Default for Animations<N> {
333 fn default() -> Self {
334 Self::new()
335 }
336}
337
338pub struct AnimManager<'a> {
348 animations: &'a mut [AnimInstance],
350 anim_status: &'a [AnimStatus],
351 next_id: AnimId,
353}
354
355impl<'a> AnimManager<'a> {
356 pub const fn new(animations: &'a mut [AnimInstance], anim_status: &'a [AnimStatus]) -> Self {
358 Self {
359 animations,
360 anim_status,
361 next_id: 0, }
363 }
364
365 pub fn add(&mut self, anim: Anim) -> Option<AnimId> {
375 if self.next_id as usize >= self.animations.len() {
376 return None;
377 }
378 let id = self.next_id;
379 self.next_id = self.next_id.wrapping_add(1);
380
381 let anim_instance = AnimInstance {
382 id,
383 state: AnimState::Stopped,
384 anim,
385 elapsed: Duration::ZERO,
386 current_repeat: 0,
387 is_reversed: false,
388 delay_passed: false,
389 };
390
391 if let Some(instance) = self.animations.get_mut(id as usize) {
392 *instance = anim_instance;
393 return Some(id);
394 };
395
396 None
397 }
398
399 pub fn remove(&mut self, id: AnimId) -> bool {
409 if let Some(instance) = self.animations.get_mut(id as usize) {
410 instance.id = INVALID_ANIM_ID;
411 return true;
412 }
413
414 false
415 }
416
417 pub fn play(&mut self, id: AnimId) -> bool {
427 if let Some(instance) = self.animations.get_mut(id as usize)
428 && instance.id == id
429 {
430 instance.state = AnimState::Playing;
431 instance.elapsed = Duration::ZERO;
432 instance.current_repeat = 0;
433 instance.is_reversed = instance.anim.options.play_backward;
434 instance.delay_passed = instance.anim.options.start_delay.is_zero();
435
436 if let Some(status) = self.anim_status.get(id as usize) {
437 status.set(instance.anim.start_value);
438 }
439 return true;
440 }
441 false
442 }
443
444 pub fn pause(&mut self, id: AnimId) -> bool {
454 if let Some(instance) = self.animations.get_mut(id as usize)
455 && instance.id == id
456 && instance.state == AnimState::Playing
457 {
458 instance.state = AnimState::Paused;
459 return true;
460 }
461 false
462 }
463
464 pub fn resume(&mut self, id: AnimId) -> bool {
474 if let Some(instance) = self.animations.get_mut(id as usize)
475 && instance.id == id
476 && instance.state == AnimState::Paused
477 {
478 instance.state = AnimState::Playing;
479 return true;
480 }
481 false
482 }
483
484 pub fn stop(&mut self, id: AnimId) -> bool {
494 if let Some(instance) = self.animations.get_mut(id as usize)
495 && instance.id == id
496 {
497 instance.state = AnimState::Stopped;
498 instance.elapsed = Duration::ZERO;
499 instance.current_repeat = 0;
500 return true;
501 }
502 false
503 }
504
505 pub fn get_state(&self, id: AnimId) -> Option<AnimState> {
515 if let Some(instance) = self.animations.get(id as usize)
516 && instance.id == id
517 {
518 return Some(instance.state);
519 }
520 None
521 }
522
523 pub fn tick(&mut self, elapsed: Duration) -> bool {
532 let mut updated = false;
533
534 for (idx, instance) in self.animations.iter_mut().enumerate() {
535 if idx >= self.next_id as usize {
536 break;
537 }
538 if instance.id == INVALID_ANIM_ID || instance.state != AnimState::Playing {
539 continue;
540 }
541 let Some(status) = self.anim_status.get(instance.id as usize) else {
542 continue;
543 };
544
545 if !instance.delay_passed {
547 instance.elapsed += elapsed;
548 if instance.elapsed >= instance.anim.options.start_delay {
549 instance.delay_passed = true;
550 instance.elapsed = Duration::ZERO;
551 } else {
552 continue;
553 }
554 } else {
555 instance.elapsed += elapsed;
556 }
557
558 let duration = instance.anim.duration;
559 let duration_ms = duration.as_millis() as u64;
560 let elapsed_ms = instance.elapsed.as_millis() as u64;
561
562 let progress = if duration_ms == 0 {
564 ANIM_SCALE
565 } else {
566 let effective_elapsed = if instance.is_reversed {
567 duration_ms.saturating_sub(elapsed_ms)
568 } else {
569 elapsed_ms.min(duration_ms)
570 };
571 ((effective_elapsed * ANIM_SCALE as u64) / duration_ms) as i32
572 };
573
574 let value = instance.anim.calc_value(progress);
576
577 if instance.elapsed < duration {
579 status.set(value);
580 } else {
581 let final_value = if instance.is_reversed {
583 instance.anim.start_value
584 } else {
585 instance.anim.end_value
586 };
587 status.set(final_value);
588
589 let repeat_count = instance.anim.options.repeat_count;
591 let should_repeat = repeat_count == 0 || instance.current_repeat < repeat_count - 1;
592
593 if should_repeat {
594 instance.current_repeat += 1;
595 instance.elapsed = Duration::ZERO;
596
597 if instance.anim.options.reverse {
599 instance.is_reversed = !instance.is_reversed;
600 }
601 } else {
602 instance.state = AnimState::Stopped;
603 }
604 }
605 updated = true;
606 }
607
608 updated
609 }
610
611 pub fn count(&self) -> usize {
613 self.animations
614 .iter()
615 .filter(|s| s.id != INVALID_ANIM_ID)
616 .count()
617 }
618
619 pub fn is_empty(&self) -> bool {
621 self.animations.iter().all(|s| s.id == INVALID_ANIM_ID)
622 }
623}