Files
renderbug-bike/src/animation.rs

278 lines
7.5 KiB
Rust

use embassy_time::{Duration, Instant, Timer};
use figments::{liber8tion::interpolate::Fract8, surface::Surface};
use figments_render::output::Brightness;
use core::{fmt::Debug, ops::{Deref, DerefMut}};
use log::*;
use crate::graphics::display::DisplayControls;
#[derive(Default, Debug, Clone, Copy)]
pub struct Animation<T> {
from: Option<T>,
to: Option<T>,
duration: Duration
}
pub trait AnimationActor<T> {
fn get_value(&self) -> T;
fn set_value(&mut self, value: T);
}
impl<S: Surface> AnimationActor<Fract8> for S {
fn get_value(&self) -> Fract8 {
unimplemented!()
}
fn set_value(&mut self, opacity: Fract8) {
self.set_opacity(opacity);
}
}
#[derive(Debug)]
pub struct AnimDisplay<'a>(pub &'a mut DisplayControls);
impl<'a> AnimationActor<Fract8> for AnimDisplay<'a> {
fn get_value(&self) -> Fract8 {
self.0.brightness()
}
fn set_value(&mut self, opacity: Fract8) {
self.0.set_brightness(opacity);
}
}
impl<S: Surface> AnimationActor<Fract8> for AnimatedSurface<S> {
fn get_value(&self) -> Fract8 {
self.opacity
}
fn set_value(&mut self, opacity: Fract8) {
self.surface.set_opacity(opacity);
self.opacity = opacity;
}
}
trait Tickable {
fn tick(&mut self) -> TickResult;
}
struct Slot<'a, T> {
from: T,
to: T,
cur_step: T,
step_time: Duration,
next_update: Instant,
target: &'a mut dyn AnimationActor<T>
}
enum TickResult {
Finished,
Continue
}
impl<'a, T> Slot<'a, T> {
fn is_valid(&self) -> bool {
self.step_time.as_ticks() != 0
}
}
impl<'a> Tickable for Slot<'a, Fract8> {
/// Advances the animation by one tick, and then returns whether or not the animation should continue or not
fn tick(&mut self) -> TickResult {
self.next_update += self.step_time;
self.cur_step = if self.to > self.from {
self.cur_step + Fract8::from_raw(1)
} else {
self.cur_step - Fract8::from_raw(1)
};
self.target.set_value(self.cur_step);
if (self.to > self.from && self.cur_step >= self.to) ||
(self.to <= self.from && self.cur_step <= self.to) {
TickResult::Finished
} else {
TickResult::Continue
}
}
}
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()
}
}
struct Animator<'a, T, const ACTOR_COUNT: usize> {
animators: [Slot<'a, T>; ACTOR_COUNT]
}
impl<'a, T, const ACTOR_COUNT: usize> Animator<'a, T, ACTOR_COUNT> {
}
impl<T> Animation<T> {
pub const fn new() -> Self {
Self {
from: None,
to: None,
duration: Duration::from_ticks(0)
}
}
async fn execute<'a, const ACTOR_COUNT: usize>(mut animators: [Slot<'a, T>; ACTOR_COUNT]) where Slot<'a, T>: Tickable {
let mut now: Instant = Instant::now();
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.is_valid() {
continue;
}
if animator.next_update <= now {
finished = match animator.tick() {
TickResult::Finished => true,
TickResult::Continue => finished
}
}
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.as_millis());
Timer::after(keyframe_delay).await;
now += keyframe_delay;
}
}
}
impl Animation<Fract8> {
pub const fn duration(self, duration: Duration) -> Self {
Self {
duration,
..self
}
}
pub const fn from(self, from: Fract8) -> Self {
Self {
from: Some(from),
..self
}
}
pub const fn to(self, to: Fract8) -> Self {
Self {
to: Some(to),
..self
}
}
}
const MIN_ANIMATION_RATE: Duration = Duration::from_millis(5);
impl Animation<Fract8> {
pub async fn apply<const ACTOR_COUNT: usize>(&self, actors: [&mut dyn AnimationActor<Fract8>; ACTOR_COUNT]) {
let now = Instant::now();
trace!("start now={now:?} ACTOR_COUNT={ACTOR_COUNT}");
let mut actors = actors.into_iter();
let 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 step_time = if to == from {
// Zero ticks is an 'invalid' animator that shouldn't get processed because start == end already
Duration::from_ticks(0)
} else {
let steps = to.abs_diff(from);
// FIXME: if the resulting duration is less than the animation rate, we also need to re-scale the number added to animator.cur_step further down below. Otherwise a 0-255 animation with a 100ms duration actually ends up running for 255ms
(self.duration / steps.to_raw().into()).max(MIN_ANIMATION_RATE)
};
Slot {
from,
to,
cur_step: from,
step_time,
next_update: now,
target
}
});
trace!("animators={animators:?}");
Self::execute(animators).await;
}
}
#[derive(Debug)]
pub struct AnimatedSurface<S: Surface> {
surface: S,
is_on: bool,
opacity: Fract8
}
impl<S: Surface> Deref for AnimatedSurface<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.surface
}
}
impl<S: Surface> DerefMut for AnimatedSurface<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.surface
}
}
impl<S: Surface + Debug> From<S> for AnimatedSurface<S> {
fn from(surface: S) -> Self {
trace!("create anim surface={surface:?}");
AnimatedSurface {
surface,
is_on: false,
opacity: Fract8::MAX
}
}
}
impl<S: Surface + Debug> AnimatedSurface<S> {
pub async fn set_on(&mut self, is_on: bool) {
if self.is_on != is_on {
let anim = if is_on {
self.surface.set_visible(true);
Animation::default().duration(Duration::from_secs(1)).from(Fract8::MIN).to(Fract8::MAX)
} else {
Animation::default().duration(Duration::from_secs(1)).from(Fract8::MAX).to(Fract8::MIN)
};
anim.apply([&mut self.surface]).await;
self.is_on = true;
if !is_on {
self.surface.set_visible(false);
}
}
}
}