use core::time::Duration;
use crate::animation::Timed;
use crate::mesh::{Animation, Posing};
const WHOLE: f32 = 1.0;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Running {
pub(crate) posed: Posed,
pub(crate) fading: Option<Fading>,
pub(crate) held: Option<Duration>,
}
impl Running {
pub(crate) fn posing(&self, now: Duration, clips: &[Animation]) -> Posing {
let at = self.held.unwrap_or(now);
let (Posed::Playing(motion), Some(fading)) = (self.posed, self.fading) else {
return self.posed.posing(at, clips);
};
motion.blended_over(fading.under.posing(at, clips), fading.weight(at), at, clips)
}
#[cfg(all(test, feature = "offscreen"))]
pub(crate) fn stopped(posing: Posing) -> Self {
Self {
posed: Posed::Stopped(posing),
fading: None,
held: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum Posed {
Playing(Timed),
Stopped(Posing),
}
impl Posed {
fn posing(self, now: Duration, clips: &[Animation]) -> Posing {
match self {
Self::Playing(motion) => motion.posing(now, clips),
Self::Stopped(posing) => posing,
}
}
pub(crate) fn shifted(self, span: Duration) -> Self {
match self {
Self::Playing(motion) => Self::Playing(motion.shifted(span)),
stopped @ Self::Stopped(_) => stopped,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Fading {
pub(crate) under: Posed,
pub(crate) from: Duration,
pub(crate) over: Duration,
}
impl Fading {
pub(crate) fn weight(&self, now: Duration) -> f32 {
now.saturating_sub(self.from)
.div_duration_f32(self.over)
.clamp(0.0, WHOLE)
}
pub(crate) fn shifted(self, span: Duration) -> Self {
Self {
under: self.under.shifted(span),
from: self.from.checked_add(span).unwrap_or(self.from),
..self
}
}
}