Skip to content

Commit

Permalink
Add most common interpolations (#15675)
Browse files Browse the repository at this point in the history
# Objective

- Followup for #14788 
- Support most usual ease function

## Solution

- Use the crate
[`interpolation`](https://docs.rs/interpolation/0.3.0/interpolation/trait.Ease.html)
which has them all
- it's already used by bevy_easings, bevy_tweening, be_tween,
bevy_tweening_captured, bevy_enoki, kayak_ui in the Bevy ecosystem for
various easing/tweening/interpolation
  • Loading branch information
mockersf authored Oct 7, 2024
1 parent 0a150b0 commit 4357539
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 6 deletions.
1 change: 1 addition & 0 deletions crates/bevy_math/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rand = { version = "0.8", features = [
], default-features = false, optional = true }
rand_distr = { version = "0.4.3", optional = true }
smallvec = { version = "1.11" }
interpolation = "0.3"

bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
"glam",
Expand Down
136 changes: 130 additions & 6 deletions crates/bevy_math/src/curve/easing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
ops::{self, FloatPow},
VectorSpace,
};
use interpolation::Ease;

use super::{Curve, FunctionCurve, Interval};

Expand Down Expand Up @@ -84,6 +85,51 @@ where
}

impl EasingCurve<f32, FunctionCurve<f32, fn(f32) -> f32>> {
/// A [`Curve`] mapping the [unit interval] to itself.
///
/// [unit interval]: `Interval::UNIT`
pub fn ease(function: EaseFunction) -> Self {
Self {
start: 0.0,
end: 1.0,
easing: FunctionCurve::new(
Interval::UNIT,
match function {
EaseFunction::QuadraticIn => Ease::quadratic_in,
EaseFunction::QuadraticOut => Ease::quadratic_out,
EaseFunction::QuadraticInOut => Ease::quadratic_in_out,
EaseFunction::CubicIn => Ease::cubic_in,
EaseFunction::CubicOut => Ease::cubic_out,
EaseFunction::CubicInOut => Ease::cubic_in_out,
EaseFunction::QuarticIn => Ease::quartic_in,
EaseFunction::QuarticOut => Ease::quartic_out,
EaseFunction::QuarticInOut => Ease::quartic_in_out,
EaseFunction::QuinticIn => Ease::quintic_in,
EaseFunction::QuinticOut => Ease::quintic_out,
EaseFunction::QuinticInOut => Ease::quintic_in_out,
EaseFunction::SineIn => Ease::sine_in,
EaseFunction::SineOut => Ease::sine_out,
EaseFunction::SineInOut => Ease::sine_in_out,
EaseFunction::CircularIn => Ease::circular_in,
EaseFunction::CircularOut => Ease::circular_out,
EaseFunction::CircularInOut => Ease::circular_in_out,
EaseFunction::ExponentialIn => Ease::exponential_in,
EaseFunction::ExponentialOut => Ease::exponential_out,
EaseFunction::ExponentialInOut => Ease::exponential_in_out,
EaseFunction::ElasticIn => Ease::elastic_in,
EaseFunction::ElasticOut => Ease::elastic_out,
EaseFunction::ElasticInOut => Ease::elastic_in_out,
EaseFunction::BackIn => Ease::back_in,
EaseFunction::BackOut => Ease::back_out,
EaseFunction::BackInOut => Ease::back_in_out,
EaseFunction::BounceIn => Ease::bounce_in,
EaseFunction::BounceOut => Ease::bounce_out,
EaseFunction::BounceInOut => Ease::bounce_in_out,
},
),
}
}

/// A [`Curve`] mapping the [unit interval] to itself.
///
/// Quadratic easing functions can have exactly one critical point. This is a point on the function
Expand All @@ -92,7 +138,7 @@ impl EasingCurve<f32, FunctionCurve<f32, fn(f32) -> f32>> {
///
/// It uses the function `f(t) = t²`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
/// [`t = 1`]: `Self::quadratic_ease_out`
pub fn quadratic_ease_in() -> Self {
Self {
Expand All @@ -110,7 +156,7 @@ impl EasingCurve<f32, FunctionCurve<f32, fn(f32) -> f32>> {
///
/// It uses the function `f(t) = 1 - (1 - t)²`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
/// [`t = 0`]: `Self::quadratic_ease_in`
pub fn quadratic_ease_out() -> Self {
fn f(t: f32) -> f32 {
Expand All @@ -132,7 +178,7 @@ impl EasingCurve<f32, FunctionCurve<f32, fn(f32) -> f32>> {
///
/// It uses the function `f(t) = t² * (3 - 2t)`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
/// [sigmoid function]: https://en.wikipedia.org/wiki/Sigmoid_function
/// [smoothstep function]: https://en.wikipedia.org/wiki/Smoothstep
pub fn smoothstep() -> Self {
Expand All @@ -150,7 +196,7 @@ impl EasingCurve<f32, FunctionCurve<f32, fn(f32) -> f32>> {
///
/// It uses the function `f(t) = t`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
pub fn identity() -> Self {
Self {
start: 0.0,
Expand Down Expand Up @@ -219,7 +265,7 @@ where
/// - for `n >= 2` the curve has a start segment and an end segment of length `1 / (2 * n)` and in
/// between there are `n - 1` segments of length `1 / n`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
/// [`constant_curve(Interval::UNIT, 0.0)`]: `crate::curve::constant_curve`
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -265,7 +311,7 @@ impl StepCurve {
///
/// parametrized by `omega`
///
/// [unit domain]: `Interval::UNIT`
/// [unit interval]: `Interval::UNIT`
/// [smoothstep function]: https://en.wikipedia.org/wiki/Smoothstep
/// [spring-mass-system]: https://notes.yvt.jp/Graphics/Easing-Functions/#elastic-easing
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -296,3 +342,81 @@ impl ElasticCurve {
Self { omega }
}
}

/// Curve functions over the [unit interval], commonly used for easing transitions.
///
/// [unit interval]: `Interval::UNIT`
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub enum EaseFunction {
/// `f(t) = t²`
QuadraticIn,
/// `f(t) = -(t * (t - 2.0))`
QuadraticOut,
/// Behaves as `EaseFunction::QuadraticIn` for t < 0.5 and as `EaseFunction::QuadraticOut` for t >= 0.5
QuadraticInOut,

/// `f(t) = t³`
CubicIn,
/// `f(t) = (t - 1.0)³ + 1.0`
CubicOut,
/// Behaves as `EaseFunction::CubicIn` for t < 0.5 and as `EaseFunction::CubicOut` for t >= 0.5
CubicInOut,

/// `f(t) = t⁴`
QuarticIn,
/// `f(t) = (t - 1.0)³ * (1.0 - t) + 1.0`
QuarticOut,
/// Behaves as `EaseFunction::QuarticIn` for t < 0.5 and as `EaseFunction::QuarticOut` for t >= 0.5
QuarticInOut,

/// `f(t) = t⁵`
QuinticIn,
/// `f(t) = (t - 1.0)⁵ + 1.0`
QuinticOut,
/// Behaves as `EaseFunction::QuinticIn` for t < 0.5 and as `EaseFunction::QuinticOut` for t >= 0.5
QuinticInOut,

/// `f(t) = sin((t - 1.0) * π / 2.0) + 1.0`
SineIn,
/// `f(t) = sin(t * π / 2.0)`
SineOut,
/// Behaves as `EaseFunction::SineIn` for t < 0.5 and as `EaseFunction::SineOut` for t >= 0.5
SineInOut,

/// `f(t) = 1.0 - sqrt(1.0 - t²)`
CircularIn,
/// `f(t) = sqrt((2.0 - t) * t)`
CircularOut,
/// Behaves as `EaseFunction::CircularIn` for t < 0.5 and as `EaseFunction::CircularOut` for t >= 0.5
CircularInOut,

/// `f(t) = 2.0.powf(10.0 * (t - 1.0))`
ExponentialIn,
/// `f(t) = 1.0 - 2.0.powf(-10.0 * t)`
ExponentialOut,
/// Behaves as `EaseFunction::ExponentialIn` for t < 0.5 and as `EaseFunction::ExponentialOut` for t >= 0.5
ExponentialInOut,

/// `f(t) = sin(13.0 * π / 2.0 * t) * 2.0.powf(10.0 * (t - 1.0))`
ElasticIn,
/// `f(t) = sin(-13.0 * π / 2.0 * (t + 1.0)) * 2.0.powf(-10.0 * t) + 1.0`
ElasticOut,
/// Behaves as `EaseFunction::ElasticIn` for t < 0.5 and as `EaseFunction::ElasticOut` for t >= 0.5
ElasticInOut,

/// `f(t) = t³ - t * sin(t * π)`
BackIn,
/// `f(t) = 1.0 - (1.0 - t)³ - t * sin((1.0 - t) * π))`
BackOut,
/// Behaves as `EaseFunction::BackIn` for t < 0.5 and as `EaseFunction::BackOut` for t >= 0.5
BackInOut,

/// bouncy at the start!
BounceIn,
/// bouncy at the end!
BounceOut,
/// Behaves as `EaseFunction::BounceIn` for t < 0.5 and as `EaseFunction::BounceOut` for t >= 0.5
BounceInOut,
}

0 comments on commit 4357539

Please sign in to comment.