eazy_data/interpolation/polynomial/
smootherstep.rs

1//! The Smootherstep Interpolating Polynomial Curve.
2//!
3//! Smootherstep is Ken Perlin's improved S-curve: `6t⁵ - 15t⁴ + 10t³`.
4//! It has zero first AND second derivatives at t=0 and t=1.
5//! This module provides In, Out, and InOut variants derived mathematically:
6//!
7//! - **InOut** (standard): `6t⁵ - 15t⁴ + 10t³` - symmetric S-curve
8//! - **In**: First half of InOut stretched: `t³ * (3t² - 15t + 20) / 8`
9//! - **Out**: Mirror of In: `t * (3t⁴ - 10t² + 15) / 8`
10
11use crate::easing::Curve;
12
13/// The [`InSmoother`] Curve (ease-in).
14///
15/// Derived from smootherstep InOut by taking the first half and stretching:
16/// `In(t) = 2 * InOut(t/2) = t³ * (3t² - 15t + 20) / 8`
17///
18/// Starts slow, accelerates toward the end.
19///
20/// #### examples.
21///
22/// ```
23/// use eazy::Curve;
24/// use eazy::interpolation::polynomial::smootherstep::InSmoother;
25///
26/// let p = InSmoother.y(0.5);
27/// assert!((p - 0.20703125).abs() < 0.0001);
28/// ```
29#[derive(Debug)]
30pub struct InSmoother;
31
32impl Curve for InSmoother {
33  #[inline(always)]
34  fn y(&self, p: f32) -> f32 {
35    let p = p.clamp(0.0, 1.0);
36    // t³ * (3t² - 15t + 20) / 8
37    let p2 = p * p;
38    let p3 = p2 * p;
39    p3 * (3.0 * p2 - 15.0 * p + 20.0) * 0.125
40  }
41}
42
43#[test]
44fn test_in_smoother() {
45  // In(0) = 0
46  assert_eq!(InSmoother.y(0.0), 0.0);
47  // In(1) = 1
48  assert_eq!(InSmoother.y(1.0), 1.0);
49  // In(0.5) ≈ 0.207 (ease-in: slower at start)
50  assert!((InSmoother.y(0.5) - 0.20703125).abs() < 0.0001);
51}
52
53/// The [`OutSmoother`] Curve (ease-out).
54///
55/// Mirror of InSmoother: `Out(t) = 1 - In(1-t) = t * (3t⁴ - 10t² + 15) / 8`
56///
57/// Starts fast, decelerates toward the end.
58///
59/// #### examples.
60///
61/// ```
62/// use eazy::Curve;
63/// use eazy::interpolation::polynomial::smootherstep::OutSmoother;
64///
65/// let p = OutSmoother.y(0.5);
66/// assert!((p - 0.79296875).abs() < 0.0001);
67/// ```
68#[derive(Debug)]
69pub struct OutSmoother;
70
71impl Curve for OutSmoother {
72  #[inline(always)]
73  fn y(&self, p: f32) -> f32 {
74    let p = p.clamp(0.0, 1.0);
75    // t * (3t⁴ - 10t² + 15) / 8
76    let p2 = p * p;
77    let p4 = p2 * p2;
78    p * (3.0 * p4 - 10.0 * p2 + 15.0) * 0.125
79  }
80}
81
82#[test]
83fn test_out_smoother() {
84  // Out(0) = 0
85  assert_eq!(OutSmoother.y(0.0), 0.0);
86  // Out(1) = 1
87  assert_eq!(OutSmoother.y(1.0), 1.0);
88  // Out(0.5) ≈ 0.793 (ease-out: faster at start)
89  assert!((OutSmoother.y(0.5) - 0.79296875).abs() < 0.0001);
90}
91
92/// The [`InOutSmoother`] Curve (standard smootherstep).
93///
94/// Ken Perlin's smootherstep: `6t⁵ - 15t⁴ + 10t³`
95///
96/// Has zero first AND second derivatives at endpoints for extra smoothness.
97/// Starts slow, speeds up in the middle, slows down at the end.
98///
99/// #### examples.
100///
101/// ```
102/// use eazy::Curve;
103/// use eazy::interpolation::polynomial::smootherstep::InOutSmoother;
104///
105/// let p = InOutSmoother.y(0.5);
106/// assert_eq!(p, 0.5);
107/// ```
108#[derive(Debug)]
109pub struct InOutSmoother;
110
111impl Curve for InOutSmoother {
112  #[inline(always)]
113  fn y(&self, p: f32) -> f32 {
114    let p = p.clamp(0.0, 1.0);
115    // 6t⁵ - 15t⁴ + 10t³ = t³ * (6t² - 15t + 10)
116    let p2 = p * p;
117    let p3 = p2 * p;
118    p3 * (p * (6.0 * p - 15.0) + 10.0)
119  }
120}
121
122#[test]
123fn test_in_out_smoother() {
124  // InOut(0) = 0
125  assert_eq!(InOutSmoother.y(0.0), 0.0);
126  // InOut(0.5) = 0.5 (symmetric)
127  assert_eq!(InOutSmoother.y(0.5), 0.5);
128  // InOut(1) = 1
129  assert_eq!(InOutSmoother.y(1.0), 1.0);
130}
131
132/// The Non-linear Interpolation.
133///
134/// Interpolates smoothly between `min` and `max`. It will accelerated from the
135/// start and deccelerated toward the end with a cubic easing.
136///
137/// #### params.
138///
139/// |      |                            |
140/// |:-----|:---------------------------|
141/// | `p`  | The progress.              |
142/// | `x0` | The `min` start value.     |
143/// | `x1` | The `max` end value.       |
144///
145/// #### returns.
146///
147/// `f32` — The interpolated result between the two float values.
148///
149/// #### examples.
150///
151/// ```
152/// use eazy::interpolation::polynomial::smootherstep::smootherstep;
153///
154/// let p = smootherstep(0.25, 0.0, 1.0);
155///
156/// assert_eq!(p, 0.103515625);
157/// ```
158///
159/// #### notes.
160///
161/// The formula was suggested by Ken Perlin. For more information about the
162/// formula go to the [wiki](<https://en.wikipedia.org/wiki/Smoothstep>)
163#[inline(always)]
164pub fn smootherstep(p: f32, x0: f32, x1: f32) -> f32 {
165  let mut p = (p - x0) / (x1 - x0);
166
167  p = p.clamp(0.0, 1.0);
168  p * p * p * (p * (6.0 * p - 15.0) + 10.0)
169}