1use core::fmt;
4
5use crate::impl_option;
6
7#[derive(Debug, Clone, PartialEq)]
8#[repr(C)]
9pub struct InterpolateResolver {
10 pub interpolate_func: AnimationInterpolationFunction,
11 pub parent_rect_width: f32,
12 pub parent_rect_height: f32,
13 pub current_rect_width: f32,
14 pub current_rect_height: f32,
15}
16
17#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
18#[repr(C)]
19pub struct SvgPoint {
20 pub x: f32,
21 pub y: f32,
22}
23
24#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
25#[repr(C)]
26pub struct SvgCubicCurve {
27 pub start: SvgPoint,
28 pub ctrl_1: SvgPoint,
29 pub ctrl_2: SvgPoint,
30 pub end: SvgPoint,
31}
32
33#[derive(Debug, Copy, Clone, PartialEq)]
35#[repr(C, u8)]
36pub enum AnimationInterpolationFunction {
37 Ease,
38 Linear,
39 EaseIn,
40 EaseOut,
41 EaseInOut,
42 CubicBezier(SvgCubicCurve),
43}
44
45#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
46#[repr(C)]
47pub struct SvgRect {
48 pub width: f32,
49 pub height: f32,
50 pub x: f32,
51 pub y: f32,
52 pub radius_top_left: f32,
53 pub radius_top_right: f32,
54 pub radius_bottom_left: f32,
55 pub radius_bottom_right: f32,
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
59#[repr(C)]
60pub struct SvgVector {
61 pub x: f64,
62 pub y: f64,
63}
64
65#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
66#[repr(C)]
67pub struct SvgQuadraticCurve {
68 pub start: SvgPoint,
69 pub ctrl: SvgPoint,
70 pub end: SvgPoint,
71}
72
73impl_option!(
76 SvgPoint,
77 OptionSvgPoint,
78 [Debug, Clone, PartialEq, PartialOrd]
79);
80
81impl SvgPoint {
82 #[inline]
84 pub const fn new(x: f32, y: f32) -> Self {
85 Self { x, y }
86 }
87
88 #[inline]
89 pub fn distance(&self, other: Self) -> f64 {
90 let dx = other.x - self.x;
91 let dy = other.y - self.y;
92 libm::hypotf(dx, dy) as f64
93 }
94}
95
96impl SvgRect {
97 pub fn union_with(&mut self, other: &Self) {
98 let self_max_x = self.x + self.width;
99 let self_max_y = self.y + self.height;
100 let self_min_x = self.x;
101 let self_min_y = self.y;
102
103 let other_max_x = other.x + other.width;
104 let other_max_y = other.y + other.height;
105 let other_min_x = other.x;
106 let other_min_y = other.y;
107
108 let max_x = self_max_x.max(other_max_x);
109 let max_y = self_max_y.max(other_max_y);
110 let min_x = self_min_x.min(other_min_x);
111 let min_y = self_min_y.min(other_min_y);
112
113 self.x = min_x;
114 self.y = min_y;
115 self.width = max_x - min_x;
116 self.height = max_y - min_y;
117 }
118
119 pub fn contains_point(&self, point: SvgPoint) -> bool {
122 point.x > self.x
123 && point.x < self.x + self.width
124 && point.y > self.y
125 && point.y < self.y + self.height
126 }
127
128 pub fn expand(
130 &self,
131 padding_top: f32,
132 padding_bottom: f32,
133 padding_left: f32,
134 padding_right: f32,
135 ) -> SvgRect {
136 SvgRect {
137 width: self.width + padding_left + padding_right,
138 height: self.height + padding_top + padding_bottom,
139 x: self.x - padding_left,
140 y: self.y - padding_top,
141 ..*self
142 }
143 }
144
145 pub fn get_center(&self) -> SvgPoint {
146 SvgPoint {
147 x: self.x + (self.width / 2.0),
148 y: self.y + (self.height / 2.0),
149 }
150 }
151}
152
153const STEP_SIZE: usize = 20;
154const STEP_SIZE_F64: f64 = 0.05;
155
156impl SvgCubicCurve {
157 #[inline]
159 pub const fn new(start: SvgPoint, ctrl_1: SvgPoint, ctrl_2: SvgPoint, end: SvgPoint) -> Self {
160 Self { start, ctrl_1, ctrl_2, end }
161 }
162
163 pub fn reverse(&mut self) {
164 let temp = self.start;
165 self.start = self.end;
166 self.end = temp;
167 let temp = self.ctrl_1;
168 self.ctrl_1 = self.ctrl_2;
169 self.ctrl_2 = temp;
170 }
171
172 pub fn get_start(&self) -> SvgPoint {
173 self.start
174 }
175 pub fn get_end(&self) -> SvgPoint {
176 self.end
177 }
178
179 pub fn get_x_at_t(&self, t: f64) -> f64 {
181 let c_x = 3.0 * (self.ctrl_1.x as f64 - self.start.x as f64);
182 let b_x = 3.0 * (self.ctrl_2.x as f64 - self.ctrl_1.x as f64) - c_x;
183 let a_x = self.end.x as f64 - self.start.x as f64 - c_x - b_x;
184
185 (a_x * t * t * t) + (b_x * t * t) + (c_x * t) + self.start.x as f64
186 }
187
188 pub fn get_y_at_t(&self, t: f64) -> f64 {
189 let c_y = 3.0 * (self.ctrl_1.y as f64 - self.start.y as f64);
190 let b_y = 3.0 * (self.ctrl_2.y as f64 - self.ctrl_1.y as f64) - c_y;
191 let a_y = self.end.y as f64 - self.start.y as f64 - c_y - b_y;
192
193 (a_y * t * t * t) + (b_y * t * t) + (c_y * t) + self.start.y as f64
194 }
195
196 pub fn get_length(&self) -> f64 {
197 let mut arc_length = 0.0;
199 let mut prev_point = self.get_start();
200
201 for i in 0..STEP_SIZE {
202 let t_next = (i + 1) as f64 * STEP_SIZE_F64;
203 let next_point = SvgPoint {
204 x: self.get_x_at_t(t_next) as f32,
205 y: self.get_y_at_t(t_next) as f32,
206 };
207 arc_length += prev_point.distance(next_point);
208 prev_point = next_point;
209 }
210
211 arc_length
212 }
213
214 pub fn get_t_at_offset(&self, offset: f64) -> f64 {
215 let mut arc_length = 0.0;
219 let mut t_current = 0.0;
220 let mut prev_point = self.get_start();
221
222 for i in 0..STEP_SIZE {
223 let t_next = (i + 1) as f64 * STEP_SIZE_F64;
224 let next_point = SvgPoint {
225 x: self.get_x_at_t(t_next) as f32,
226 y: self.get_y_at_t(t_next) as f32,
227 };
228
229 let distance = prev_point.distance(next_point);
230
231 arc_length += distance;
232
233 if arc_length > offset {
235 let remaining = arc_length - offset;
236 return t_current + (remaining / distance) * STEP_SIZE_F64;
237 }
238
239 prev_point = next_point;
240 t_current = t_next;
241 }
242
243 t_current
244 }
245
246 pub fn get_tangent_vector_at_t(&self, t: f64) -> SvgVector {
247 let w0 = SvgPoint {
257 x: self.ctrl_1.x - self.start.x,
258 y: self.ctrl_1.y - self.start.y,
259 };
260
261 let w1 = SvgPoint {
262 x: self.ctrl_2.x - self.ctrl_1.x,
263 y: self.ctrl_2.y - self.ctrl_1.y,
264 };
265
266 let w2 = SvgPoint {
267 x: self.end.x - self.ctrl_2.x,
268 y: self.end.y - self.ctrl_2.y,
269 };
270
271 let quadratic_curve = SvgQuadraticCurve {
272 start: w0,
273 ctrl: w1,
274 end: w2,
275 };
276
277 let tangent_vector = SvgVector {
282 x: quadratic_curve.get_x_at_t(t),
283 y: quadratic_curve.get_y_at_t(t),
284 };
285
286 tangent_vector.normalize()
287 }
288
289 pub fn get_bounds(&self) -> SvgRect {
290 let min_x = self
291 .start
292 .x
293 .min(self.end.x)
294 .min(self.ctrl_1.x)
295 .min(self.ctrl_2.x);
296 let max_x = self
297 .start
298 .x
299 .max(self.end.x)
300 .max(self.ctrl_1.x)
301 .max(self.ctrl_2.x);
302
303 let min_y = self
304 .start
305 .y
306 .min(self.end.y)
307 .min(self.ctrl_1.y)
308 .min(self.ctrl_2.y);
309 let max_y = self
310 .start
311 .y
312 .max(self.end.y)
313 .max(self.ctrl_1.y)
314 .max(self.ctrl_2.y);
315
316 let width = (max_x - min_x).abs();
317 let height = (max_y - min_y).abs();
318
319 SvgRect {
320 width,
321 height,
322 x: min_x,
323 y: min_y,
324 ..SvgRect::default()
325 }
326 }
327}
328
329impl SvgVector {
330 #[inline]
332 pub fn angle_degrees(&self) -> f64 {
333 (-self.y).atan2(self.x).to_degrees()
334 }
335
336 #[inline]
337 #[must_use = "returns a new vector"]
338 pub fn normalize(&self) -> Self {
339 let tangent_length = libm::hypotf(self.x as f32, self.y as f32) as f64;
340 if tangent_length == 0.0 {
341 return Self { x: 0.0, y: 0.0 };
342 }
343 Self {
344 x: self.x / tangent_length,
345 y: self.y / tangent_length,
346 }
347 }
348
349 #[must_use = "returns a new vector"]
351 #[inline]
352 pub fn rotate_90deg_ccw(&self) -> Self {
353 Self {
354 x: -self.y,
355 y: self.x,
356 }
357 }
358}
359
360impl SvgQuadraticCurve {
361 #[inline]
363 pub const fn new(start: SvgPoint, ctrl: SvgPoint, end: SvgPoint) -> Self {
364 Self { start, ctrl, end }
365 }
366
367 pub fn reverse(&mut self) {
368 let temp = self.start;
369 self.start = self.end;
370 self.end = temp;
371 }
372 pub fn get_start(&self) -> SvgPoint {
373 self.start
374 }
375 pub fn get_end(&self) -> SvgPoint {
376 self.end
377 }
378 pub fn get_bounds(&self) -> SvgRect {
379 let min_x = self.start.x.min(self.end.x).min(self.ctrl.x);
380 let max_x = self.start.x.max(self.end.x).max(self.ctrl.x);
381
382 let min_y = self.start.y.min(self.end.y).min(self.ctrl.y);
383 let max_y = self.start.y.max(self.end.y).max(self.ctrl.y);
384
385 let width = (max_x - min_x).abs();
386 let height = (max_y - min_y).abs();
387
388 SvgRect {
389 width,
390 height,
391 x: min_x,
392 y: min_y,
393 ..SvgRect::default()
394 }
395 }
396
397 pub fn get_x_at_t(&self, t: f64) -> f64 {
398 let one_minus = 1.0 - t;
399 one_minus * one_minus * self.start.x as f64
400 + 2.0 * one_minus * t * self.ctrl.x as f64
401 + t * t * self.end.x as f64
402 }
403
404 pub fn get_y_at_t(&self, t: f64) -> f64 {
405 let one_minus = 1.0 - t;
406 one_minus * one_minus * self.start.y as f64
407 + 2.0 * one_minus * t * self.ctrl.y as f64
408 + t * t * self.end.y as f64
409 }
410
411 pub fn get_length(&self) -> f64 {
412 self.to_cubic().get_length()
413 }
414
415 pub fn get_t_at_offset(&self, offset: f64) -> f64 {
416 self.to_cubic().get_t_at_offset(offset)
417 }
418
419 pub fn get_tangent_vector_at_t(&self, t: f64) -> SvgVector {
420 self.to_cubic().get_tangent_vector_at_t(t)
421 }
422
423 fn to_cubic(&self) -> SvgCubicCurve {
424 SvgCubicCurve {
425 start: self.start,
426 ctrl_1: SvgPoint {
427 x: self.start.x + (2.0 / 3.0) * (self.ctrl.x - self.start.x),
428 y: self.start.y + (2.0 / 3.0) * (self.ctrl.y - self.start.y),
429 },
430 ctrl_2: SvgPoint {
431 x: self.end.x + (2.0 / 3.0) * (self.ctrl.x - self.end.x),
432 y: self.end.y + (2.0 / 3.0) * (self.ctrl.y - self.end.y),
433 },
434 end: self.end,
435 }
436 }
437}
438
439impl AnimationInterpolationFunction {
440 pub const fn get_curve(self) -> SvgCubicCurve {
441 match self {
442 AnimationInterpolationFunction::Ease => SvgCubicCurve {
443 start: SvgPoint { x: 0.0, y: 0.0 },
444 ctrl_1: SvgPoint { x: 0.25, y: 0.1 },
445 ctrl_2: SvgPoint { x: 0.25, y: 1.0 },
446 end: SvgPoint { x: 1.0, y: 1.0 },
447 },
448 AnimationInterpolationFunction::Linear => SvgCubicCurve {
449 start: SvgPoint { x: 0.0, y: 0.0 },
450 ctrl_1: SvgPoint { x: 0.0, y: 0.0 },
451 ctrl_2: SvgPoint { x: 1.0, y: 1.0 },
452 end: SvgPoint { x: 1.0, y: 1.0 },
453 },
454 AnimationInterpolationFunction::EaseIn => SvgCubicCurve {
455 start: SvgPoint { x: 0.0, y: 0.0 },
456 ctrl_1: SvgPoint { x: 0.42, y: 0.0 },
457 ctrl_2: SvgPoint { x: 1.0, y: 1.0 },
458 end: SvgPoint { x: 1.0, y: 1.0 },
459 },
460 AnimationInterpolationFunction::EaseOut => SvgCubicCurve {
461 start: SvgPoint { x: 0.0, y: 0.0 },
462 ctrl_1: SvgPoint { x: 0.0, y: 0.0 },
463 ctrl_2: SvgPoint { x: 0.58, y: 1.0 },
464 end: SvgPoint { x: 1.0, y: 1.0 },
465 },
466 AnimationInterpolationFunction::EaseInOut => SvgCubicCurve {
467 start: SvgPoint { x: 0.0, y: 0.0 },
468 ctrl_1: SvgPoint { x: 0.42, y: 0.0 },
469 ctrl_2: SvgPoint { x: 0.58, y: 1.0 },
470 end: SvgPoint { x: 1.0, y: 1.0 },
471 },
472 AnimationInterpolationFunction::CubicBezier(c) => c,
473 }
474 }
475
476 pub fn evaluate(self, t: f64) -> f32 {
477 self.get_curve().get_y_at_t(t) as f32
478 }
479}