use std::cell::Cell;
use std::rc::{Rc, Weak};
use super::tween::TweenObj;
use super::TimelineState;
pub struct Action {
kind: ActionKind,
timeline: Option<Weak<TimelineState>>,
registered: Cell<bool>,
}
enum ActionKind {
Tween(Rc<dyn TweenObj>),
Wait(f64),
Seq(Vec<Action>, f64),
Par(Vec<Action>),
}
impl Action {
fn new(kind: ActionKind) -> Action {
Action { kind, timeline: None, registered: Cell::new(false) }
}
pub(crate) fn from_tween(tween: Rc<dyn TweenObj>) -> Action {
Action::new(ActionKind::Tween(tween))
}
pub(crate) fn attach_timeline(&mut self, tl: Weak<TimelineState>) {
if tl.strong_count() > 0 {
self.timeline = Some(tl);
}
}
pub(super) fn mark_registered(&self) {
self.registered.set(true);
}
}
impl Drop for Action {
fn drop(&mut self) {
if self.registered.get() {
return;
}
self.registered.set(true);
if let Some(state) = self.timeline.as_ref().and_then(Weak::upgrade) {
let detached = Action {
kind: std::mem::replace(&mut self.kind, ActionKind::Wait(0.0)),
timeline: None,
registered: Cell::new(true),
};
state.append(detached);
}
}
}
fn combine(items: Vec<Action>, kind: impl FnOnce(Vec<Action>) -> ActionKind) -> Action {
let timeline = items.iter().find_map(|a| a.timeline.clone());
for it in &items {
it.mark_registered();
}
Action { kind: kind(items), timeline, registered: Cell::new(false) }
}
pub fn pause(seconds: f64) -> Action {
Action::new(ActionKind::Wait(seconds.max(0.0)))
}
pub fn parallel(items: impl IntoIterator<Item = Action>) -> Action {
let items: Vec<Action> = items.into_iter().collect();
merge_overlapping(
&items,
|t| t.morph_group(),
|t| t.morph_from(),
|t| t.morph_new(),
|t, old, new| t.rebase(old, new),
);
merge_overlapping(
&items,
|t| t.highlight_group(),
|t| t.highlight_from(),
|t| t.highlight_to(),
|t, old, new| t.highlight_rebase(old.clone(), new.clone()),
);
combine(items, ActionKind::Par)
}
fn merge_overlapping<T: Clone + PartialEq>(
items: &[Action],
key: impl Fn(&Rc<dyn TweenObj>) -> Option<*const ()>,
from: impl Fn(&Rc<dyn TweenObj>) -> Option<T>,
to: impl Fn(&Rc<dyn TweenObj>) -> Option<T>,
rebase: impl Fn(&Rc<dyn TweenObj>, &T, &T),
) {
let mut groups: Vec<(*const (), Vec<&Rc<dyn TweenObj>>)> = Vec::new();
for it in items {
let tween = match &it.kind {
ActionKind::Tween(t) => t,
_ => continue,
};
let k = match key(tween) {
Some(k) => k,
None => continue,
};
match groups.iter_mut().find(|(g, _)| *g == k) {
Some((_, group)) => group.push(tween),
None => groups.push((k, vec![tween])),
}
}
for (_, group) in &groups {
if group.len() < 2 {
continue;
}
let olds: Vec<T> = group.iter().filter_map(|t| from(t)).collect();
let news: Vec<T> = group.iter().filter_map(|t| to(t)).collect();
if olds.len() != group.len() || news.len() != group.len() {
continue; }
let bases: Vec<&T> = olds.iter().filter(|o| !news.contains(o)).collect();
let finals: Vec<&T> = news.iter().filter(|n| !olds.contains(n)).collect();
if bases.len() == 1 && finals.len() == 1 {
for t in group {
rebase(t, bases[0], finals[0]);
}
}
}
}
pub fn sequence(items: impl IntoIterator<Item = Action>) -> Action {
combine(items.into_iter().collect(), |v| ActionKind::Seq(v, 0.0))
}
pub fn cascade(items: impl IntoIterator<Item = Action>, gap: f64) -> Action {
let gap = gap.max(0.0);
combine(items.into_iter().collect(), move |v| ActionKind::Seq(v, gap))
}
pub fn delay(seconds: f64, action: Action) -> Action {
sequence(vec![pause(seconds), action])
}
impl Action {
pub fn then(self, next: Action) -> Action {
sequence(vec![self, next])
}
pub fn with(self, other: Action) -> Action {
parallel(vec![self, other])
}
pub fn after(self, seconds: f64) -> Action {
delay(seconds, self)
}
}
pub(super) fn flatten(action: &Action, base: f64, out: &mut Vec<Rc<dyn TweenObj>>) -> f64 {
match &action.kind {
ActionKind::Tween(t) => {
t.set_start(base);
out.push(t.clone());
t.duration()
}
ActionKind::Wait(d) => *d,
ActionKind::Seq(items, gap) => {
let mut offset = 0.0;
let n = items.len();
for (i, it) in items.iter().enumerate() {
offset += flatten(it, base + offset, out);
if i + 1 < n {
offset += *gap;
}
}
offset
}
ActionKind::Par(items) => {
let mut max = 0.0_f64;
for it in items {
max = max.max(flatten(it, base, out));
}
max
}
}
}
pub(super) fn duration_of(action: &Action) -> f64 {
match &action.kind {
ActionKind::Tween(t) => t.duration(),
ActionKind::Wait(d) => *d,
ActionKind::Seq(items, gap) => {
let mut total = 0.0;
let n = items.len();
for (i, it) in items.iter().enumerate() {
total += duration_of(it);
if i + 1 < n {
total += *gap;
}
}
total
}
ActionKind::Par(items) => items.iter().map(duration_of).fold(0.0_f64, f64::max),
}
}