animation: rewrite the apply() algorithm to use fewer timers and multiple targets at once
This commit is contained in:
195
src/animation.rs
195
src/animation.rs
@@ -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> {
|
||||||
let from = if let Some(val) = self.from {
|
pub async fn apply<const ACTOR_COUNT: usize>(&self, actors: [&mut dyn AnimationActor<Fract8>; ACTOR_COUNT]) {
|
||||||
val
|
|
||||||
} else {
|
|
||||||
sfc.get_opacity()
|
|
||||||
};
|
|
||||||
let to = if let Some(val) = self.to {
|
|
||||||
val
|
|
||||||
} else {
|
|
||||||
sfc.get_opacity()
|
|
||||||
};
|
|
||||||
let steps = from.abs_diff(to);
|
|
||||||
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 mut now = Instant::now();
|
||||||
sfc.set_opacity(opacity);
|
trace!("start now={now:?} ACTOR_COUNT={ACTOR_COUNT}");
|
||||||
Timer::after(step_time).await;
|
|
||||||
|
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 {
|
||||||
|
val
|
||||||
|
} else {
|
||||||
|
target.get_value()
|
||||||
|
};
|
||||||
|
let to = if let Some(val) = self.to {
|
||||||
|
val
|
||||||
|
} else {
|
||||||
|
target.get_value()
|
||||||
|
};
|
||||||
|
let steps = to.abs_diff(from);
|
||||||
|
|
||||||
|
let step_time = if steps == Fract8::MIN {
|
||||||
|
Duration::from_ticks(0)
|
||||||
|
} 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);
|
||||||
|
|||||||
Reference in New Issue
Block a user