use crate::timeline::{MergedTimeline, Timeline, TimelineOrBuilder};
pub use enum_map::Enum as State;
use enum_map::{EnumArray, EnumMap};
use std::marker::PhantomData;
use std::time::Duration;
pub trait MapLike<K, V> {
fn get(&self, key: &K) -> Option<&V>;
fn get_mut(&mut self, key: &K) -> Option<&mut V>;
}
impl<K: Clone + EnumArray<Option<V>>, V> MapLike<K, V> for EnumMap<K, Option<V>> {
fn get(&self, key: &K) -> Option<&V> {
self[key.clone()].as_ref()
}
fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self[key.clone()].as_mut()
}
}
pub trait StateAnimator {
type State;
type Values;
fn advance(&mut self, elapsed_seconds: f32);
fn current_state(&self) -> &Self::State;
fn current_values(&self) -> &Self::Values;
fn set_state(&mut self, state: &Self::State);
}
pub type EnumStateAnimator<State, Timeline> =
MappedTimelineAnimator<State, Timeline, EnumMap<State, Option<MergedTimeline<Timeline>>>>;
pub struct MappedTimelineAnimator<State, Timeline, TimelineMap>
where
State: Clone + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone,
TimelineMap: MapLike<State, MergedTimeline<Timeline>>,
{
timelines: TimelineMap,
current_state: State,
current_values: Timeline::Target,
paused_animation: Option<(State, Duration)>,
state_duration: Duration,
_timeline_phantom: PhantomData<Timeline>,
}
impl<State, Timeline, TimelineMap> MappedTimelineAnimator<State, Timeline, TimelineMap>
where
State: Clone + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone,
TimelineMap: MapLike<State, MergedTimeline<Timeline>>,
{
fn new(timelines: TimelineMap, initial_state: State, initial_values: Timeline::Target) -> Self {
let mut animator = MappedTimelineAnimator {
timelines,
current_state: initial_state.clone(),
current_values: initial_values,
paused_animation: None,
state_duration: Duration::ZERO,
_timeline_phantom: PhantomData,
};
animator.blend_next_timeline(&initial_state);
animator
}
fn blend_next_timeline(&mut self, state: &State) {
if let Some(next_timeline) = self.timelines.get_mut(state) {
next_timeline.start_with(&self.current_values);
}
}
fn update_current_values(&mut self) {
if let Some(timeline) = self.timelines.get(&self.current_state) {
timeline.update(&mut self.current_values, self.state_duration.as_secs_f32());
}
}
}
impl<State, Timeline, TimelineMap> StateAnimator
for MappedTimelineAnimator<State, Timeline, TimelineMap>
where
State: Clone + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone,
TimelineMap: MapLike<State, MergedTimeline<Timeline>>,
{
type State = State;
type Values = Timeline::Target;
fn advance(&mut self, elapsed_seconds: f32) {
self.state_duration += Duration::from_secs_f32(elapsed_seconds);
self.update_current_values();
}
fn current_state(&self) -> &Self::State {
&self.current_state
}
fn current_values(&self) -> &Self::Values {
&self.current_values
}
fn set_state(&mut self, state: &State) {
if state == &self.current_state {
return;
}
match self.paused_animation.as_ref() {
Some((paused_state, paused_position)) if state == paused_state => {
self.state_duration = *paused_position;
}
_ => {
let was_animating = self.timelines.get(&self.current_state).is_some();
let will_animate = self.timelines.get(state).is_some();
if was_animating && !will_animate {
self.paused_animation = Some((self.current_state.clone(), self.state_duration));
}
self.blend_next_timeline(state);
self.state_duration = Duration::ZERO;
}
}
self.current_state = state.clone();
self.update_current_values();
}
}
pub struct StateAnimatorBuilder<State, Timeline>
where
State: Clone + Default + EnumArray<Option<MergedTimeline<Timeline>>> + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone + Default,
{
initial_state: State,
initial_values: Timeline::Target,
timelines: EnumMap<State, Option<MergedTimeline<Timeline>>>,
}
impl<State, Timeline> Default for StateAnimatorBuilder<State, Timeline>
where
State: Clone + Default + EnumArray<Option<MergedTimeline<Timeline>>> + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone + Default,
{
fn default() -> Self {
Self {
initial_state: Default::default(),
initial_values: Timeline::Target::default(),
timelines: EnumMap::default(),
}
}
}
impl<State, Timeline> StateAnimatorBuilder<State, Timeline>
where
State: Clone + Default + EnumArray<Option<MergedTimeline<Timeline>>> + PartialEq,
Timeline: crate::timeline::Timeline,
Timeline::Target: Clone + Default,
{
pub fn new() -> Self {
Self::default()
}
pub fn build(
self,
) -> MappedTimelineAnimator<State, Timeline, EnumMap<State, Option<MergedTimeline<Timeline>>>>
{
MappedTimelineAnimator::new(self.timelines, self.initial_state, self.initial_values)
}
pub fn from_state(mut self, state: State) -> Self {
self.initial_state = state;
self
}
pub fn from_values(mut self, values: Timeline::Target) -> Self {
self.initial_values = values;
self
}
pub fn on(mut self, state: State, timeline: impl TimelineOrBuilder<Timeline>) -> Self {
self.timelines[state] = Some(timeline.build());
self
}
}