use core::cell::Cell;
use core::fmt::Debug;
use core::time::Duration;
pub const ANIM_SCALE: i32 = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Easing {
Linear,
EaseIn,
#[default]
EaseOut,
EaseInOut,
}
impl Easing {
pub fn calc(&self, progress: i32) -> i32 {
let t = progress.clamp(0, ANIM_SCALE);
match self {
Easing::Linear => t,
Easing::EaseIn => mul_div(t, t, ANIM_SCALE),
Easing::EaseOut => {
let inv_t = ANIM_SCALE - t;
ANIM_SCALE - mul_div(inv_t, inv_t, ANIM_SCALE)
}
Easing::EaseInOut => {
if t < ANIM_SCALE / 2 {
2 * mul_div(t, t, ANIM_SCALE)
} else {
let inv_t = ANIM_SCALE - t;
ANIM_SCALE - 2 * mul_div(inv_t, inv_t, ANIM_SCALE)
}
}
}
}
}
#[inline]
const fn mul_div(a: i32, b: i32, c: i32) -> i32 {
(a * b) / c
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AnimState {
#[default]
Stopped,
Playing,
Paused,
}
pub type AnimId = u16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AnimOptions {
pub repeat_count: u16,
pub reverse: bool,
pub start_delay: Duration,
pub play_backward: bool,
}
impl Default for AnimOptions {
fn default() -> Self {
Self {
repeat_count: 1,
reverse: false,
start_delay: Duration::ZERO,
play_backward: false,
}
}
}
impl AnimOptions {
pub const fn new() -> Self {
Self {
repeat_count: 1,
reverse: false,
start_delay: Duration::ZERO,
play_backward: false,
}
}
pub const fn with_repeat(mut self, count: u16) -> Self {
self.repeat_count = count;
self
}
pub const fn with_reverse(mut self, reverse: bool) -> Self {
self.reverse = reverse;
self
}
pub const fn with_start_delay(mut self, delay: Duration) -> Self {
self.start_delay = delay;
self
}
pub const fn with_play_backward(mut self, backward: bool) -> Self {
self.play_backward = backward;
self
}
}
#[derive(Debug, Clone)]
pub struct Anim {
pub start_value: i32,
pub end_value: i32,
pub duration: Duration,
pub easing: Easing,
pub options: AnimOptions,
}
impl Anim {
pub const fn new(start_value: i32, end_value: i32, duration: Duration) -> Self {
Self {
start_value,
end_value,
duration,
easing: Easing::Linear,
options: AnimOptions::new(),
}
}
pub const fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub const fn with_options(mut self, options: AnimOptions) -> Self {
self.options = options;
self
}
pub const fn with_reverse(mut self, reverse: bool) -> Self {
self.options.reverse = reverse;
self
}
pub const fn with_repeat(mut self, count: u16) -> Self {
self.options.repeat_count = count;
self
}
pub const fn with_start_delay(mut self, delay: Duration) -> Self {
self.options.start_delay = delay;
self
}
pub fn calc_value(&self, progress: i32) -> i32 {
let eased_progress = self.easing.calc(progress);
let range = self.end_value - self.start_value;
self.start_value + mul_div(range, eased_progress, ANIM_SCALE)
}
}
const INVALID_ANIM_ID: AnimId = AnimId::MAX;
#[derive(Debug, Clone)]
pub struct AnimInstance {
id: AnimId,
state: AnimState,
anim: Anim,
elapsed: Duration,
current_repeat: u16,
is_reversed: bool,
delay_passed: bool,
}
impl AnimInstance {
const fn new() -> Self {
Self {
id: INVALID_ANIM_ID,
state: AnimState::Playing,
anim: Anim::new(0, 0, Duration::ZERO),
elapsed: Duration::ZERO,
current_repeat: 0,
is_reversed: false,
delay_passed: false,
}
}
}
#[derive(Debug)]
pub struct AnimStatus(Cell<Option<i32>>);
impl AnimStatus {
pub fn new() -> Self {
Self(Cell::new(None))
}
pub fn set(&self, value: i32) {
self.0.set(Some(value));
}
pub fn take(&self) -> Option<i32> {
self.0.take()
}
pub fn get(&self) -> Option<i32> {
self.0.get()
}
}
pub struct Animations<const N: usize> {
animations: [AnimInstance; N],
anim_status: [AnimStatus; N],
}
impl<const N: usize> Animations<N> {
pub fn new() -> Self {
let animations = core::array::from_fn(|_| AnimInstance::new());
let anim_status = core::array::from_fn(|_| AnimStatus::new());
Self {
animations,
anim_status,
}
}
pub fn split(self) -> ([AnimInstance; N], [AnimStatus; N]) {
(self.animations, self.anim_status)
}
}
pub struct AnimManager<'a> {
animations: &'a mut [AnimInstance],
anim_status: &'a [AnimStatus],
next_id: AnimId,
}
impl<'a> AnimManager<'a> {
pub const fn new(animations: &'a mut [AnimInstance], anim_status: &'a [AnimStatus]) -> Self {
Self {
animations,
anim_status,
next_id: 0, }
}
pub fn add(&mut self, anim: Anim) -> Option<AnimId> {
if self.next_id as usize >= self.animations.len() {
return None;
}
let id = self.next_id;
self.next_id = self.next_id.wrapping_add(1);
let start_value = anim.start_value;
let anim_instance = AnimInstance {
id,
state: AnimState::Stopped,
anim,
elapsed: Duration::ZERO,
current_repeat: 0,
is_reversed: false,
delay_passed: false,
};
if let Some(instance) = self.animations.get_mut(id as usize) {
if let Some(status) = self.anim_status.get(id as usize) {
status.set(start_value);
*instance = anim_instance;
return Some(id);
}
};
None
}
pub fn remove(&mut self, id: AnimId) -> bool {
if let Some(instance) = self.animations.get_mut(id as usize) {
instance.id = INVALID_ANIM_ID;
return true;
}
false
}
pub fn play(&mut self, id: AnimId) -> bool {
if let Some(instance) = self.animations.get_mut(id as usize) {
if instance.id == id {
instance.state = AnimState::Playing;
instance.elapsed = Duration::ZERO;
instance.current_repeat = 0;
instance.is_reversed = instance.anim.options.play_backward;
instance.delay_passed = instance.anim.options.start_delay.is_zero();
return true;
}
}
false
}
pub fn pause(&mut self, id: AnimId) -> bool {
if let Some(instance) = self.animations.get_mut(id as usize) {
if instance.id == id && instance.state == AnimState::Playing {
instance.state = AnimState::Paused;
return true;
}
}
false
}
pub fn resume(&mut self, id: AnimId) -> bool {
if let Some(instance) = self.animations.get_mut(id as usize) {
if instance.id == id && instance.state == AnimState::Paused {
instance.state = AnimState::Playing;
return true;
}
}
false
}
pub fn stop(&mut self, id: AnimId) -> bool {
if let Some(instance) = self.animations.get_mut(id as usize) {
if instance.id == id {
instance.state = AnimState::Stopped;
instance.elapsed = Duration::ZERO;
instance.current_repeat = 0;
return true;
}
}
false
}
pub fn get_state(&self, id: AnimId) -> Option<AnimState> {
if let Some(instance) = self.animations.get(id as usize) {
if instance.id == id {
return Some(instance.state);
}
}
None
}
pub fn tick(&mut self, elapsed: Duration) {
for (idx, instance) in self.animations.iter_mut().enumerate() {
if idx >= self.next_id as usize {
break;
}
if instance.id == INVALID_ANIM_ID || instance.state != AnimState::Playing {
continue;
}
let Some(status) = self.anim_status.get(instance.id as usize) else {
continue;
};
if !instance.delay_passed {
instance.elapsed += elapsed;
if instance.elapsed >= instance.anim.options.start_delay {
instance.delay_passed = true;
instance.elapsed = Duration::ZERO;
} else {
continue;
}
} else {
instance.elapsed += elapsed;
}
let duration = instance.anim.duration;
let duration_ms = duration.as_millis() as u64;
let elapsed_ms = instance.elapsed.as_millis() as u64;
let progress = if duration_ms == 0 {
ANIM_SCALE
} else {
let effective_elapsed = if instance.is_reversed {
duration_ms.saturating_sub(elapsed_ms)
} else {
elapsed_ms.min(duration_ms)
};
((effective_elapsed * ANIM_SCALE as u64) / duration_ms) as i32
};
let value = instance.anim.calc_value(progress);
if instance.elapsed < duration {
status.set(value);
} else {
let final_value = if instance.is_reversed {
instance.anim.start_value
} else {
instance.anim.end_value
};
status.set(final_value);
let repeat_count = instance.anim.options.repeat_count;
let should_repeat = repeat_count == 0 || instance.current_repeat < repeat_count - 1;
if should_repeat {
instance.current_repeat += 1;
instance.elapsed = Duration::ZERO;
if instance.anim.options.reverse {
instance.is_reversed = !instance.is_reversed;
}
} else {
instance.state = AnimState::Stopped;
}
}
}
}
pub fn count(&self) -> usize {
self.animations
.iter()
.filter(|s| s.id != INVALID_ANIM_ID)
.count()
}
pub fn is_empty(&self) -> bool {
self.animations.iter().all(|s| s.id == INVALID_ANIM_ID)
}
}