animato_devtools/
easing_editor.rs1use animato_core::Easing;
4
5#[derive(Clone, Debug, PartialEq)]
7pub struct EasingCurveEditor {
8 pub current: Easing,
10 pub compare: Option<Easing>,
12 pub sample_count: usize,
14}
15
16impl EasingCurveEditor {
17 pub fn new(easing: Easing) -> Self {
19 Self {
20 current: easing,
21 compare: None,
22 sample_count: 100,
23 }
24 }
25
26 pub fn set_easing(&mut self, easing: Easing) {
28 self.current = easing;
29 }
30
31 pub fn set_compare(&mut self, easing: Option<Easing>) {
33 self.compare = easing;
34 }
35
36 pub fn set_sample_count(&mut self, sample_count: usize) {
38 self.sample_count = sample_count.max(2);
39 }
40
41 pub fn samples(&self) -> Vec<[f32; 2]> {
43 let mut out = Vec::with_capacity(self.sample_count.max(2));
44 self.samples_into(&mut out);
45 out
46 }
47
48 pub fn samples_into(&self, out: &mut Vec<[f32; 2]>) {
50 sample_easing(&self.current, self.sample_count, out);
51 }
52
53 pub fn compare_samples(&self) -> Option<Vec<[f32; 2]>> {
55 self.compare.as_ref().map(|easing| {
56 let mut out = Vec::with_capacity(self.sample_count.max(2));
57 sample_easing(easing, self.sample_count, &mut out);
58 out
59 })
60 }
61
62 pub fn set_control_points(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
64 self.current = Easing::CubicBezier(x1, y1, x2, y2);
65 }
66
67 pub fn copy_code(&self) -> String {
69 match self.current {
70 Easing::CubicBezier(x1, y1, x2, y2) => {
71 format!("Easing::CubicBezier({x1:.3}, {y1:.3}, {x2:.3}, {y2:.3})")
72 }
73 Easing::Steps(steps) => format!("Easing::Steps({steps})"),
74 _ => format!("Easing::{:?}", self.current),
75 }
76 }
77}
78
79impl Default for EasingCurveEditor {
80 fn default() -> Self {
81 Self::new(Easing::Linear)
82 }
83}
84
85fn sample_easing(easing: &Easing, sample_count: usize, out: &mut Vec<[f32; 2]>) {
86 out.clear();
87 let count = sample_count.max(2);
88 for index in 0..count {
89 let t = index as f32 / (count - 1) as f32;
90 out.push([t, easing.apply(t)]);
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn samples_include_endpoints() {
100 let mut editor = EasingCurveEditor::new(Easing::EaseOutCubic);
101 editor.set_sample_count(8);
102 let samples = editor.samples();
103 assert_eq!(samples.len(), 8);
104 assert_eq!(samples[0], [0.0, 0.0]);
105 assert_eq!(samples[7], [1.0, 1.0]);
106 }
107
108 #[test]
109 fn compare_and_cubic_updates_work() {
110 let mut editor = EasingCurveEditor::default();
111 editor.set_compare(Some(Easing::EaseInSine));
112 assert!(editor.compare_samples().is_some());
113 editor.set_control_points(0.1, 0.2, 0.3, 0.4);
114 assert_eq!(editor.current, Easing::CubicBezier(0.1, 0.2, 0.3, 0.4));
115 assert!(editor.copy_code().contains("CubicBezier"));
116 }
117}