use dyn_clone::{clone_trait_object, DynClone};
use lazy_static::lazy_static;
use lyon_geom::{CubicBezierSegment, Point};
use std::fmt::Debug;
pub trait EasingFunction: Debug + DynClone + Send + Sync {
fn calc(&self, x: f32) -> f32;
}
clone_trait_object!(EasingFunction);
#[derive(Clone, Debug, Default)]
pub enum Easing {
#[default]
Linear,
Ease,
In,
Out,
InOut,
InSine,
OutSine,
InOutSine,
InQuad,
OutQuad,
InOutQuad,
InCubic,
OutCubic,
InOutCubic,
InQuart,
OutQuart,
InOutQuart,
InQuint,
OutQuint,
InOutQuint,
InExpo,
OutExpo,
InOutExpo,
InCirc,
OutCirc,
InOutCirc,
InBack,
OutBack,
InOutBack,
Custom(Box<dyn EasingFunction>),
}
impl EasingFunction for Easing {
fn calc(&self, x: f32) -> f32 {
match self {
Self::Linear => EASE_LINEAR.calc(x),
Self::Ease => EASE_WEB.calc(x),
Self::In => EASE_IN.calc(x),
Self::Out => EASE_OUT.calc(x),
Self::InOut => EASE_IN_OUT.calc(x),
Self::InSine => EASE_IN_SINE.calc(x),
Self::OutSine => EASE_OUT_SINE.calc(x),
Self::InOutSine => EASE_IN_OUT_SINE.calc(x),
Self::InQuad => EASE_IN_QUAD.calc(x),
Self::OutQuad => EASE_OUT_QUAD.calc(x),
Self::InOutQuad => EASE_IN_OUT_QUAD.calc(x),
Self::InCubic => EASE_IN_CUBIC.calc(x),
Self::OutCubic => EASE_OUT_CUBIC.calc(x),
Self::InOutCubic => EASE_IN_OUT_CUBIC.calc(x),
Self::InQuart => EASE_IN_QUART.calc(x),
Self::OutQuart => EASE_OUT_QUART.calc(x),
Self::InOutQuart => EASE_IN_OUT_QUART.calc(x),
Self::InQuint => EASE_IN_QUINT.calc(x),
Self::OutQuint => EASE_OUT_QUINT.calc(x),
Self::InOutQuint => EASE_IN_OUT_QUINT.calc(x),
Self::InExpo => EASE_IN_EXPO.calc(x),
Self::OutExpo => EASE_OUT_EXPO.calc(x),
Self::InOutExpo => EASE_IN_OUT_EXPO.calc(x),
Self::InCirc => EASE_IN_CIRC.calc(x),
Self::OutCirc => EASE_OUT_CIRC.calc(x),
Self::InOutCirc => EASE_IN_OUT_CIRC.calc(x),
Self::InBack => EASE_IN_BACK.calc(x),
Self::OutBack => EASE_OUT_BACK.calc(x),
Self::InOutBack => EASE_IN_OUT_BACK.calc(x),
Self::Custom(custom) => custom.calc(x),
}
}
}
lazy_static! {
static ref EASE_LINEAR: LinearEasing = LinearEasing;
static ref EASE_WEB: CubicBezierEasing = cubic_bezier(0.25, 0.1, 0.25, 1.0);
static ref EASE_IN: CubicBezierEasing = cubic_bezier(0.42, 0.0, 1.0, 1.0);
static ref EASE_OUT: CubicBezierEasing = cubic_bezier(0.0, 0.0, 0.58, 1.0);
static ref EASE_IN_OUT: CubicBezierEasing = cubic_bezier(0.42, 0.0, 0.58, 1.0);
static ref EASE_IN_SINE: CubicBezierEasing = cubic_bezier(0.12, 0.0, 0.39, 0.0);
static ref EASE_OUT_SINE: CubicBezierEasing = cubic_bezier(0.61, 1.0, 0.88, 1.0);
static ref EASE_IN_OUT_SINE: CubicBezierEasing = cubic_bezier(0.37, 0.0, 0.63, 1.0);
static ref EASE_IN_QUAD: CubicBezierEasing = cubic_bezier(0.11, 0.0, 0.5, 0.0);
static ref EASE_OUT_QUAD: CubicBezierEasing = cubic_bezier(0.5, 1.0, 0.89, 1.0);
static ref EASE_IN_OUT_QUAD: CubicBezierEasing = cubic_bezier(0.45, 0.0, 0.55, 1.0);
static ref EASE_IN_CUBIC: CubicBezierEasing = cubic_bezier(0.32, 0.0, 0.67, 0.0);
static ref EASE_OUT_CUBIC: CubicBezierEasing = cubic_bezier(0.33, 1.0, 0.68, 1.0);
static ref EASE_IN_OUT_CUBIC: CubicBezierEasing = cubic_bezier(0.65, 0.0, 0.35, 1.0);
static ref EASE_IN_QUART: CubicBezierEasing = cubic_bezier(0.5, 0.0, 0.75, 0.0);
static ref EASE_OUT_QUART: CubicBezierEasing = cubic_bezier(0.25, 1.0, 0.5, 1.0);
static ref EASE_IN_OUT_QUART: CubicBezierEasing = cubic_bezier(0.76, 0.0, 0.24, 1.0);
static ref EASE_IN_QUINT: CubicBezierEasing = cubic_bezier(0.64, 0.0, 0.78, 0.0);
static ref EASE_OUT_QUINT: CubicBezierEasing = cubic_bezier(0.22, 1.0, 0.36, 1.0);
static ref EASE_IN_OUT_QUINT: CubicBezierEasing = cubic_bezier(0.83, 0.0, 0.17, 1.0);
static ref EASE_IN_EXPO: CubicBezierEasing = cubic_bezier(0.7, 0.0, 0.84, 0.0);
static ref EASE_OUT_EXPO: CubicBezierEasing = cubic_bezier(0.16, 1.0, 0.3, 1.0);
static ref EASE_IN_OUT_EXPO: CubicBezierEasing = cubic_bezier(0.87, 0.0, 0.13, 1.0);
static ref EASE_IN_CIRC: CubicBezierEasing = cubic_bezier(0.55, 0.0, 1.0, 0.45);
static ref EASE_OUT_CIRC: CubicBezierEasing = cubic_bezier(0.0, 0.55, 0.45, 1.0);
static ref EASE_IN_OUT_CIRC: CubicBezierEasing = cubic_bezier(0.85, 0.0, 0.15, 1.0);
static ref EASE_IN_BACK: CubicBezierEasing = cubic_bezier(0.36, 0.0, 0.66, -0.56);
static ref EASE_OUT_BACK: CubicBezierEasing = cubic_bezier(0.34, 1.56, 0.64, 1.0);
static ref EASE_IN_OUT_BACK: CubicBezierEasing = cubic_bezier(0.68, -0.6, 0.32, 1.6);
}
#[derive(Clone, Debug)]
pub struct LinearEasing;
impl EasingFunction for LinearEasing {
fn calc(&self, x: f32) -> f32 {
x
}
}
#[derive(Clone, Debug)]
pub struct CubicBezierEasing {
segment: CubicBezierSegment<f32>,
}
impl CubicBezierEasing {
pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
Self {
segment: CubicBezierSegment {
from: Point::new(0.0, 0.0),
to: Point::new(1.0, 1.0),
ctrl1: Point::new(x1, y1),
ctrl2: Point::new(x2, y2),
},
}
}
}
impl EasingFunction for CubicBezierEasing {
fn calc(&self, x: f32) -> f32 {
self.segment.y(x)
}
}
fn cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> CubicBezierEasing {
CubicBezierEasing::new(x1, y1, x2, y2)
}