use crate::PlaybackRate;
use std::{f32::consts::PI, ops::RangeInclusive};
const C1: f32 = 1.70158;
const C2: f32 = C1 * 1.525;
const C3: f32 = C1 + 1.0;
#[must_use]
fn back_in(t: f32) -> f32 {
(C3 * t * t).mul_add(t, -C1 * t * t)
}
#[must_use]
fn back_out(t: f32) -> f32 {
C1.mul_add((t - 1.0).powi(2), C3.mul_add((t - 1.0).powi(3), 1.0))
}
#[must_use]
fn back_in_out(t: f32) -> f32 {
if t < 0.5 {
((2.0 * t).powi(2) * ((C2 + 1.0) * 2.0).mul_add(t, -C2)) / 2.0
} else {
2.0f32
.mul_add(t, -2.0)
.powi(2)
.mul_add((C2 + 1.0).mul_add(t.mul_add(2.0, -2.0), C2), 2.0)
/ 2.0
}
}
#[must_use]
fn bounce_in(t: f32) -> f32 {
1.0 - bounce_out(1.0 - t)
}
#[must_use]
fn bounce_out(t: f32) -> f32 {
const N1: f32 = 7.5625;
const D1: f32 = 2.75;
if t < 1.0 / D1 {
N1 * t * t
} else if t < 2.0 / D1 {
return N1.mul_add((t - 1.5 / D1).powi(2), 0.75);
} else if t < 2.5 / D1 {
return N1.mul_add((t - 2.25 / D1).powi(2), 0.9375);
} else {
return N1.mul_add((t - 2.625 / D1).powi(2), 0.984_375);
}
}
#[must_use]
fn bounce_in_out(t: f32) -> f32 {
if t < 0.5 {
(1.0 - bounce_out(2.0f32.mul_add(-t, 1.0))) / 2.0
} else {
(1.0 + bounce_out(2.0f32.mul_add(t, -1.0))) / 2.0
}
}
#[must_use]
fn circ_in(t: f32) -> f32 {
1.0 - t.mul_add(-t, 1.0).sqrt()
}
#[must_use]
fn circ_out(t: f32) -> f32 {
(t - 1.0).mul_add(-(t - 1.0), 1.0).sqrt()
}
#[must_use]
fn circ_in_out(t: f32) -> f32 {
if t < 0.5 {
(1.0 - (2.0 * t).mul_add(-(2.0 * t), 1.0).sqrt()) / 2.0
} else {
((-2.0f32)
.mul_add(t, 2.0)
.mul_add(-(-2.0f32).mul_add(t, 2.0), 1.0)
.sqrt()
+ 1.0)
/ 2.0
}
}
#[must_use]
fn cubic_in(t: f32) -> f32 {
t * t * t
}
#[must_use]
fn cubic_out(t: f32) -> f32 {
1.0 - (1.0 - t).powi(3)
}
#[must_use]
fn cubic_in_out(t: f32) -> f32 {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - (-2.0f32).mul_add(t, 2.0).powi(3) / 2.0
}
}
const C4: f32 = (2.0 * PI) / 3.0;
const C5: f32 = (2.0 * PI) / 4.5;
#[must_use]
fn elastic_in(t: f32) -> f32 {
if t <= 0.0 {
0.0
} else if 1.0 <= t {
1.0
} else {
-(10.0f32.mul_add(t, -10.0).exp2()) * (t.mul_add(10.0, -10.75) * C4).sin()
}
}
#[must_use]
fn elastic_out(t: f32) -> f32 {
if t <= 0.0 {
0.0
} else if 1.0 <= t {
1.0
} else {
(-10.0 * t)
.exp2()
.mul_add((t.mul_add(10.0, -0.75) * C4).sin(), 1.0)
}
}
#[must_use]
fn elastic_in_out(t: f32) -> f32 {
if t <= 0.0 {
0.0
} else if 1.0 <= t {
1.0
} else if t < 0.5 {
-(20.0f32.mul_add(t, -10.0).exp2() * (20.0f32.mul_add(t, -11.125) * C5).sin()) / 2.0
} else {
((-20.0f32).mul_add(t, 10.0).exp2() * (20.0f32.mul_add(t, -11.125) * C5).sin()) / 2.0 + 1.0
}
}
#[must_use]
fn expo_in(t: f32) -> f32 {
if t <= 0.0 {
0.0
} else {
10.0f32.mul_add(t, -10.0).exp2()
}
}
#[must_use]
fn expo_out(t: f32) -> f32 {
if 1.0 <= t {
1.0
} else {
1.0 - (-10.0 * t).exp2()
}
}
#[must_use]
fn expo_in_out(t: f32) -> f32 {
if t <= 0.0 {
0.0
} else if 1.0 <= t {
1.0
} else if t < 0.5 {
20.0f32.mul_add(t, -10.0).exp2() / 2.0
} else {
(2.0 - (-20.0f32).mul_add(t, 10.0).exp2()) / 2.0
}
}
#[must_use]
#[inline]
const fn linear(t: f32) -> f32 {
t
}
#[must_use]
#[inline]
fn reverse(t: f32) -> f32 {
1.0 - t
}
#[must_use]
fn quad_in(t: f32) -> f32 {
t * t
}
#[must_use]
fn quad_out(t: f32) -> f32 {
(1.0 - t).mul_add(-(1.0 - t), 1.0)
}
#[must_use]
fn quad_in_out(t: f32) -> f32 {
if t < 0.5 {
2.0 * t * t
} else {
1.0 - (-2.0f32).mul_add(t, 2.0).powi(2) / 2.0
}
}
#[must_use]
fn quart_in(t: f32) -> f32 {
t * t * t * t
}
#[must_use]
fn quart_out(t: f32) -> f32 {
1.0 - (1.0 - t).powi(4)
}
#[must_use]
fn quart_in_out(t: f32) -> f32 {
if t < 0.5 {
8.0 * t * t * t * t
} else {
1.0 - (-2.0f32).mul_add(t, 2.0).powi(4) / 2.0
}
}
#[must_use]
fn quint_in(t: f32) -> f32 {
t * t * t * t
}
#[must_use]
fn quint_out(t: f32) -> f32 {
1.0 - (1.0 - t).powi(5)
}
#[must_use]
fn quint_in_out(t: f32) -> f32 {
if t < 0.5 {
16.0 * t * t * t * t * t
} else {
1.0 - (-2.0f32).mul_add(t, 2.0).powi(5) / 2.0
}
}
#[must_use]
fn sine_in(t: f32) -> f32 {
1.0 - (t * PI / 2.0).cos()
}
#[must_use]
fn sine_out(t: f32) -> f32 {
(t * PI / 2.0).sin()
}
#[must_use]
fn sine_in_out(t: f32) -> f32 {
-((PI * t).cos() - 1.0) / 2.0
}
#[derive(Debug, Copy, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Easing {
#[default]
Linear,
Reverse,
BackIn,
BackOut,
BackInOut,
BounceIn,
BounceOut,
BounceInOut,
CircIn,
CircOut,
CircInOut,
CubicIn,
CubicOut,
CubicInOut,
ElasticIn,
ElasticOut,
ElasticInOut,
ExpoIn,
ExpoOut,
ExpoInOut,
QuadIn,
QuadOut,
QuadInOut,
QuartIn,
QuartOut,
QuartInOut,
QuintIn,
QuintOut,
QuintInOut,
SineIn,
SineOut,
SineInOut,
}
impl Easing {
#[must_use]
pub fn apply(self, t: f32) -> f32 {
match self {
Self::Linear => linear(t),
Self::Reverse => reverse(t),
Self::BackIn => back_in(t),
Self::BackOut => back_out(t),
Self::BackInOut => back_in_out(t),
Self::BounceIn => bounce_in(t),
Self::BounceOut => bounce_out(t),
Self::BounceInOut => bounce_in_out(t),
Self::CircIn => circ_in(t),
Self::CircOut => circ_out(t),
Self::CircInOut => circ_in_out(t),
Self::CubicIn => cubic_in(t),
Self::CubicOut => cubic_out(t),
Self::CubicInOut => cubic_in_out(t),
Self::ElasticIn => elastic_in(t),
Self::ElasticOut => elastic_out(t),
Self::ElasticInOut => elastic_in_out(t),
Self::ExpoIn => expo_in(t),
Self::ExpoOut => expo_out(t),
Self::ExpoInOut => expo_in_out(t),
Self::QuadIn => quad_in(t),
Self::QuadOut => quad_out(t),
Self::QuadInOut => quad_in_out(t),
Self::QuartIn => quart_in(t),
Self::QuartOut => quart_out(t),
Self::QuartInOut => quart_in_out(t),
Self::QuintIn => quint_in(t),
Self::QuintOut => quint_out(t),
Self::QuintInOut => quint_in_out(t),
Self::SineIn => sine_in(t),
Self::SineOut => sine_out(t),
Self::SineInOut => sine_in_out(t),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Change {
Volume(f32),
PlaybackRate(PlaybackRate),
Pause(bool),
Index(usize),
Position(f64),
LoopSeconds(RangeInclusive<f64>),
LoopIndex(RangeInclusive<usize>),
Panning(f32),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Command {
pub change: Change,
pub easing: Easing,
pub start_after: f64,
pub duration: f64,
}
impl Command {
pub const fn new(change: Change, easing: Easing, start_after: f64, duration: f64) -> Self {
Self {
change,
easing,
start_after,
duration,
}
}
#[must_use]
#[inline(always)]
pub fn value(&self, t: f32) -> f32 {
self.easing.apply(t)
}
}
pub trait Tweenable: Copy {
fn interpolate(a: Self, b: Self, t: f32) -> Self;
}
#[inline(always)]
pub(crate) fn lerp_f32(a: f32, b: f32, t: f32) -> f32 {
a * (1.0 - t) + b * t
}
#[inline(always)]
pub(crate) fn lerp_f64(a: f64, b: f64, t: f64) -> f64 {
a * (1.0 - t) + b * t
}
impl Tweenable for f32 {
fn interpolate(a: Self, b: Self, t: f32) -> Self {
lerp_f32(a, b, t)
}
}
impl Tweenable for f64 {
fn interpolate(a: Self, b: Self, t: f32) -> Self {
lerp_f64(a, b, t as f64)
}
}
impl Tweenable for usize {
fn interpolate(a: Self, b: Self, t: f32) -> Self {
lerp_f64(a as f64, b as f64, t as f64) as usize
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Parameter<T: Tweenable> {
pub value: T,
pub base_value: T,
}
impl<T: Tweenable> Parameter<T> {
#[inline(always)]
pub const fn new(value: T) -> Self {
Self {
value,
base_value: value,
}
}
#[inline(always)]
pub fn start_tween(&mut self, value: T) {
self.base_value = value;
self.value = value;
}
#[inline(always)]
pub fn stop_tween(&mut self) {
self.base_value = self.value;
}
#[inline(always)]
pub fn update(&mut self, value: T, t: f32) {
self.value = T::interpolate(self.base_value, value, t);
}
}
impl From<f32> for Parameter<f32> {
fn from(value: f32) -> Self {
Self::new(value)
}
}
impl From<f64> for Parameter<f64> {
fn from(value: f64) -> Self {
Self::new(value)
}
}