use crate::easing::Easing;
use crate::time_scale::{TimeScale, TimeScalePosition};
use std::fmt::Debug;
pub trait Timeline {
type Target;
fn start_with(&mut self, values: &Self::Target);
fn update(&self, values: &mut Self::Target, time: f32);
}
pub trait TimelineBuilder<T: Timeline> {
fn build(self) -> T;
}
pub trait TimelineOrBuilder<T: Timeline> {
fn build(self) -> MergedTimeline<T>;
}
impl<T: Timeline> TimelineOrBuilder<T> for MergedTimeline<T> {
fn build(self) -> MergedTimeline<T> {
self
}
}
pub struct TimelineBuilderArguments<Data: Clone + Debug> {
pub boundary_times: Vec<f32>,
pub default_easing: Easing,
pub keyframes: Vec<Keyframe<Data>>,
pub timescale: TimeScale,
}
impl<Data: Clone + Debug> From<TimelineConfiguration<Data>> for TimelineBuilderArguments<Data> {
fn from(value: TimelineConfiguration<Data>) -> Self {
let mut args = Self {
timescale: value.create_timescale(),
boundary_times: value.get_boundary_times(),
default_easing: value.default_easing,
keyframes: value.keyframes,
};
args.keyframes
.sort_by(|a, b| a.normalized_time.total_cmp(&b.normalized_time));
args
}
}
#[derive(Clone, Debug)]
pub struct TimelineConfiguration<Data: Clone + Debug> {
default_easing: Easing,
delay_seconds: f32,
duration_seconds: f32,
keyframes: Vec<Keyframe<Data>>,
repeat: Repeat,
reverse: bool,
}
impl<Data: Clone + Debug> Default for TimelineConfiguration<Data> {
fn default() -> Self {
Self {
default_easing: Easing::default(),
delay_seconds: 0.0,
duration_seconds: 1.0,
keyframes: Vec::new(),
repeat: Repeat::None,
reverse: false,
}
}
}
impl<Data: Clone + Debug> TimelineConfiguration<Data> {
pub fn default_easing(mut self, default_easing: Easing) -> Self {
self.default_easing = default_easing;
self
}
pub fn delay_seconds(mut self, delay_seconds: f32) -> Self {
self.delay_seconds = delay_seconds;
self
}
pub fn duration_seconds(mut self, duration_seconds: f32) -> Self {
self.duration_seconds = duration_seconds;
self
}
pub fn keyframe(mut self, builder: impl KeyframeBuilder<Data = Data>) -> Self {
self.keyframes.push(builder.build());
self
}
pub fn repeat(mut self, repeat: Repeat) -> Self {
self.repeat = repeat;
self
}
pub fn reverse(mut self, reverse: bool) -> Self {
self.reverse = reverse;
self
}
fn create_timescale(&self) -> TimeScale {
TimeScale::new(
self.duration_seconds,
self.delay_seconds,
self.repeat,
self.reverse,
)
}
fn get_boundary_times(&self) -> Vec<f32> {
self.keyframes.iter().map(|k| k.normalized_time).collect()
}
}
#[derive(Clone, Debug)]
pub struct Keyframe<Data: Clone> {
pub(super) data: Data,
pub(super) easing: Option<Easing>,
pub(super) normalized_time: f32,
}
impl<Data: Clone> Keyframe<Data> {
pub fn new(normalized_time: f32, data: Data, easing: Option<Easing>) -> Self {
Self {
normalized_time,
data,
easing,
}
}
}
pub trait KeyframeBuilder {
type Data: Clone + Debug;
fn build(&self) -> Keyframe<Self::Data>;
fn easing(self, easing: Easing) -> Self;
}
pub struct MergedTimeline<T: Timeline> {
timelines: Vec<T>,
}
impl<T: Timeline> MergedTimeline<T> {
pub fn of(timelines: impl IntoIterator<Item = T>) -> Self {
Self {
timelines: timelines.into_iter().collect(),
}
}
}
impl<T: Timeline + Clone> Clone for MergedTimeline<T> {
fn clone(&self) -> Self {
MergedTimeline::of(self.timelines.iter().cloned())
}
}
impl<T: Timeline> From<T> for MergedTimeline<T> {
fn from(value: T) -> Self {
MergedTimeline::of([value])
}
}
impl<T: Timeline> Timeline for MergedTimeline<T> {
type Target = T::Target;
fn start_with(&mut self, values: &Self::Target) {
for timeline in self.timelines.iter_mut() {
timeline.start_with(values);
}
}
fn update(&self, values: &mut Self::Target, time: f32) {
for timeline in &self.timelines {
timeline.update(values, time);
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub enum Repeat {
#[default]
None,
Times(u32),
Infinite,
}
pub fn prepare_frame(
time: f32,
boundary_times: &[f32],
timescale: &TimeScale,
) -> Option<(f32, usize, bool)> {
if boundary_times.is_empty() {
return None;
}
let (normalized_time, enable_start_override) = match timescale.get_position(time) {
TimeScalePosition::Active(t, loop_state) => {
(t, !loop_state.is_repeating && !loop_state.is_reversing)
}
TimeScalePosition::NotStarted => (0.0, true),
TimeScalePosition::Ended(t) => (t, false),
};
let frame_index = match boundary_times.binary_search_by(|t| t.total_cmp(&normalized_time)) {
Ok(index) => index,
Err(next_index) => next_index.max(1) - 1,
};
Some((normalized_time, frame_index, enable_start_override))
}
#[cfg(test)]
mod tests {
use super::*;
use ordered_float::OrderedFloat;
use std::collections::HashMap;
#[derive(Debug, Default, PartialEq)]
struct TestValues {
foo: u8,
bar: u32,
baz: f32,
}
struct StubTimeline {
frames: HashMap<OrderedFloat<f32>, StubFrame>,
}
impl StubTimeline {
fn new() -> Self {
Self {
frames: HashMap::new(),
}
}
fn add_frame(
mut self,
time: f32,
foo: Option<u8>,
bar: Option<u32>,
baz: Option<f32>,
) -> Self {
self.frames
.insert(OrderedFloat(time), StubFrame { foo, bar, baz });
self
}
}
impl Timeline for StubTimeline {
type Target = TestValues;
fn start_with(&mut self, values: &Self::Target) {
if let Some(first_frame) = self.frames.get_mut(&OrderedFloat(0.0)) {
first_frame.foo = Some(values.foo);
first_frame.bar = Some(values.bar);
first_frame.baz = Some(values.baz);
}
}
fn update(&self, values: &mut Self::Target, time: f32) {
if let Some(frame) = self.frames.get(&OrderedFloat(time)) {
if let Some(foo) = frame.foo {
values.foo = foo;
}
if let Some(bar) = frame.bar {
values.bar = bar;
}
if let Some(baz) = frame.baz {
values.baz = baz;
}
}
}
}
struct StubFrame {
foo: Option<u8>,
bar: Option<u32>,
baz: Option<f32>,
}
#[test]
fn merged_timeline_delegates_to_component_timelines() {
let timeline1 = StubTimeline::new()
.add_frame(0.1, Some(10), Some(555), Some(0.12))
.add_frame(0.2, Some(20), None, None)
.add_frame(0.3, Some(30), Some(777), None);
let timeline2 = StubTimeline::new()
.add_frame(0.1, None, None, Some(1.5))
.add_frame(0.2, None, None, Some(2.5))
.add_frame(0.3, None, None, Some(6.8));
let merged_timeline = MergedTimeline::of([timeline1, timeline2]);
let mut values = <[TestValues; 3]>::default();
merged_timeline.update(&mut values[0], 0.1);
merged_timeline.update(&mut values[1], 0.2);
merged_timeline.update(&mut values[2], 0.3);
assert_eq!(
values[0],
TestValues {
foo: 10,
bar: 555,
baz: 1.5
}
);
assert_eq!(
values[1],
TestValues {
foo: 20,
bar: 0,
baz: 2.5
}
);
assert_eq!(
values[2],
TestValues {
foo: 30,
bar: 777,
baz: 6.8
}
);
}
}