///|
/// Numerical summary of a normalized motion curve.
pub struct CurveReport {
min_value : Double
max_value : Double
max_speed : Double
time_of_max_speed : Double
monotonic : Bool
endpoint_error : Double
}
///|
/// Acceptance limits for a motion curve.
///
/// A negative `max_speed` disables the speed limit. `max_overshoot` is the
/// largest permitted distance outside the normalized `[0, 1]` range.
pub struct CurvePolicy {
endpoint_tolerance : Double
require_monotonic : Bool
max_overshoot : Double
max_speed : Double
}
///|
/// A practical default for curves that must stay in range and finish exactly.
pub fn CurvePolicy::strict() -> CurvePolicy {
{
endpoint_tolerance: 0.000001,
require_monotonic: true,
max_overshoot: 0.0,
max_speed: -1.0,
}
}
///|
/// Deterministic result of checking a curve against a `CurvePolicy`.
pub struct CurveCheck {
report : CurveReport
overshoot : Double
endpoints_ok : Bool
monotonic_ok : Bool
overshoot_ok : Bool
speed_ok : Bool
passed : Bool
}
///|
/// Profile a curve using a deterministic number of samples.
pub fn profile(motion : MotionFn, samples : Int) -> CurveReport {
let count = if samples < 2 { 2 } else { samples }
let mut min_value = motion(0.0)
let mut max_value = min_value
let mut max_speed = 0.0
let mut time_of_max_speed = 0.0
let mut monotonic = true
let mut previous = min_value
for i in 1.. max_value {
max_value = value
}
if value < previous {
monotonic = false
}
let speed = ((value - previous) * (count - 1).to_double()).abs()
if speed > max_speed {
max_speed = speed
time_of_max_speed = t
}
previous = value
}
{
min_value,
max_value,
max_speed,
time_of_max_speed,
monotonic,
endpoint_error: motion(0.0).abs() + (motion(1.0) - 1.0).abs(),
}
}
///|
/// Check a curve against explicit product requirements.
pub fn check(
motion : MotionFn,
samples : Int,
policy : CurvePolicy,
) -> CurveCheck {
let report = profile(motion, samples)
let overshoot = max_overshoot(motion, samples)
let tolerance = if policy.endpoint_tolerance < 0.0 {
0.0
} else {
policy.endpoint_tolerance
}
let endpoints_ok = report.endpoint_error <= tolerance
let monotonic_ok = !policy.require_monotonic || report.monotonic
let overshoot_ok = overshoot <= policy.max_overshoot
let speed_ok = policy.max_speed < 0.0 || report.max_speed <= policy.max_speed
{
report,
overshoot,
endpoints_ok,
monotonic_ok,
overshoot_ok,
speed_ok,
passed: endpoints_ok && monotonic_ok && overshoot_ok && speed_ok,
}
}