///|
/// A normalized motion curve maps progress in `[0, 1]` to a value.
pub type MotionFn = (Double) -> Double

///|
/// The identity curve, useful as a baseline in diagnostics and timelines.
pub fn linear(t : Double) -> Double {
  t
}

///|
/// A tunable anticipation curve. `overshoot` controls how far it pulls back.
pub fn back_in(t : Double, overshoot : Double) -> Double {
  if t <= 0.0 {
    0.0
  } else if t >= 1.0 {
    1.0
  } else {
    let c3 = overshoot + 1.0
    c3 * t * t * t - overshoot * t * t
  }
}

///|
/// A tunable settling curve. `overshoot` controls how far it exceeds the end.
pub fn back_out(t : Double, overshoot : Double) -> Double {
  if t <= 0.0 {
    0.0
  } else if t >= 1.0 {
    1.0
  } else {
    let t1 = t - 1.0
    1.0 + (overshoot + 1.0) * t1 * t1 * t1 + overshoot * t1 * t1
  }
}

///|
/// A damped oscillation. `frequency` and `damping` are explicit tuning inputs.
pub fn spring_out(t : Double, frequency : Double, damping : Double) -> Double {
  if t <= 0.0 {
    return 0.0
  }
  if t >= 1.0 {
    return 1.0
  }
  let safe_frequency = if frequency <= 0.0 { 1.0 } else { frequency }
  let safe_damping = if damping < 0.0 { 0.0 } else { damping }
  let decay = @math.pow(2.718281828, -safe_damping * t)
  1.0 - decay * @math.cos(2.0 * @math.PI * safe_frequency * t)
}