1use crate::core::SvgMotion;
3use gpui_kit::{App, Window, px};
4use std::{ops::Deref, time::Duration};
5
6#[derive(Debug, Default)]
8pub struct SvgMotionState {
9 inner: crate::core::SvgMotionState,
10 clock: crate::Clock,
11}
12
13impl SvgMotionState {
14 pub fn frame(&mut self, motion: SvgMotion, window: &mut Window, cx: &App) -> SvgFrame {
16 let now = self.clock.tick(cx);
17
18 let frame = self.sample(motion, now, cx.reduce_motion());
19
20 if frame.is_animating() {
21 window.request_animation_frame();
22 }
23
24 frame
25 }
26
27 pub fn sample(&mut self, motion: SvgMotion, now: Duration, reduced_motion: bool) -> SvgFrame {
29 SvgFrame(self.inner.sample(motion, now, reduced_motion))
30 }
31}
32
33#[derive(Clone, Copy, Debug)]
35pub struct SvgFrame(crate::core::SvgFrame);
36
37impl From<crate::core::SvgFrame> for SvgFrame {
38 fn from(frame: crate::core::SvgFrame) -> Self {
39 Self(frame)
40 }
41}
42
43impl Deref for SvgFrame {
44 type Target = crate::core::SvgFrame;
45
46 fn deref(&self) -> &Self::Target {
47 &self.0
48 }
49}
50
51impl SvgFrame {
52 pub fn transformation(self) -> gpui_kit::Transformation {
56 let [x, y] = self.target().scale_factors();
57
58 let angle = self
59 .target()
60 .rotation_degrees()
61 .rem_euclid(360.0)
62 .to_radians() as f32;
63
64 gpui_kit::Transformation::scale(gpui_kit::size(x, y))
65 .with_rotation(gpui_kit::radians(angle))
66 .with_translation(gpui_kit::point(
67 px(self.target().translation()[0]),
68 px(self.target().translation()[1]),
69 ))
70 }
71
72 pub fn transformation_about(
76 self,
77 bounds: gpui_kit::Size<gpui_kit::Pixels>,
78 pivot: gpui_kit::Point<gpui_kit::Pixels>,
79 ) -> gpui_kit::Transformation {
80 assert!(
81 [
82 f32::from(bounds.width),
83 f32::from(bounds.height),
84 f32::from(pivot.x),
85 f32::from(pivot.y)
86 ]
87 .iter()
88 .all(|v| v.is_finite())
89 && bounds.width >= px(0.)
90 && bounds.height >= px(0.),
91 "SVG bounds and pivot must be finite with non-negative size"
92 );
93 let [sx, sy] = self.target().scale_factors().map(f64::from);
94
95 let angle = self
96 .target()
97 .rotation_degrees()
98 .rem_euclid(360.)
99 .to_radians();
100
101 let (sin, cos) = angle.sin_cos();
102 let dx = f64::from(pivot.x) - f64::from(bounds.width) * 0.5;
103 let dy = f64::from(pivot.y) - f64::from(bounds.height) * 0.5;
104 let [tx, ty] = self.target().translation().map(f64::from);
105 let finite = |v: f64| px(v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32);
106
107 self.transformation().with_translation(gpui_kit::point(
108 finite(tx + dx - (cos * sx * dx - sin * sy * dy)),
109 finite(ty + dy - (sin * sx * dx + cos * sy * dy)),
110 ))
111 }
112
113 pub fn apply(self, element: gpui_kit::Svg) -> gpui_kit::Svg {
127 element.with_transformation(self.transformation())
128 }
129}