jugar_probar/animation/
easing.rs1use super::types::EasingFunction;
7
8#[derive(Clone, Debug)]
10pub struct Keyframe {
11 pub t: f64,
13 pub value: f64,
15}
16
17#[derive(Clone, Debug)]
19pub struct EasingVerification {
20 pub expected: EasingFunction,
22 pub max_deviation: f64,
24 pub mean_deviation: f64,
26 pub passed: bool,
28 pub deviations: Vec<f64>,
30}
31
32#[must_use]
37pub fn verify_easing(
38 keyframes: &[Keyframe],
39 expected: &EasingFunction,
40 tolerance: f64,
41) -> EasingVerification {
42 if keyframes.is_empty() {
43 return EasingVerification {
44 expected: expected.clone(),
45 max_deviation: 0.0,
46 mean_deviation: 0.0,
47 passed: true,
48 deviations: Vec::new(),
49 };
50 }
51
52 let mut max_dev: f64 = 0.0;
53 let mut sum_dev: f64 = 0.0;
54 let mut deviations = Vec::with_capacity(keyframes.len());
55
56 for kf in keyframes {
57 let expected_value = expected.evaluate(kf.t);
58 let deviation = (kf.value - expected_value).abs();
59 deviations.push(deviation);
60 sum_dev += deviation;
61 if deviation > max_dev {
62 max_dev = deviation;
63 }
64 }
65
66 let mean_dev = sum_dev / keyframes.len() as f64;
67 let passed = max_dev <= tolerance;
68
69 EasingVerification {
70 expected: expected.clone(),
71 max_deviation: max_dev,
72 mean_deviation: mean_dev,
73 passed,
74 deviations,
75 }
76}
77
78#[must_use]
82pub fn sample_easing(easing: &EasingFunction, num_samples: usize) -> Vec<Keyframe> {
83 if num_samples == 0 {
84 return Vec::new();
85 }
86 if num_samples == 1 {
87 return vec![Keyframe {
88 t: 0.0,
89 value: easing.evaluate(0.0),
90 }];
91 }
92
93 (0..num_samples)
94 .map(|i| {
95 let t = i as f64 / (num_samples - 1) as f64;
96 Keyframe {
97 t,
98 value: easing.evaluate(t),
99 }
100 })
101 .collect()
102}
103
104#[cfg(test)]
105#[allow(clippy::unwrap_used)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_verify_easing_perfect_match() {
111 let easing = EasingFunction::Linear;
112 let keyframes: Vec<Keyframe> = (0..=10)
113 .map(|i| {
114 let t = i as f64 / 10.0;
115 Keyframe { t, value: t }
116 })
117 .collect();
118 let result = verify_easing(&keyframes, &easing, 0.01);
119 assert!(result.passed);
120 assert!(result.max_deviation < 0.001);
121 }
122
123 #[test]
124 fn test_verify_easing_mismatch() {
125 let easing = EasingFunction::EaseIn; let keyframes: Vec<Keyframe> = (0..=10)
128 .map(|i| {
129 let t = i as f64 / 10.0;
130 Keyframe { t, value: t } })
132 .collect();
133 let result = verify_easing(&keyframes, &easing, 0.01);
134 assert!(!result.passed);
135 assert!(result.max_deviation > 0.01);
136 }
137
138 #[test]
139 fn test_verify_easing_empty() {
140 let easing = EasingFunction::Linear;
141 let result = verify_easing(&[], &easing, 0.01);
142 assert!(result.passed);
143 assert!(result.deviations.is_empty());
144 }
145
146 #[test]
147 fn test_verify_easing_within_tolerance() {
148 let easing = EasingFunction::Linear;
149 let keyframes = vec![
150 Keyframe {
151 t: 0.5,
152 value: 0.505,
153 }, ];
155 let result = verify_easing(&keyframes, &easing, 0.01);
156 assert!(result.passed);
157 }
158
159 #[test]
160 fn test_sample_easing_linear() {
161 let samples = sample_easing(&EasingFunction::Linear, 11);
162 assert_eq!(samples.len(), 11);
163 assert!((samples[0].t).abs() < f64::EPSILON);
164 assert!((samples[0].value).abs() < f64::EPSILON);
165 assert!((samples[10].t - 1.0).abs() < f64::EPSILON);
166 assert!((samples[10].value - 1.0).abs() < f64::EPSILON);
167 assert!((samples[5].t - 0.5).abs() < f64::EPSILON);
169 assert!((samples[5].value - 0.5).abs() < f64::EPSILON);
170 }
171
172 #[test]
173 fn test_sample_easing_ease_in() {
174 let samples = sample_easing(&EasingFunction::EaseIn, 11);
175 assert_eq!(samples.len(), 11);
176 assert!(samples[5].value < 0.5);
178 }
179
180 #[test]
181 fn test_sample_easing_empty() {
182 let samples = sample_easing(&EasingFunction::Linear, 0);
183 assert!(samples.is_empty());
184 }
185
186 #[test]
187 fn test_sample_easing_single() {
188 let samples = sample_easing(&EasingFunction::Linear, 1);
189 assert_eq!(samples.len(), 1);
190 assert!((samples[0].t).abs() < f64::EPSILON);
191 }
192
193 #[test]
194 fn test_deviations_count() {
195 let easing = EasingFunction::Linear;
196 let keyframes = vec![
197 Keyframe { t: 0.0, value: 0.0 },
198 Keyframe { t: 0.5, value: 0.5 },
199 Keyframe { t: 1.0, value: 1.0 },
200 ];
201 let result = verify_easing(&keyframes, &easing, 0.01);
202 assert_eq!(result.deviations.len(), 3);
203 }
204
205 #[test]
206 fn test_mean_deviation() {
207 let easing = EasingFunction::Linear;
208 let keyframes = vec![
209 Keyframe {
210 t: 0.5,
211 value: 0.52,
212 }, Keyframe {
214 t: 0.8,
215 value: 0.84,
216 }, ];
218 let result = verify_easing(&keyframes, &easing, 0.05);
219 assert!((result.mean_deviation - 0.03).abs() < 0.001);
220 }
221}