///|
/// Clamp a value to the normalized animation interval.
pub fn clamp01(value : Double) -> Double {
if value < 0.0 {
0.0
} else if value > 1.0 {
1.0
} else {
value
}
}
///|
/// Map normalized motion progress onto an application value range.
pub fn interpolate(
start : Double,
end : Double,
t : Double,
motion : MotionFn,
) -> Double {
start + (end - start) * motion(t)
}
///|
/// Sample a motion curve at evenly spaced points, including both endpoints.
pub fn sample(motion : MotionFn, count : Int) -> Array[Double] {
let result : Array[Double] = Array::new()
if count <= 0 {
return result
}
if count == 1 {
result.push(motion(0.0))
return result
}
for i in 0.. Array[Double] {
let result : Array[Double] = Array::new()
if count <= 0 {
return result
}
if count == 1 {
result.push(start)
return result
}
for i in 0.. MotionFn {
fn(t : Double) -> Double {
if split <= 0.0 {
second(t)
} else if split >= 1.0 {
first(t)
} else if t < split {
first(t / split) * split
} else {
split + second((t - split) / (1.0 - split)) * (1.0 - split)
}
}
}
///|
/// Estimate local velocity with a centered finite difference.
pub fn velocity(motion : MotionFn, t : Double, epsilon : Double) -> Double {
let step = if epsilon <= 0.0 { 0.000001 } else { epsilon }
(motion(t + step) - motion(t - step)) / (2.0 * step)
}
///|
/// Check whether sampled values never decrease.
pub fn is_monotonic(motion : MotionFn, samples : Int) -> Bool {
let values = sample(motion, samples)
for i in 1.. Double {
let mut largest = 0.0
for value in sample(motion, samples) {
let excess = if value < 0.0 {
-value
} else if value > 1.0 {
value - 1.0
} else {
0.0
}
if excess > largest {
largest = excess
}
}
largest
}