use std::f32::consts::PI;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum Easing {
#[default]
Linear,
InSine,
OutSine,
InOutSine,
InQuad,
OutQuad,
InOutQuad,
InCubic,
OutCubic,
InOutCubic,
InQuart,
OutQuart,
InOutQuart,
InQuint,
OutQuint,
InOutQuint,
InExpo,
OutExpo,
InOutExpo,
InCirc,
OutCirc,
InOutCirc,
InBack,
OutBack,
InOutBack,
InElastic,
OutElastic,
InOutElastic,
InBounce,
OutBounce,
InOutBounce,
CubicBezier(f32, f32, f32, f32),
}
impl Easing {
pub fn apply(self, progress: f32) -> f32 {
let x = progress.clamp(0.0, 1.0);
match self {
Easing::Linear => x,
Easing::InSine => 1.0 - ((x * PI) / 2.0).cos(),
Easing::OutSine => ((x * PI) / 2.0).sin(),
Easing::InOutSine => -((PI * x).cos() - 1.0) / 2.0,
Easing::InQuad => x.powi(2),
Easing::OutQuad => 1.0 - (1.0 - x).powi(2),
Easing::InOutQuad => {
if x < 0.5 {
2.0 * x.powi(2)
} else {
1.0 - (-2.0 * x + 2.0).powi(2) / 2.0
}
}
Easing::InCubic => x.powi(3),
Easing::OutCubic => 1.0 - (1.0 - x).powi(3),
Easing::InOutCubic => {
if x < 0.5 {
4.0 * x.powi(3)
} else {
1.0 - (-2.0 * x + 2.0).powi(3) / 2.0
}
}
Easing::InQuart => x.powi(4),
Easing::OutQuart => 1.0 - (1.0 - x).powi(4),
Easing::InOutQuart => {
if x < 0.5 {
8.0 * x.powi(4)
} else {
1.0 - (-2.0 * x + 2.0).powi(4) / 2.0
}
}
Easing::InQuint => x.powi(5),
Easing::OutQuint => 1.0 - (1.0 - x).powi(5),
Easing::InOutQuint => {
if x < 0.5 {
16.0 * x.powi(5)
} else {
1.0 - (-2.0 * x + 2.0).powi(5) / 2.0
}
}
Easing::InExpo => {
if x == 0.0 {
0.0
} else {
2.0_f32.powf(10.0 * x - 10.0)
}
}
Easing::OutExpo => {
if x == 1.0 {
1.0
} else {
1.0 - 2.0_f32.powf(-10.0 * x)
}
}
Easing::InOutExpo => {
if x == 0.0 {
0.0
} else if x == 1.0 {
1.0
} else if x < 0.5 {
2.0_f32.powf(20.0 * x - 10.0) / 2.0
} else {
(2.0 - 2.0_f32.powf(-20.0 * x + 10.0)) / 2.0
}
}
Easing::InCirc => 1.0 - (1.0 - x.powi(2)).sqrt(),
Easing::OutCirc => (1.0 - (x - 1.0).powi(2)).sqrt(),
Easing::InOutCirc => {
if x < 0.5 {
(1.0 - (1.0 - (2.0 * x).powi(2)).sqrt()) / 2.0
} else {
((1.0 - (-2.0 * x + 2.0).powi(2)).sqrt() + 1.0) / 2.0
}
}
Easing::InBack => {
let c1 = 1.70158;
let c3 = c1 + 1.0;
c3 * x.powi(3) - c1 * x.powi(2)
}
Easing::OutBack => {
let c1 = 1.70158;
let c3 = c1 + 1.0;
1.0 + c3 * (x - 1.0).powi(3) + c1 * (x - 1.0).powi(2)
}
Easing::InOutBack => {
let c1 = 1.70158;
let c2 = c1 * 1.525;
if x < 0.5 {
((2.0 * x).powi(2) * ((c2 + 1.0) * 2.0 * x - c2)) / 2.0
} else {
((2.0 * x - 2.0).powi(2) * ((c2 + 1.0) * (x * 2.0 - 2.0) + c2) + 2.0) / 2.0
}
}
Easing::InElastic => {
if x == 0.0 {
0.0
} else if x == 1.0 {
1.0
} else {
let c4 = (2.0 * PI) / 3.0;
-2.0_f32.powf(10.0 * x - 10.0) * ((x * 10.0 - 10.75) * c4).sin()
}
}
Easing::OutElastic => {
if x == 0.0 {
0.0
} else if x == 1.0 {
1.0
} else {
let c4 = (2.0 * PI) / 3.0;
2.0_f32.powf(-10.0 * x) * ((x * 10.0 - 0.75) * c4).sin() + 1.0
}
}
Easing::InOutElastic => {
if x == 0.0 {
0.0
} else if x == 1.0 {
1.0
} else {
let c5 = (2.0 * PI) / 4.5;
if x < 0.5 {
-(2.0_f32.powf(20.0 * x - 10.0) * ((20.0 * x - 11.125) * c5).sin()) / 2.0
} else {
(2.0_f32.powf(-20.0 * x + 10.0) * ((20.0 * x - 11.125) * c5).sin()) / 2.0
+ 1.0
}
}
}
Easing::InBounce => 1.0 - Easing::OutBounce.apply(1.0 - x),
Easing::OutBounce => out_bounce(x),
Easing::InOutBounce => {
if x < 0.5 {
(1.0 - out_bounce(1.0 - 2.0 * x)) / 2.0
} else {
(1.0 + out_bounce(2.0 * x - 1.0)) / 2.0
}
}
Easing::CubicBezier(x1, y1, x2, y2) => cubic_bezier(x1, y1, x2, y2, x),
}
.clamp(0.0, 1.0)
}
}
fn out_bounce(x: f32) -> f32 {
let n1 = 7.5625;
let d1 = 2.75;
if x < 1.0 / d1 {
n1 * x * x
} else if x < 2.0 / d1 {
let x = x - 1.5 / d1;
n1 * x * x + 0.75
} else if x < 2.5 / d1 {
let x = x - 2.25 / d1;
n1 * x * x + 0.9375
} else {
let x = x - 2.625 / d1;
n1 * x * x + 0.984375
}
}
fn cubic_bezier(_x1: f32, y1: f32, _x2: f32, y2: f32, t: f32) -> f32 {
let u = 1.0 - t;
3.0 * u * u * t * y1 + 3.0 * u * t * t * y2 + t * t * t
}