use crate::{
easing::{Easing, EasingFunction},
interpolation::Lerp,
timeline::Keyframe,
};
use std::fmt::Debug;
#[derive(Clone, Debug)]
pub struct SubTimeline<Value: Clone> {
frames: Vec<SplitKeyframe<Value>>,
frame_index_map: Vec<usize>,
start_frame_override: Option<SplitKeyframe<Value>>,
}
impl<Value: Clone + Lerp> SubTimeline<Value> {
pub fn from_keyframes<'a, Data: 'a + Clone + Debug, ValueFn>(
keyframes: impl IntoIterator<Item = &'a Keyframe<Data>>,
default_value: Value,
get_value: ValueFn,
default_easing: Easing,
) -> Self
where
ValueFn: Fn(&Data) -> Option<Value>,
{
let mut converted_frames = Vec::new();
let mut frame_index_map = Vec::new();
let mut current_easing = default_easing;
let mut has_frame_data = false;
for keyframe in keyframes.into_iter() {
if converted_frames.is_empty() && keyframe.normalized_time > 0.0 {
converted_frames.push(SplitKeyframe::new(
0.0,
default_value.clone(),
current_easing.clone(),
));
}
if let Some(data) = get_value(&keyframe.data) {
has_frame_data = true;
if let Some(easing) = &keyframe.easing {
current_easing = easing.clone();
}
converted_frames.push(SplitKeyframe::new(
keyframe.normalized_time,
data,
current_easing.clone(),
));
}
frame_index_map.push(converted_frames.len().max(1) - 1);
}
if !has_frame_data {
return Self::empty();
}
let trailing_frame = match converted_frames.last() {
Some(frame) if frame.normalized_time < 1.0 =>
{
Some(frame.with_time(1.0))
}
_ => None,
};
if let Some(trailing_frame) = trailing_frame {
converted_frames.push(trailing_frame);
}
Self {
frames: converted_frames,
frame_index_map,
start_frame_override: None,
}
}
pub fn override_start_value(&mut self, value: Value) {
if let Some(first_frame) = self.frames.first() {
self.start_frame_override = Some(first_frame.with_value(value));
}
}
pub fn value_at(
&self,
normalized_time: f32,
index_hint: usize,
enable_start_override: bool,
) -> Option<Value> {
if self.frame_index_map.is_empty() {
return None;
}
let normalized_time = normalized_time.clamp(0.0, 1.0);
let bounding_frames =
self.get_bounding_frames(normalized_time, index_hint, enable_start_override)?;
Some(interpolate_value(&bounding_frames, normalized_time))
}
fn empty() -> Self {
Self {
frame_index_map: vec![],
frames: vec![],
start_frame_override: None,
}
}
fn get_bounding_frames(
&self,
normalized_time: f32,
index_hint: usize,
enable_start_override: bool,
) -> Option<[&SplitKeyframe<Value>; 2]> {
let index_at = *self.frame_index_map.get(index_hint)?;
let frame_at = self.get_frame(index_at, enable_start_override)?;
if normalized_time < frame_at.normalized_time {
if index_at > 0 {
Some([
self.get_frame(index_at - 1, enable_start_override)?,
frame_at,
])
} else {
None
}
} else if index_at == self.frames.len() - 1 {
Some([frame_at, frame_at])
} else {
self.frames
.get(index_at + 1)
.map(|next_frame| [frame_at, next_frame])
}
}
fn get_frame(
&self,
index: usize,
enable_start_override: bool,
) -> Option<&SplitKeyframe<Value>> {
if enable_start_override && index == 0 {
if let Some(ref override_frame) = self.start_frame_override {
Some(override_frame)
} else {
self.frames.get(0)
}
} else {
self.frames.get(index)
}
}
}
#[derive(Clone, Debug)]
struct SplitKeyframe<Value: Clone> {
easing: Easing,
normalized_time: f32,
value: Value,
}
impl<Value: Clone> SplitKeyframe<Value> {
fn new(normalized_time: f32, value: Value, easing: Easing) -> Self {
Self {
normalized_time,
value,
easing,
}
}
fn with_time(&self, normalized_time: f32) -> Self {
SplitKeyframe::new(normalized_time, self.value.clone(), self.easing.clone())
}
fn with_value(&self, value: Value) -> Self {
SplitKeyframe::new(self.normalized_time, value, self.easing.clone())
}
}
fn interpolate_value<Value: Clone + Lerp>(
bounding_frames: &[&SplitKeyframe<Value>; 2],
time: f32,
) -> Value {
let [start_frame, end_frame] = bounding_frames;
let duration = end_frame.normalized_time - start_frame.normalized_time;
if duration == 0.0 {
return start_frame.value.clone();
}
let easing = &start_frame.easing;
let x = (time - start_frame.normalized_time) / duration;
let y = easing.calc(x);
start_frame.value.lerp(&end_frame.value, y)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::timeline::Timeline;
#[derive(Clone, Debug, Default, PartialEq)]
struct TestValues {
foo: u8,
bar: f32,
}
impl TestValues {
pub fn new(foo: u8, bar: f32) -> Self {
Self { foo, bar }
}
pub fn round(&self) -> Self {
Self {
foo: self.foo,
bar: self.bar.round(),
}
}
}
#[derive(Clone, Debug)]
struct TestKeyframeData {
foo: Option<u8>,
bar: Option<f32>,
}
impl TestKeyframeData {
fn new(foo: Option<u8>, bar: Option<f32>) -> Self {
Self { foo, bar }
}
fn full(foo: u8, bar: f32) -> Self {
Self {
foo: Some(foo),
bar: Some(bar),
}
}
}
#[derive(Debug)]
struct TestTimeline {
boundary_times: Vec<f32>,
foo: SubTimeline<u8>,
bar: SubTimeline<f32>,
}
impl TestTimeline {
fn new(keyframes: Vec<Keyframe<TestKeyframeData>>, default_easing: Easing) -> Self {
let defaults = TestValues::default();
Self {
foo: SubTimeline::from_keyframes(
&keyframes,
defaults.foo,
|k| k.foo,
default_easing.clone(),
),
bar: SubTimeline::from_keyframes(
&keyframes,
defaults.bar,
|k| k.bar,
default_easing,
),
boundary_times: keyframes.iter().map(|k| k.normalized_time).collect(),
}
}
fn update_with_override(
&self,
target: &mut TestValues,
time: f32,
enable_start_override: bool,
) {
if self.boundary_times.is_empty() {
return;
}
let frame_index = match self.boundary_times.binary_search_by(|t| t.total_cmp(&time)) {
Ok(index) => index,
Err(next_index) => next_index.max(1) - 1,
};
if let Some(foo) = self.foo.value_at(time, frame_index, enable_start_override) {
target.foo = foo;
}
if let Some(bar) = self.bar.value_at(time, frame_index, enable_start_override) {
target.bar = bar;
}
}
fn values_at(&self, time: f32) -> TestValues {
let mut target = TestValues::default();
self.update(&mut target, time);
target
}
fn non_overridden_values_at(&self, time: f32) -> TestValues {
let mut target = TestValues::default();
self.update_with_override(&mut target, time, false);
target
}
fn updated_values_at<'a>(&self, values: &TestValues, time: f32) -> TestValues {
let mut updated_values = values.clone();
self.update(&mut updated_values, time);
updated_values
}
}
impl Timeline for TestTimeline {
type Target = TestValues;
fn start_with(&mut self, values: &Self::Target) {
self.foo.override_start_value(values.foo);
self.bar.override_start_value(values.bar);
}
fn update(&self, target: &mut Self::Target, time: f32) {
self.update_with_override(target, time, true);
}
}
#[test]
fn when_empty_then_does_not_modify() {
let timeline = TestTimeline::new(vec![], Easing::default());
let initial_values = TestValues::new(123, 583.122);
assert_eq!(
timeline.updated_values_at(&initial_values, 0.0),
initial_values
);
assert_eq!(
timeline.updated_values_at(&initial_values, 0.5),
initial_values
);
assert_eq!(
timeline.updated_values_at(&initial_values, 1.0),
initial_values
);
}
#[test]
fn when_property_not_used_then_does_not_modify_property() {
let keyframes = vec![
Keyframe::new(0.4, TestKeyframeData::new(Some(40), None), None),
Keyframe::new(0.6, TestKeyframeData::new(Some(50), None), None),
Keyframe::new(0.6, TestKeyframeData::new(Some(80), None), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
let initial_values = TestValues::new(100, 0.123);
assert_eq!(
timeline.updated_values_at(&initial_values, 0.0),
TestValues::new(0, 0.123)
);
assert_eq!(
timeline.updated_values_at(&initial_values, 0.5),
TestValues::new(45, 0.123)
);
assert_eq!(
timeline.updated_values_at(&initial_values, 1.0),
TestValues::new(80, 0.123)
);
}
#[test]
fn when_no_keyframe_at_zero_then_interpolates_from_defaults() {
let keyframes = vec![
Keyframe::new(0.25, TestKeyframeData::new(None, Some(50.0)), None),
Keyframe::new(0.5, TestKeyframeData::new(Some(80), Some(200.0)), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.0), TestValues::default());
assert_eq!(timeline.values_at(0.1), TestValues::new(16, 20.0));
assert_eq!(timeline.values_at(0.25), TestValues::new(40, 50.0));
}
#[test]
fn when_full_keyframe_at_zero_then_interpolates_from_first_keyframe() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::full(10, 20.0), None),
Keyframe::new(0.4, TestKeyframeData::full(50, 200.0), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.0), TestValues::new(10, 20.0));
assert_eq!(timeline.values_at(0.2), TestValues::new(30, 110.0));
assert_eq!(timeline.values_at(0.4), TestValues::new(50, 200.0));
}
#[test]
fn when_partial_keyframe_at_zero_then_interpolates_from_defaults_and_first_keyframe() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::new(Some(10), None), None),
Keyframe::new(0.4, TestKeyframeData::full(50, 200.0), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.0), TestValues::new(10, 0.0));
assert_eq!(timeline.values_at(0.2), TestValues::new(30, 100.0));
assert_eq!(timeline.values_at(0.4), TestValues::new(50, 200.0));
}
#[test]
fn when_no_keyframe_at_end_then_stays_at_last_keyframe() {
let keyframes = vec![
Keyframe::new(0.5, TestKeyframeData::new(Some(30), None), None),
Keyframe::new(0.75, TestKeyframeData::new(Some(50), Some(1000.0)), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.75), TestValues::new(50, 1000.0));
assert_eq!(timeline.values_at(0.85), TestValues::new(50, 1000.0));
assert_eq!(timeline.values_at(1.0), TestValues::new(50, 1000.0));
}
#[test]
fn when_keyframe_at_end_then_interpolates_to_last_keyframe() {
let keyframes = vec![
Keyframe::new(0.25, TestKeyframeData::full(40, 250.0), None),
Keyframe::new(0.5, TestKeyframeData::full(20, 0.0), None),
Keyframe::new(1.0, TestKeyframeData::full(60, 1000.0), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.5), TestValues::new(20, 0.0));
assert_eq!(timeline.values_at(0.75), TestValues::new(40, 500.0));
assert_eq!(timeline.values_at(1.0), TestValues::new(60, 1000.0));
}
#[test]
fn when_easing_not_overridden_then_interpolates_with_default_easing() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::full(0, 0.0), None),
Keyframe::new(1.0, TestKeyframeData::full(40, 100.0), None),
];
let timeline = TestTimeline::new(keyframes, Easing::OutQuad);
assert_eq!(timeline.values_at(0.0).round(), TestValues::new(0, 0.0));
assert_eq!(timeline.values_at(0.2).round(), TestValues::new(20, 49.0));
assert_eq!(timeline.values_at(0.4).round(), TestValues::new(31, 78.0));
assert_eq!(timeline.values_at(0.6).round(), TestValues::new(37, 94.0));
assert_eq!(timeline.values_at(0.8).round(), TestValues::new(40, 99.0));
assert_eq!(timeline.values_at(1.0).round(), TestValues::new(40, 100.0));
}
#[test]
fn when_easing_overridden_then_uses_new_easing() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::full(0, 0.0), None),
Keyframe::new(0.2, TestKeyframeData::full(50, 100.0), None),
Keyframe::new(
0.4,
TestKeyframeData::full(100, 400.0),
Some(Easing::OutCirc),
),
Keyframe::new(0.6, TestKeyframeData::full(150, 1000.0), None),
Keyframe::new(
0.8,
TestKeyframeData::full(200, 5000.0),
Some(Easing::InSine),
),
Keyframe::new(1.0, TestKeyframeData::full(250, 10000.0), None),
];
let timeline = TestTimeline::new(keyframes, Easing::default());
assert_eq!(timeline.values_at(0.0).round(), TestValues::new(0, 0.0));
assert_eq!(timeline.values_at(0.1).round(), TestValues::new(25, 50.0));
assert_eq!(timeline.values_at(0.2).round(), TestValues::new(50, 100.0));
assert_eq!(timeline.values_at(0.3).round(), TestValues::new(75, 250.0));
assert_eq!(timeline.values_at(0.4).round(), TestValues::new(100, 400.0));
assert_eq!(timeline.values_at(0.5).round(), TestValues::new(135, 824.0));
assert_eq!(
timeline.values_at(0.6).round(),
TestValues::new(150, 1000.0)
);
assert_eq!(
timeline.values_at(0.7).round(),
TestValues::new(185, 3825.0)
);
assert_eq!(
timeline.values_at(0.8).round(),
TestValues::new(200, 5000.0)
);
assert_eq!(
timeline.values_at(0.9).round(),
TestValues::new(206, 5625.0)
);
assert_eq!(
timeline.values_at(1.0).round(),
TestValues::new(250, 10000.0)
);
}
#[test]
fn when_start_value_overridden_then_updates_if_non_empty() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::new(Some(10), None), None),
Keyframe::new(0.5, TestKeyframeData::new(Some(15), None), None),
Keyframe::new(1.0, TestKeyframeData::new(Some(50), None), None),
];
let mut timeline = TestTimeline::new(keyframes, Easing::default());
timeline.start_with(&TestValues::new(5, 8.5));
assert_eq!(timeline.values_at(0.0), TestValues::new(5, 0.0));
assert_eq!(timeline.values_at(0.25), TestValues::new(10, 0.0));
assert_eq!(timeline.values_at(0.5), TestValues::new(15, 0.0));
assert_eq!(timeline.values_at(1.0), TestValues::new(50, 0.0));
}
#[test]
fn when_start_override_disabled_then_interpolates_with_original_keyframe() {
let keyframes = vec![
Keyframe::new(0.0, TestKeyframeData::new(Some(10), None), None),
Keyframe::new(0.5, TestKeyframeData::new(Some(15), None), None),
Keyframe::new(1.0, TestKeyframeData::new(Some(50), None), None),
];
let mut timeline = TestTimeline::new(keyframes, Easing::default());
timeline.start_with(&TestValues::new(5, 8.5));
assert_eq!(
timeline.non_overridden_values_at(0.0),
TestValues::new(10, 0.0)
);
assert_eq!(
timeline.non_overridden_values_at(0.25),
TestValues::new(12, 0.0)
);
assert_eq!(
timeline.non_overridden_values_at(0.5),
TestValues::new(15, 0.0)
);
assert_eq!(
timeline.non_overridden_values_at(1.0),
TestValues::new(50, 0.0)
);
}
}