animation: rewrite the apply() algorithm to use fewer timers and multiple targets at once

This commit is contained in:
2026-02-02 19:43:43 +01:00
parent 2aaa64374b
commit 08a3c65346

View File

@@ -1,29 +1,30 @@
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Instant, Timer};
use figments::surface::Surface; use esp_rtos::CurrentThreadHandle;
use figments::{liber8tion::interpolate::Fract8, surface::Surface};
use figments_render::output::Brightness; use figments_render::output::Brightness;
use core::{fmt::Debug, ops::{Deref, DerefMut}}; use core::{fmt::Debug, mem::MaybeUninit, ops::{Deref, DerefMut}};
use log::*; use log::*;
use crate::graphics::display::DisplayControls; use crate::graphics::display::DisplayControls;
#[derive(Default, Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]
pub struct Animation { pub struct Animation<T> {
from: Option<u8>, from: Option<T>,
to: Option<u8>, to: Option<T>,
duration: Duration duration: Duration
} }
pub trait AnimationActor { pub trait AnimationActor<T> {
fn get_opacity(&self) -> u8; fn get_value(&self) -> T;
fn set_opacity(&mut self, opacity: u8); fn set_value(&mut self, value: T);
} }
impl<S: Surface> AnimationActor for S { impl<S: Surface> AnimationActor<Fract8> for S {
fn get_opacity(&self) -> u8 { fn get_value(&self) -> Fract8 {
0 unimplemented!()
} }
fn set_opacity(&mut self, opacity: u8) { fn set_value(&mut self, opacity: Fract8) {
self.set_opacity(opacity); self.set_opacity(opacity);
} }
} }
@@ -31,28 +32,48 @@ impl<S: Surface> AnimationActor for S {
#[derive(Debug)] #[derive(Debug)]
pub struct AnimDisplay<'a>(pub &'a mut DisplayControls); pub struct AnimDisplay<'a>(pub &'a mut DisplayControls);
impl<'a> AnimationActor for AnimDisplay<'a> { impl<'a> AnimationActor<Fract8> for AnimDisplay<'a> {
fn get_opacity(&self) -> u8 { fn get_value(&self) -> Fract8 {
self.0.brightness() self.0.brightness()
} }
fn set_opacity(&mut self, opacity: u8) { fn set_value(&mut self, opacity: Fract8) {
self.0.set_brightness(opacity); self.0.set_brightness(opacity);
} }
} }
impl<S: Surface> AnimationActor for AnimatedSurface<S> { impl<S: Surface> AnimationActor<Fract8> for AnimatedSurface<S> {
fn get_opacity(&self) -> u8 { fn get_value(&self) -> Fract8 {
self.opacity self.opacity
} }
fn set_opacity(&mut self, opacity: u8) { fn set_value(&mut self, opacity: Fract8) {
self.surface.set_opacity(opacity); self.surface.set_opacity(opacity);
self.opacity = opacity; self.opacity = opacity;
} }
} }
impl Animation { struct Slot<'a, T> {
from: T,
to: T,
cur_step: T,
step_time: Duration,
next_update: Instant,
target: &'a mut dyn AnimationActor<T>
}
impl<'a, T: Debug> core::fmt::Debug for Slot<'a, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Slot")
.field("from", &self.from)
.field("to", &self.to)
.field("cur_step", &self.cur_step)
.field("step_time", &self.step_time)
.field("next_update", &self.next_update).finish()
}
}
impl Animation<Fract8> {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
from: None, from: None,
@@ -60,61 +81,111 @@ impl Animation {
duration: Duration::from_ticks(0) duration: Duration::from_ticks(0)
} }
} }
pub const fn from(self, from: u8) -> Self {
pub const fn from(self, from: Fract8) -> Self {
Self { Self {
from: Some(from), from: Some(from),
to: self.to, ..self
duration: self.duration
} }
} }
pub const fn to(self, to: u8) -> Self { pub const fn to(self, to: Fract8) -> Self {
Self { Self {
from: self.from,
to: Some(to), to: Some(to),
duration: self.duration ..self
} }
} }
pub const fn duration(self, duration: Duration) -> Self { pub const fn duration(self, duration: Duration) -> Self {
Self { Self {
from: self.from, duration,
to: self.to, ..self
duration }
} }
} }
pub async fn apply<S: AnimationActor>(&self, sfc: &mut S) { impl Animation<Fract8> {
pub async fn apply<const ACTOR_COUNT: usize>(&self, actors: [&mut dyn AnimationActor<Fract8>; ACTOR_COUNT]) {
let mut now = Instant::now();
trace!("start now={now:?} ACTOR_COUNT={ACTOR_COUNT}");
let mut actors = actors.into_iter();
let mut animators: [Slot<Fract8>; ACTOR_COUNT] = core::array::from_fn(|_| {
let target = actors.next().unwrap();
let from = if let Some(val) = self.from { let from = if let Some(val) = self.from {
val val
} else { } else {
sfc.get_opacity() target.get_value()
}; };
let to = if let Some(val) = self.to { let to = if let Some(val) = self.to {
val val
} else { } else {
sfc.get_opacity() target.get_value()
}; };
let steps = from.abs_diff(to); let steps = to.abs_diff(from);
if steps == 0 {
return;
}
let step_time = self.duration / steps.into();
trace!("fade={self:?} steps={steps} time={step_time}");
if from > to {
let range = (to..=from).rev();
for opacity in range {
sfc.set_opacity(opacity);
Timer::after(step_time).await;
}
} else {
let range = from..=to;
for opacity in range { let step_time = if steps == Fract8::MIN {
sfc.set_opacity(opacity); Duration::from_ticks(0)
Timer::after(step_time).await; } else {
(self.duration / steps.to_raw().into()).max(Duration::from_millis(1))
};
Slot {
from,
to,
cur_step: from,
step_time,
next_update: now,
target
}
});
trace!("animators={animators:?}");
loop {
// Find the next shortest delay
let mut next_keyframe_time = animators[0].next_update;
let mut finished = false;
for animator in &mut animators {
if animator.step_time.as_ticks() == 0 {
continue;
}
if animator.next_update <= now {
animator.next_update += animator.step_time;
animator.cur_step = if animator.to > animator.from {
animator.cur_step + Fract8::from_raw(1)
} else {
animator.cur_step - Fract8::from_raw(1)
};
if (animator.to > animator.from && animator.cur_step >= animator.to) ||
(animator.to <= animator.from && animator.cur_step <= animator.to) {
finished = true;
}
/*if animator.cur_step == animator.from || animator.cur_step == animator.to {
finished = true;
}*/
animator.target.set_value(animator.cur_step);
}
if next_keyframe_time <= now || animator.next_update < next_keyframe_time {
next_keyframe_time = animator.next_update;
} }
} }
if finished {
break;
}
let keyframe_delay = next_keyframe_time - now;
trace!("delay {keyframe_delay:?}");
Timer::after(keyframe_delay).await;
now += keyframe_delay;
}
trace!("finished animators={animators:?}");
} }
} }
@@ -122,7 +193,7 @@ impl Animation {
pub struct AnimatedSurface<S: Surface> { pub struct AnimatedSurface<S: Surface> {
surface: S, surface: S,
is_on: bool, is_on: bool,
opacity: u8 opacity: Fract8
} }
impl<S: Surface> Deref for AnimatedSurface<S> { impl<S: Surface> Deref for AnimatedSurface<S> {
@@ -145,7 +216,7 @@ impl<S: Surface + Debug> From<S> for AnimatedSurface<S> {
AnimatedSurface { AnimatedSurface {
surface, surface,
is_on: false, is_on: false,
opacity: 255 opacity: Fract8::MAX
} }
} }
} }
@@ -155,11 +226,11 @@ impl<S: Surface + Debug> AnimatedSurface<S> {
if self.is_on != is_on { if self.is_on != is_on {
let anim = if is_on { let anim = if is_on {
self.surface.set_visible(true); self.surface.set_visible(true);
Animation::default().duration(Duration::from_secs(1)).from(0).to(255) Animation::default().duration(Duration::from_secs(1)).from(Fract8::MIN).to(Fract8::MAX)
} else { } else {
Animation::default().duration(Duration::from_secs(1)).from(255).to(0) Animation::default().duration(Duration::from_secs(1)).from(Fract8::MAX).to(Fract8::MIN)
}; };
anim.apply(&mut self.surface).await; anim.apply([&mut self.surface]).await;
self.is_on = true; self.is_on = true;
if !is_on { if !is_on {
self.surface.set_visible(false); self.surface.set_visible(false);