///|
/// Numerically estimate a first derivative without allocating a sample buffer.
pub fn derivative_of(
func : (Double) -> Double,
t : Double,
step? : Double = 0.000001,
) -> Double {
let width = if step <= 0.0 { 0.000001 } else { step }
let left = clamp(t - width, 0.0, 1.0)
let right = clamp(t + width, 0.0, 1.0)
if right == left {
0.0
} else {
(func(right) - func(left)) / (right - left)
}
}
///|
fn derivative_power(t : Double, exponent : Double) -> Double {
if t <= 0.0 && exponent < 1.0 {
0.0
} else {
exponent * power(t, exponent - 1.0)
}
}
///|
fn derivative_bounce_out(t : Double) -> Double {
let n1 = 7.5625
let d1 = 2.75
if t < 1.0 / d1 {
2.0 * n1 * t
} else if t < 2.0 / d1 {
2.0 * n1 * (t - 1.5 / d1)
} else if t < 2.5 / d1 {
2.0 * n1 * (t - 2.25 / d1)
} else {
2.0 * n1 * (t - 2.625 / d1)
}
}
///|
/// First derivative of a built-in easing curve on normalized time.
pub fn EasingId::derivative(self : EasingId, t : Double) -> Double {
let x = clamp01(t)
match self {
Linear => 1.0
QuadIn => 2.0 * x
QuadOut => 2.0 - 2.0 * x
QuadInOut => if x < 0.5 { 4.0 * x } else { 4.0 - 4.0 * x }
CubicIn => 3.0 * square(x)
CubicOut => 3.0 * square(1.0 - x)
CubicInOut =>
if x < 0.5 {
12.0 * square(x)
} else {
12.0 * square(1.0 - x)
}
QuartIn => derivative_power(x, 4.0)
QuartOut => derivative_power(1.0 - x, 4.0)
QuartInOut =>
if x < 0.5 {
4.0 * derivative_power(2.0 * x, 4.0)
} else {
4.0 * derivative_power(2.0 - 2.0 * x, 4.0)
}
QuintIn => derivative_power(x, 5.0)
QuintOut => derivative_power(1.0 - x, 5.0)
QuintInOut =>
if x < 0.5 {
5.0 * derivative_power(2.0 * x, 5.0)
} else {
5.0 * derivative_power(2.0 - 2.0 * x, 5.0)
}
SineIn => @math.sin(x * @math.PI / 2.0) * @math.PI / 2.0
SineOut => @math.cos(x * @math.PI / 2.0) * @math.PI / 2.0
SineInOut => @math.sin(x * @math.PI) * @math.PI / 2.0
ExpoIn => 10.0 * @math.ln(2.0) * power(2.0, 10.0 * (x - 1.0))
ExpoOut => 10.0 * @math.ln(2.0) * power(2.0, -10.0 * x)
ExpoInOut =>
if x < 0.5 {
10.0 * @math.ln(2.0) * power(2.0, 20.0 * x - 10.0)
} else {
10.0 * @math.ln(2.0) * power(2.0, -20.0 * x + 10.0)
}
CircIn => if x >= 1.0 { 0.0 } else { x / (1.0 - square(x)).sqrt() }
CircOut =>
if x <= 0.0 {
0.0
} else {
(1.0 - x) / (1.0 - square(x - 1.0)).sqrt()
}
CircInOut => derivative_of(fn(value) { self.apply(value) }, x)
BackIn => derivative_of(fn(value) { back_in(value) }, x)
BackOut => derivative_of(fn(value) { back_out(value) }, x)
BackInOut => derivative_of(fn(value) { back_in_out(value) }, x)
ElasticIn => derivative_of(fn(value) { elastic_in(value) }, x)
ElasticOut => derivative_of(fn(value) { elastic_out(value) }, x)
ElasticInOut => derivative_of(fn(value) { elastic_in_out(value) }, x)
BounceIn => -derivative_bounce_out(1.0 - x)
BounceOut => derivative_bounce_out(x)
BounceInOut =>
if x < 0.5 {
derivative_bounce_out(1.0 - 2.0 * x)
} else {
derivative_bounce_out(2.0 * x - 1.0)
}
}
}
///|
pub fn Curve::derivative(self : Curve, t : Double) -> Double {
match self {
Builtin(id) => id.derivative(t)
CubicBezier(curve) => curve.derivative(t)
}
}
///|
pub fn Tween::velocity_at(self : Tween, time : Double) -> Double {
self.sample_motion(time).velocity()
}