use std::any::Any;
use crate::{
Extract,
animation::{AnimationCell, CoreItemAnimation, Eval, Static},
core_item::{AnyExtractCoreItem, CoreItem, DynItem},
};
#[derive(Default)]
pub struct Timeline {
anims: Vec<Box<dyn CoreItemAnimation>>,
cur_sec: f64,
planning_static_start_sec: Option<f64>,
}
impl Timeline {
pub fn new() -> Self {
Self::default()
}
pub fn show(&mut self) -> &mut Self {
if self.planning_static_start_sec.is_none() {
self.planning_static_start_sec = Some(self.cur_sec)
}
self
}
pub fn hide(&mut self) -> &mut Self {
self._submit_planning_static_anim();
self
}
pub fn forward(&mut self, secs: f64) -> &mut Self {
self.cur_sec += secs;
self
}
pub fn forward_to(&mut self, target_sec: f64) -> &mut Self {
if target_sec > self.cur_sec {
self.forward(target_sec - self.cur_sec);
}
self
}
fn _submit_planning_static_anim(&mut self) -> bool {
if let (Some(start), Some(last_anim)) =
(self.planning_static_start_sec.take(), self.anims.last())
{
let state = last_anim.eval_alpha_dyn(1.0);
self.anims.push(Box::new(
Static(state)
.into_animation_cell()
.at(start)
.with_duration(self.cur_sec - start)
.with_enabled(last_anim.anim_info().enabled),
));
return true;
}
false
}
pub fn play<T: AnyExtractCoreItem>(&mut self, anim: AnimationCell<T>) -> &mut Self {
self._submit_planning_static_anim();
let duration = anim.info.duration_secs;
self.anims
.push(Box::new(anim.at(self.cur_sec).with_duration(duration)));
self.cur_sec += duration;
self.show();
self
}
pub fn eval_at_alpha(&self, alpha: f64) -> Option<(usize, DynItem)> {
let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
return None;
};
self.eval_at_sec(alpha * (end - start) + start)
}
pub fn eval_at_sec(&self, target_sec: f64) -> Option<(usize, DynItem)> {
let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
return None;
};
if !(start..=end).contains(&target_sec) {
return None;
}
self.anims
.iter()
.enumerate()
.filter(|(_, a)| a.anim_info().enabled)
.find_map(|(idx, anim)| {
let range = anim.anim_info().range();
if range.contains(&target_sec)
|| (idx == self.anims.len() - 1 && target_sec == range.end)
{
anim.eval_global_sec_dyn(target_sec)
.map(|dyn_item| (idx, dyn_item))
} else {
None
}
})
}
}
impl TimelineFunc for Timeline {
fn start_sec(&self) -> Option<f64> {
self.anims.first().map(|a| a.anim_info().range().start)
}
fn end_sec(&self) -> Option<f64> {
self.anims.last().map(|a| a.anim_info().range().end)
}
fn seal(&mut self) {
self._submit_planning_static_anim();
}
fn cur_sec(&self) -> f64 {
self.cur_sec
}
fn forward(&mut self, duration_secs: f64) {
self.cur_sec += duration_secs;
}
fn show(&mut self) {
self.show();
}
fn hide(&mut self) {
self.hide();
}
fn get_animation_infos(&self) -> Vec<AnimationInfo> {
self.anims
.iter()
.map(|a| AnimationInfo {
anim_name: a.anim_name().to_string(),
range: a.anim_info().range(),
})
.collect()
}
fn type_name(&self) -> &str {
""
}
fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(usize, Vec<CoreItem>)> {
self.eval_at_sec(target_sec).map(|(idx, dyn_item)| {
let mut items = Vec::new();
dyn_item.extract_into(&mut items);
(idx, items)
})
}
}
pub trait TimelineFunc: Any {
fn start_sec(&self) -> Option<f64>;
fn end_sec(&self) -> Option<f64>;
fn range_sec(&self) -> Option<std::ops::Range<f64>> {
let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
return None;
};
Some(start..end)
}
fn seal(&mut self);
fn cur_sec(&self) -> f64;
fn forward(&mut self, secs: f64);
fn forward_to(&mut self, target_sec: f64) {
let duration = target_sec - self.cur_sec();
if duration > 0.0 {
self.forward(duration);
}
}
fn show(&mut self);
fn hide(&mut self);
fn get_animation_infos(&self) -> Vec<AnimationInfo>;
fn type_name(&self) -> &str;
fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(usize, Vec<CoreItem>)>;
}
pub trait TimelinesFunc {
fn seal(&mut self);
fn max_total_secs(&self) -> f64;
fn sync(&mut self);
fn forward(&mut self, secs: f64);
fn forward_to(&mut self, target_sec: f64);
}
impl<I: ?Sized, T: TimelineFunc> TimelinesFunc for I
where
for<'a> &'a mut I: IntoIterator<Item = &'a mut T>,
for<'a> &'a I: IntoIterator<Item = &'a T>,
{
fn seal(&mut self) {
self.into_iter().for_each(|timeline: &mut T| {
timeline.seal();
});
}
fn max_total_secs(&self) -> f64 {
self.into_iter()
.map(|timeline: &T| timeline.cur_sec())
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap()
}
fn sync(&mut self) {
let max_elapsed_secs = self.max_total_secs();
self.into_iter().for_each(|timeline: &mut T| {
timeline.forward_to(max_elapsed_secs);
});
}
fn forward(&mut self, secs: f64) {
self.into_iter()
.for_each(|timeline: &mut T| timeline.forward(secs));
}
fn forward_to(&mut self, target_sec: f64) {
self.into_iter().for_each(|timeline: &mut T| {
timeline.forward_to(target_sec);
});
}
}
pub struct AnimationInfo {
pub anim_name: String,
pub range: std::ops::Range<f64>,
}