1use crate::{MotionValue, Transition};
3use std::time::Duration;
4#[cfg(feature = "gpui")]
5use std::time::Instant;
6
7#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct SvgTarget {
12 values: [f64; 5],
13}
14
15impl Default for SvgTarget {
16 fn default() -> Self {
17 Self {
18 values: [1.0, 1.0, 0.0, 0.0, 0.0],
19 }
20 }
21}
22
23impl SvgTarget {
24 pub fn scale(self, factor: f32) -> Self {
26 self.scale_xy(factor, factor)
27 }
28
29 pub fn scale_xy(mut self, x: f32, y: f32) -> Self {
31 assert!(x.is_finite() && y.is_finite(), "SVG scale must be finite");
32 self.values[0] = f64::from(x);
33 self.values[1] = f64::from(y);
34
35 self
36 }
37
38 pub fn rotate_degrees(mut self, degrees: f64) -> Self {
40 assert!(degrees.is_finite(), "SVG rotation must be finite");
41 self.values[2] = degrees;
42
43 self
44 }
45
46 pub fn translate(mut self, x: f32, y: f32) -> Self {
48 assert!(
49 x.is_finite() && y.is_finite(),
50 "SVG translation must be finite"
51 );
52 self.values[3] = x.into();
53 self.values[4] = y.into();
54
55 self
56 }
57
58 pub fn translation(self) -> [f32; 2] {
60 [self.values[3], self.values[4]]
61 .map(|v| v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32)
62 }
63
64 pub fn scale_factors(self) -> [f32; 2] {
66 [self.values[0], self.values[1]]
67 .map(|v| v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32)
68 }
69
70 pub fn rotation_degrees(self) -> f64 {
72 self.values[2]
73 }
74}
75
76#[derive(Clone, Copy, Debug, PartialEq)]
79pub struct SvgMotion {
80 target: SvgTarget,
81 initial: Option<SvgTarget>,
82 transition: Transition,
83 delay: Duration,
84}
85
86impl SvgMotion {
87 pub fn new(target: SvgTarget) -> Self {
89 Self {
90 target,
91 initial: None,
92 transition: Transition::default(),
93 delay: Duration::ZERO,
94 }
95 }
96
97 pub fn initial(mut self, initial: SvgTarget) -> Self {
99 self.initial = Some(initial);
100
101 self
102 }
103
104 pub fn transition(mut self, transition: Transition) -> Self {
106 self.transition = transition;
107
108 self
109 }
110
111 pub fn delay(mut self, delay: Duration) -> Self {
113 self.delay = delay;
114
115 self
116 }
117}
118
119#[derive(Clone, Copy, Debug)]
121pub struct SvgFrame {
122 target: SvgTarget,
123 velocity: [f64; 3],
124 translation_velocity: [f64; 2],
125 active: bool,
126 completed: bool,
127}
128
129impl SvgFrame {
130 pub fn target(self) -> SvgTarget {
132 self.target
133 }
134
135 pub fn velocity(self) -> [f64; 3] {
137 self.velocity
138 }
139
140 pub fn translation_velocity(self) -> [f64; 2] {
142 self.translation_velocity
143 }
144
145 pub fn is_animating(self) -> bool {
147 self.active
148 }
149
150 pub fn just_completed(self) -> bool {
152 self.completed
153 }
154}
155
156#[derive(Debug)]
159pub struct SvgMotionState {
160 channels: [MotionValue; 5],
161 initialized: bool,
162 #[cfg(feature = "gpui")]
163 pub(crate) epoch: Instant,
164}
165
166impl Default for SvgMotionState {
167 fn default() -> Self {
168 Self {
169 channels: SvgTarget::default().values.map(MotionValue::new),
170 initialized: false,
171 #[cfg(feature = "gpui")]
172 epoch: Instant::now(),
173 }
174 }
175}
176
177impl SvgMotionState {
178 pub fn sample(&mut self, motion: SvgMotion, now: Duration, reduced_motion: bool) -> SvgFrame {
195 if !self.initialized {
196 let initial = motion.initial.unwrap_or(motion.target);
197
198 for (channel, value) in self.channels.iter_mut().zip(initial.values) {
199 channel.jump(value, now);
200 }
201
202 self.initialized = true;
203 }
204
205 let mut active = false;
206 let mut completed = false;
207 let mut velocity = [0.0; 5];
208
209 let values = std::array::from_fn(|ix| {
210 self.channels[ix].animate_delayed(
211 motion.target.values[ix],
212 motion.transition,
213 motion.delay,
214 now,
215 );
216 let frame = self.channels[ix].sample(now, reduced_motion);
217
218 active |= frame.is_animating();
219 completed |= frame.just_completed();
220 velocity[ix] = frame.velocity();
221
222 frame.value()
223 });
224
225 SvgFrame {
226 target: SvgTarget { values },
227 velocity: [velocity[0], velocity[1], velocity[2]],
228 translation_velocity: [velocity[3], velocity[4]],
229 active,
230 completed: completed && !active,
231 }
232 }
233}
234
235impl crate::MotionData<5> for SvgTarget {
236 fn channels(self) -> [f64; 5] {
237 self.values
238 }
239
240 fn with_channels(self, values: [f64; 5]) -> Self {
241 Self { values }
242 }
243
244 fn compatible(self, _: Self) -> bool {
245 true
246 }
247}