use crate::{Tween, TweenTime, TweenValue};
mod extrapolator;
mod looper;
mod oscillator;
pub use extrapolator::Extrapolator;
pub use looper::Looper;
pub use oscillator::Oscillator;
#[derive(Debug, PartialEq, Clone, PartialOrd, Eq, Ord, Copy)]
pub struct Tweener<Value, Time, T: ?Sized> {
pub current_time: Time,
pub duration: Time,
values: (Value, Value),
value_delta: Value,
pub tween: T,
}
impl<Value, Time, T> Tweener<Value, Time, T>
where
Time: TweenTime,
Value: TweenValue,
T: Tween<Value>,
{
pub fn new(start: Value, end: Value, duration: Time, tween: T) -> Self {
Self {
values: (start, end),
value_delta: end - start,
duration,
tween,
current_time: Time::ZERO,
}
}
pub fn new_at(start: Value, end: Value, duration: Time, tween: T, current_time: Time) -> Self {
Self {
values: (start, end),
value_delta: end - start,
duration,
tween,
current_time,
}
}
pub fn map<R: Tween<Value>>(self, mut f: impl FnMut(T) -> R) -> Tweener<Value, Time, R> {
Tweener {
current_time: self.current_time,
duration: self.duration,
values: self.values,
value_delta: self.value_delta,
tween: f(self.tween),
}
}
#[inline(always)]
pub fn move_to(&mut self, position: Time) -> Value {
self.current_time = position;
let pct = position.to_f32() / self.duration.to_f32();
if self.tween.is_finite() {
if pct < 0.0 {
return self.values.0;
} else if pct > 1.0 {
return self.values.1;
}
}
self.tween.tween(self.value_delta, pct) + self.values.0
}
#[inline]
pub fn move_by(&mut self, delta: Time) -> Value {
self.current_time += delta;
self.move_to(self.current_time)
}
#[inline]
pub fn initial_value(&self) -> Value {
self.values.0
}
#[inline]
pub fn final_value(&self) -> Value {
self.values.1
}
pub fn is_started(&self) -> bool {
self.current_time_state() != CurrentTimeState::Waiting
}
pub fn is_finished(&self) -> bool {
self.current_time_state() == CurrentTimeState::Finished
}
pub fn is_valid(&self) -> bool {
self.current_time_state() == CurrentTimeState::Valid
}
pub fn current_time_state(&self) -> CurrentTimeState {
if self.tween.is_finite() {
let pct = self.current_time.to_f32() / self.duration.to_f32();
if pct < 0.0 {
CurrentTimeState::Waiting
} else if pct >= 1.0 {
CurrentTimeState::Finished
} else {
CurrentTimeState::Valid
}
} else {
CurrentTimeState::Valid
}
}
pub fn into_fixed(self, delta: Time) -> FixedTweener<Value, Time, T> {
FixedTweener::from_tweener(self, delta)
}
}
#[derive(Debug, PartialEq, Clone, PartialOrd, Eq, Ord, Copy)]
pub struct FixedTweener<Value, Time, T: ?Sized> {
pub delta: Time,
pub tweener: Tweener<Value, Time, T>,
}
impl<Value, Time, T> FixedTweener<Value, Time, T>
where
Value: TweenValue,
Time: TweenTime,
T: Tween<Value>,
{
pub fn new(start: Value, end: Value, duration: Time, tween: T, delta: Time) -> Self {
Self::from_tweener(Tweener::new(start, end, duration, tween), delta)
}
pub fn new_at(start: Value, end: Value, duration: Time, tween: T, current_time: Time, delta: Time) -> Self {
Self::from_tweener(Tweener::new_at(start, end, duration, tween, current_time), delta)
}
pub fn from_tweener(tweener: Tweener<Value, Time, T>, delta: Time) -> Self {
Self { tweener, delta }
}
#[inline]
pub fn move_next(&mut self) -> Value {
self.tweener.move_by(self.delta)
}
}
impl<Value, Time, T> core::ops::Deref for FixedTweener<Value, Time, T> {
type Target = Tweener<Value, Time, T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.tweener
}
}
impl<Value, Time, T> core::ops::DerefMut for FixedTweener<Value, Time, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.tweener
}
}
impl<Value, Time, T> Iterator for FixedTweener<Value, Time, T>
where
Value: TweenValue,
Time: TweenTime,
T: Tween<Value>,
{
type Item = Value;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.tweener.is_valid() {
Some(self.move_next())
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CurrentTimeState {
Waiting,
Valid,
Finished,
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use super::*;
use crate::{BounceIn, BounceInOut, BounceOut, ElasticIn, Linear};
#[test]
fn tweener() {
let mut tweener = Tweener::new(0, 100, 10, Linear);
let values: std::vec::Vec<_> = (0..10).map(|_| tweener.move_by(1)).collect();
assert_eq!(*values, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
}
#[test]
fn fixed_tweener() {
let mut tweener = Tweener::new(0, 100, 10, Linear).into_fixed(1);
let values: std::vec::Vec<_> = (0..10).map(|_| tweener.next().unwrap()).collect();
assert_eq!(*values, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
let mut fixed_tweener = Tweener::new(0, 4, 4, Linear).into_fixed(1);
assert_eq!(fixed_tweener.next().unwrap(), 1);
assert_eq!(fixed_tweener.next().unwrap(), 2);
assert_eq!(fixed_tweener.next().unwrap(), 3);
assert_eq!(fixed_tweener.next().unwrap(), 4);
assert!(fixed_tweener.is_finished());
assert_eq!(fixed_tweener.next(), None);
}
#[test]
fn tweener_weird() {
let mut tweener = Tweener::new(0, 2, 2, Linear);
assert_eq!(tweener.move_by(0), 0);
assert_eq!(tweener.move_by(1), 1);
assert_eq!(tweener.move_by(1), 2);
assert_eq!(tweener.move_by(0), 2);
assert_eq!(tweener.move_by(0), 2);
assert_eq!(tweener.move_by(0), 2);
}
#[test]
fn bounds_checker() {
fn checker<T>(mut tweener: Tweener<i32, i32, T>)
where
T: Tween<i32>,
{
fn move_and_return<T>(
tweener: &mut Tweener<i32, i32, T>,
f: impl FnOnce(&Tweener<i32, i32, T>) -> bool,
) -> bool
where
T: Tween<i32>,
{
tweener.move_by(1);
f(tweener)
}
assert!(move_and_return(&mut tweener, |t| !t.is_finished()));
assert!(move_and_return(&mut tweener, |t| t.is_finished()));
tweener.move_to(-2);
assert!(move_and_return(&mut tweener, |t| !t.is_started()));
assert!(move_and_return(&mut tweener, |t| t.is_started()));
assert!(move_and_return(&mut tweener, |t| t.is_started()));
assert!(move_and_return(&mut tweener, |t| t.is_started()));
assert!(move_and_return(&mut tweener, |t| t.is_started()));
tweener.move_to(-2);
assert!(move_and_return(&mut tweener, |t| !t.is_valid()));
assert!(move_and_return(&mut tweener, |t| t.is_valid()));
assert!(move_and_return(&mut tweener, |t| t.is_valid()));
assert!(move_and_return(&mut tweener, |t| !t.is_valid()));
}
checker(Tweener::new(0, 2, 2, Linear));
checker(Tweener::new(0, 2, 2, ElasticIn));
checker(Tweener::new(0, 2, 2, BounceInOut));
checker(Tweener::new(0, 2, 2, BounceIn));
checker(Tweener::new(0, 2, 2, BounceOut));
}
#[test]
fn shortcuts() {
Tweener::back_in(0, 0, 0);
Tweener::back_out(0, 0, 0);
Tweener::back_in_out(0, 0, 0);
Tweener::bounce_in(0, 0, 0);
Tweener::bounce_out(0, 0, 0);
Tweener::bounce_in_out(0, 0, 0);
Tweener::circ_in(0, 0, 0);
Tweener::circ_out(0, 0, 0);
Tweener::circ_in_out(0, 0, 0);
Tweener::cubic_in(0, 0, 0);
Tweener::cubic_out(0, 0, 0);
Tweener::cubic_in_out(0, 0, 0);
Tweener::elastic_in(0, 0, 0);
Tweener::elastic_out(0, 0, 0);
Tweener::elastic_in_out(0, 0, 0);
Tweener::expo_in(0, 0, 0);
Tweener::expo_out(0, 0, 0);
Tweener::expo_in_out(0, 0, 0);
Tweener::linear(0, 0, 0);
Tweener::quad_in(0, 0, 0);
Tweener::quad_out(0, 0, 0);
Tweener::quad_in_out(0, 0, 0);
Tweener::quart_in(0, 0, 0);
Tweener::quart_out(0, 0, 0);
Tweener::quart_in_out(0, 0, 0);
Tweener::quint_in(0, 0, 0);
Tweener::quint_out(0, 0, 0);
Tweener::quint_in_out(0, 0, 0);
Tweener::sine_in(0, 0, 0);
Tweener::sine_out(0, 0, 0);
Tweener::sine_in_out(0, 0, 0);
Tweener::back_in_at(0, 0, 0, 0);
Tweener::back_out_at(0, 0, 0, 0);
Tweener::back_in_out_at(0, 0, 0, 0);
Tweener::bounce_in_at(0, 0, 0, 0);
Tweener::bounce_out_at(0, 0, 0, 0);
Tweener::bounce_in_out_at(0, 0, 0, 0);
Tweener::circ_in_at(0, 0, 0, 0);
Tweener::circ_out_at(0, 0, 0, 0);
Tweener::circ_in_out_at(0, 0, 0, 0);
Tweener::cubic_in_at(0, 0, 0, 0);
Tweener::cubic_out_at(0, 0, 0, 0);
Tweener::cubic_in_out_at(0, 0, 0, 0);
Tweener::elastic_in_at(0, 0, 0, 0);
Tweener::elastic_out_at(0, 0, 0, 0);
Tweener::elastic_in_out_at(0, 0, 0, 0);
Tweener::expo_in_at(0, 0, 0, 0);
Tweener::expo_out_at(0, 0, 0, 0);
Tweener::expo_in_out_at(0, 0, 0, 0);
Tweener::linear_at(0, 0, 0, 0);
Tweener::quad_in_at(0, 0, 0, 0);
Tweener::quad_out_at(0, 0, 0, 0);
Tweener::quad_in_out_at(0, 0, 0, 0);
Tweener::quart_in_at(0, 0, 0, 0);
Tweener::quart_out_at(0, 0, 0, 0);
Tweener::quart_in_out_at(0, 0, 0, 0);
Tweener::quint_in_at(0, 0, 0, 0);
Tweener::quint_out_at(0, 0, 0, 0);
Tweener::quint_in_out_at(0, 0, 0, 0);
Tweener::sine_in_at(0, 0, 0, 0);
Tweener::sine_out_at(0, 0, 0, 0);
Tweener::sine_in_out_at(0, 0, 0, 0);
}
}