1#[cfg(not(feature = "std"))]
6use crate::float::Float;
7use shuyi_core::ease::Easing;
8
9const ONE: f32 = 65_536.0;
11
12pub fn ease_f32(ease: &impl Easing, p: f32) -> f32 {
24 let p = if p.is_nan() { 0.0 } else { p.clamp(0.0, 1.0) };
25 #[expect(clippy::cast_possible_truncation, reason = "0 to 65 536")]
27 let units = (p * ONE).round() as i32;
28 #[expect(
29 clippy::cast_precision_loss,
30 reason = "every curve stays between -65 536 and 131 072"
31 )]
32 let eased = ease.ease(units) as f32;
33 eased / ONE
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use crate::cinematic::ease_in_out;
40 use shuyi_core::{Ease, Family};
41
42 #[test]
43 fn smooth_is_the_cinematics_ease_in_out() {
44 for k in 0..=10_000_u16 {
45 let p = f32::from(k) / 10_000.0;
46 let off = (ease_f32(&Ease::Smooth, p) - ease_in_out(p)).abs();
47 assert!(off <= 2.0 / 65_536.0, "{p}: {off}");
48 }
49 }
50
51 #[test]
52 fn not_a_number_and_input_out_of_range_are_clamped() {
53 let curve = Ease::In(Family::Cubic);
54 assert_eq!(ease_f32(&curve, f32::NAN), 0.0);
55 assert_eq!(ease_f32(&curve, -3.0), 0.0);
56 assert_eq!(ease_f32(&curve, f32::NEG_INFINITY), 0.0);
57 assert_eq!(ease_f32(&curve, 7.0), 1.0);
58 assert_eq!(ease_f32(&curve, f32::INFINITY), 1.0);
59 assert_eq!(ease_f32(&curve, 0.5), 0.125);
60 }
61
62 #[test]
63 fn an_overshoot_goes_past_0_and_1() {
64 assert!(ease_f32(&Ease::In(Family::Back), 0.25) < 0.0);
65 assert!(ease_f32(&Ease::Out(Family::Elastic), 0.1) > 1.0);
66 }
67}