///|
/// A normalized time remapping used by editors and media exporters.
pub struct TimeWarp {
curve : Curve
start : Double
end : Double
speed : Double
} derive(Debug)
///|
pub fn TimeWarp::new(
curve : Curve,
start : Double,
end : Double,
speed? : Double = 1.0,
) -> TimeWarp raise MotionError {
if end < start {
raise MotionError::InvalidTime(end)
}
if speed <= 0.0 {
raise MotionError::InvalidDuration(speed)
}
{ curve, start, end, speed }
}
///|
pub fn TimeWarp::duration(self : TimeWarp) -> Double {
(self.end - self.start) / self.speed
}
///|
pub fn TimeWarp::map(self : TimeWarp, time : Double) -> Double {
if self.end == self.start {
self.start
} else {
let ratio = clamp01((time - self.start) / self.duration())
self.start + self.curve.apply(ratio) * (self.end - self.start)
}
}
///|
/// Invert a timing curve with a fixed bisection budget.
pub fn TimeWarp::unmap(self : TimeWarp, mapped_time : Double) -> Double {
if self.end == self.start {
self.start
} else {
let target = clamp01((mapped_time - self.start) / (self.end - self.start))
let mut low = 0.0
let mut high = 1.0
let mut middle = 0.5
for _ in 0..<32 {
middle = (low + high) / 2.0
if self.curve.apply(middle) < target {
low = middle
} else {
high = middle
}
}
self.start + middle * self.duration()
}
}
///|
pub fn TimeWarp::curve(self : TimeWarp) -> Curve {
self.curve
}
///|
pub fn stagger(count : Int, delay : Double, interval : Double) -> Array[Double] {
let result : Array[Double] = []
if count <= 0 {
return result
}
for index in 0.. Array[Double] {
times.map(fn(time) { warp.map(time) })
}