1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use alloc::{boxed::Box, fmt, sync::Arc};
use num_traits::Float;

use crate::{
	as_f64, ease_with_scaled_time,
	easing::{EaseInOut, Linear},
	CanTween, EasingFunction,
};

/// Intermediate step in an animation sequence
#[derive(Clone)]
pub struct Keyframe<T> {
	value: T,
	pub(crate) time: f64,
	function: Arc<dyn EasingFunction + Send + Sync>,
}

impl<T> Keyframe<T> {
	/// Creates a new keyframe from the specified values.
	/// If the time value is negative the keyframe will start at 0.0.
	///
	/// # Arguments
	/// * `value` - The value that this keyframe will be tweened to/from
	/// * `time` - The start time in seconds of this keyframe
	/// * `function` - The easing function to use from the start of this keyframe to the start of the next keyframe
	#[inline]
	pub fn new<F: Float>(value: T, time: F, function: impl EasingFunction + 'static + Send + Sync) -> Self {
		Keyframe::<T> {
			value,
			time: if time < F::zero() { 0.0 } else { as_f64(time) },
			function: Arc::new(function),
		}
	}

	/// Same as [`new`](#method.new), but allows you to supply an easing function which size is not known at compile time.
	///
	/// # Arguments
	/// * `value` - The value that this keyframe will be tweened to/from
	/// * `time` - The start time in seconds of this keyframe
	/// * `function` - The easing function to use from the start of this keyframe to the start of the next keyframe
	#[inline]
	pub fn new_dynamic<F: Float>(value: T, time: F, function: Box<dyn EasingFunction + 'static + Send + Sync>) -> Self {
		Keyframe::<T> {
			value,
			time: if time < F::zero() { 0.0 } else { as_f64(time) },
			function: function.into(),
		}
	}

	/// The value of this keyframe
	#[inline]
	pub fn value(&self) -> T
	where
		T: Clone,
	{
		self.value.clone()
	}

	/// The time in seconds at which this keyframe starts in a sequence
	#[inline]
	pub fn time(&self) -> f64 {
		self.time
	}

	/// The easing function that will be used when tweening to another keyframe
	#[inline]
	pub fn function(&self) -> &dyn EasingFunction {
		self.function.as_ref()
	}

	/// Returns the value between this keyframe and the next keyframe at the specified time
	///
	/// # Note
	///
	/// The following applies if:
	/// * The requested time is before the start time of this keyframe: the value of this keyframe is returned
	/// * The requested time is after the start time of next keyframe: the value of the next keyframe is returned
	/// * The start time of the next keyframe is before the start time of this keyframe: the value of the next keyframe is returned
	#[inline]
	pub fn tween_to(&self, next: &Keyframe<T>, time: impl Float) -> T
	where
		T: CanTween + Clone,
	{
		match as_f64(time) {
			// If the requested time starts before this keyframe
			time if time < self.time => self.value.clone(),
			// If the requested time starts after the next keyframe
			time if time > next.time => next.value.clone(),
			// If the next keyframe starts before this keyframe
			_ if next.time < self.time => next.value.clone(),

			time => T::ease(
				self.value.clone(),
				next.value.clone(),
				self.function.y(ease_with_scaled_time(
					Linear,
					0.0,
					1.0,
					time - self.time,
					next.time - self.time,
				)),
			),
		}
	}
}

impl<V, T: Float> From<(V, T)> for Keyframe<V> {
	/// Creates a new keyframe from a tuple of (value, time).
	/// `EaseInOut` will be used as the easing function.
	/// If the time value is negative the keyframe will start at 0.0.
	#[inline]
	fn from(tuple: (V, T)) -> Self {
		Keyframe::new(tuple.0, as_f64(tuple.1), EaseInOut)
	}
}

impl<V, T: Float, F: EasingFunction + 'static + Send + Sync> From<(V, T, F)> for Keyframe<V> {
	/// Creates a new keyframe from a tuple of (value, time, function).
	/// If the time value is negative the keyframe will start at 0.0.
	#[inline]
	fn from(tuple: (V, T, F)) -> Self {
		Keyframe::new(tuple.0, as_f64(tuple.1), tuple.2)
	}
}

impl<V, T: Float> From<(V, T, Box<dyn EasingFunction + 'static + Send + Sync>)> for Keyframe<V> {
	/// Creates a new keyframe from a tuple of (value, time, function).
	/// If the time value is negative the keyframe will start at 0.0.
	#[inline]
	fn from(tuple: (V, T, Box<dyn EasingFunction + 'static + Send + Sync>)) -> Self {
		Keyframe::new_dynamic(tuple.0, as_f64(tuple.1), tuple.2)
	}
}

impl<T: fmt::Display> fmt::Display for Keyframe<T> {
	#[inline]
	fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
		write!(f, "Keyframe at {:.2} s: {}", self.time, self.value)
	}
}

impl<T: core::fmt::Debug> fmt::Debug for Keyframe<T> {
	#[inline]
	fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
		write!(f, "Keyframe {{ value: {:?}, time: {:.2} }}", self.value, self.time)
	}
}