1use crate::animation::easings::{Easing, IEasing, SharedEasing};
20use crate::animation::KeySplineSolver;
21use crate::numerics::Vector2;
22use crate::rendering::composition::Compositor;
23use std::cell::Cell;
24use std::f64::consts::PI;
25use std::rc::Rc;
26use std::sync::{Arc, Mutex};
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31pub enum CompositionEasingFunctionMode {
32 #[default]
34 In,
35 Out,
37 InOut,
39}
40
41fn ease_with_mode(mode: CompositionEasingFunctionMode, t: f64, ease_in: impl Fn(f64) -> f64) -> f64 {
43 match mode {
44 CompositionEasingFunctionMode::In => ease_in(t),
45 CompositionEasingFunctionMode::Out => 1.0 - ease_in(1.0 - t),
46 CompositionEasingFunctionMode::InOut => {
47 if t < 0.5 {
48 ease_in(t * 2.0) * 0.5
49 } else {
50 (1.0 - ease_in((1.0 - t) * 2.0)) * 0.5 + 0.5
51 }
52 }
53 }
54}
55
56macro_rules! value_easing {
59 ($ty:ty) => {
60 impl IEasing for $ty {
61 fn to_shared(&self) -> Arc<SharedEasing> {
62 Arc::new(*self)
63 }
64
65 fn ease(&self, progress: f64) -> f64 {
66 self.ease_core(progress)
67 }
68 }
69
70 easing_conversions!($ty);
71 };
72}
73
74macro_rules! easing_conversions {
76 ($ty:ty) => {
77 impl From<$ty> for Easing {
78 fn from(value: $ty) -> Easing {
79 Easing::new(value)
80 }
81 }
82
83 impl From<Rc<$ty>> for Easing {
84 fn from(value: Rc<$ty>) -> Easing {
85 Easing::from_rc(value)
86 }
87 }
88 };
89}
90
91pub struct CubicBezierEasingFunction {
95 control_point1: Vector2,
96 control_point2: Vector2,
97 solver: Mutex<KeySplineSolver>,
98}
99
100impl CubicBezierEasingFunction {
101 pub fn new(control_point1: Vector2, control_point2: Vector2) -> CubicBezierEasingFunction {
104 for x in [control_point1.x, control_point2.x] {
105 if !(0.0..=1.0).contains(&x) {
106 panic!("The X coordinate of a control point must be between 0 and 1");
107 }
108 }
109 CubicBezierEasingFunction {
110 control_point1,
111 control_point2,
112 solver: Mutex::new(KeySplineSolver::new(
113 control_point1.x as f64,
114 control_point1.y as f64,
115 control_point2.x as f64,
116 control_point2.y as f64,
117 )),
118 }
119 }
120
121 pub fn control_point1(&self) -> Vector2 {
123 self.control_point1
124 }
125
126 pub fn control_point2(&self) -> Vector2 {
128 self.control_point2
129 }
130}
131
132impl IEasing for CubicBezierEasingFunction {
133 fn to_shared(&self) -> Arc<SharedEasing> {
134 Arc::new(CubicBezierEasingFunction::new(self.control_point1, self.control_point2))
135 }
136
137 fn ease(&self, progress: f64) -> f64 {
138 self.solver.lock().unwrap_or_else(|e| e.into_inner()).get_spline_progress(progress)
139 }
140}
141
142easing_conversions!(CubicBezierEasingFunction);
143
144#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
146pub struct LinearEasingFunction;
147
148impl LinearEasingFunction {
149 pub fn new() -> LinearEasingFunction {
150 LinearEasingFunction
151 }
152
153 fn ease_core(&self, progress: f64) -> f64 {
154 progress
155 }
156}
157
158value_easing!(LinearEasingFunction);
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162struct StepParameters {
163 step_count: i32,
164 initial_step: i32,
165 final_step: i32,
166 is_initial_step_single_frame: bool,
167 is_final_step_single_frame: bool,
168}
169
170impl StepParameters {
171 fn ease_core(&self, t: f64) -> f64 {
181 let count = self.step_count.max(1);
182 let initial = self.initial_step.clamp(0, count);
183 let final_step = self.final_step.clamp(initial, count);
184 let step = if t <= 0.0 {
185 initial
186 } else if t >= 1.0 {
187 final_step
188 } else {
189 let first = initial + i32::from(self.is_initial_step_single_frame);
190 let last = final_step - i32::from(self.is_final_step_single_frame);
191 if last < first {
192 first.min(final_step)
193 } else {
194 let steps = (last - first + 1) as f64;
195 (first + (t * steps).floor() as i32).min(last)
196 }
197 };
198 step as f64 / count as f64
199 }
200}
201
202impl IEasing for StepParameters {
203 fn to_shared(&self) -> Arc<SharedEasing> {
204 Arc::new(*self)
205 }
206
207 fn ease(&self, progress: f64) -> f64 {
208 self.ease_core(progress)
209 }
210}
211
212pub struct StepEasingFunction {
216 step_count: Cell<i32>,
217 initial_step: Cell<i32>,
218 final_step: Cell<Option<i32>>,
220 is_initial_step_single_frame: Cell<bool>,
221 is_final_step_single_frame: Cell<bool>,
222}
223
224impl StepEasingFunction {
225 pub fn new() -> StepEasingFunction {
227 StepEasingFunction::with_step_count(1)
228 }
229
230 pub fn with_step_count(step_count: i32) -> StepEasingFunction {
232 StepEasingFunction {
233 step_count: Cell::new(step_count),
234 initial_step: Cell::new(0),
235 final_step: Cell::new(None),
236 is_initial_step_single_frame: Cell::new(false),
237 is_final_step_single_frame: Cell::new(false),
238 }
239 }
240
241 pub fn step_count(&self) -> i32 {
243 self.step_count.get()
244 }
245
246 pub fn set_step_count(&self, value: i32) {
247 self.step_count.set(value)
248 }
249
250 pub fn initial_step(&self) -> i32 {
252 self.initial_step.get()
253 }
254
255 pub fn set_initial_step(&self, value: i32) {
256 self.initial_step.set(value)
257 }
258
259 pub fn final_step(&self) -> i32 {
261 self.final_step.get().unwrap_or(self.step_count.get())
262 }
263
264 pub fn set_final_step(&self, value: i32) {
265 self.final_step.set(Some(value))
266 }
267
268 pub fn is_initial_step_single_frame(&self) -> bool {
270 self.is_initial_step_single_frame.get()
271 }
272
273 pub fn set_is_initial_step_single_frame(&self, value: bool) {
274 self.is_initial_step_single_frame.set(value)
275 }
276
277 pub fn is_final_step_single_frame(&self) -> bool {
279 self.is_final_step_single_frame.get()
280 }
281
282 pub fn set_is_final_step_single_frame(&self, value: bool) {
283 self.is_final_step_single_frame.set(value)
284 }
285
286 fn parameters(&self) -> StepParameters {
287 StepParameters {
288 step_count: self.step_count(),
289 initial_step: self.initial_step(),
290 final_step: self.final_step(),
291 is_initial_step_single_frame: self.is_initial_step_single_frame(),
292 is_final_step_single_frame: self.is_final_step_single_frame(),
293 }
294 }
295}
296
297impl Default for StepEasingFunction {
298 fn default() -> Self {
299 StepEasingFunction::new()
300 }
301}
302
303impl IEasing for StepEasingFunction {
304 fn to_shared(&self) -> Arc<SharedEasing> {
306 Arc::new(self.parameters())
307 }
308
309 fn ease(&self, progress: f64) -> f64 {
310 self.parameters().ease_core(progress)
311 }
312}
313
314easing_conversions!(StepEasingFunction);
315
316#[derive(Clone, Copy, Debug, PartialEq)]
319pub struct BackEasingFunction {
320 mode: CompositionEasingFunctionMode,
321 amplitude: f32,
322}
323
324impl BackEasingFunction {
325 pub fn new(mode: CompositionEasingFunctionMode, amplitude: f32) -> BackEasingFunction {
326 BackEasingFunction { mode, amplitude }
327 }
328
329 pub fn mode(&self) -> CompositionEasingFunctionMode {
331 self.mode
332 }
333
334 pub fn amplitude(&self) -> f32 {
336 self.amplitude
337 }
338
339 fn ease_core(&self, t: f64) -> f64 {
340 let amplitude = (self.amplitude as f64).max(0.0);
341 ease_with_mode(self.mode, t, |t| t * t * t - t * amplitude * (PI * t).sin())
342 }
343}
344
345value_easing!(BackEasingFunction);
346
347#[derive(Clone, Copy, Debug, PartialEq)]
350pub struct BounceEasingFunction {
351 mode: CompositionEasingFunctionMode,
352 bounces: i32,
353 bounciness: f32,
354}
355
356impl BounceEasingFunction {
357 pub fn new(mode: CompositionEasingFunctionMode, bounces: i32, bounciness: f32) -> BounceEasingFunction {
358 BounceEasingFunction { mode, bounces, bounciness }
359 }
360
361 pub fn mode(&self) -> CompositionEasingFunctionMode {
363 self.mode
364 }
365
366 pub fn bounces(&self) -> i32 {
368 self.bounces
369 }
370
371 pub fn bounciness(&self) -> f32 {
374 self.bounciness
375 }
376
377 fn ease_in(t: f64, bounces: i32, bounciness: f32) -> f64 {
379 let bounces = (bounces as f64).max(0.0);
380 let mut bounciness = bounciness as f64;
381 if bounciness < 1.0 || (bounciness - 1.0).abs() < 1e-9 {
383 bounciness = 1.001;
384 }
385 let pow = bounciness.powf(bounces);
386 let one_minus_bounciness = 1.0 - bounciness;
387 let sum_of_units = (1.0 - pow) / one_minus_bounciness + pow * 0.5;
390 let unit_at_t = t * sum_of_units;
391 let bounce_at_t = (-unit_at_t * (1.0 - bounciness) + 1.0).log(bounciness);
393 let start = bounce_at_t.floor();
394 let end = start + 1.0;
395 let start_time = (1.0 - bounciness.powf(start)) / (one_minus_bounciness * sum_of_units);
396 let end_time = (1.0 - bounciness.powf(end)) / (one_minus_bounciness * sum_of_units);
397 let mid_time = (start_time + end_time) * 0.5;
400 let time_relative_to_peak = t - mid_time;
401 let radius = mid_time - start_time;
402 let amplitude = (1.0 / bounciness).powf(bounces - start);
403 (-amplitude / (radius * radius)) * (time_relative_to_peak - radius) * (time_relative_to_peak + radius)
404 }
405
406 fn ease_core(&self, t: f64) -> f64 {
407 ease_with_mode(self.mode, t, |t| Self::ease_in(t, self.bounces, self.bounciness))
408 }
409}
410
411value_easing!(BounceEasingFunction);
412
413#[derive(Clone, Copy, Debug, PartialEq, Eq)]
415pub struct CircleEasingFunction {
416 mode: CompositionEasingFunctionMode,
417}
418
419impl CircleEasingFunction {
420 pub fn new(mode: CompositionEasingFunctionMode) -> CircleEasingFunction {
421 CircleEasingFunction { mode }
422 }
423
424 pub fn mode(&self) -> CompositionEasingFunctionMode {
426 self.mode
427 }
428
429 fn ease_core(&self, t: f64) -> f64 {
430 ease_with_mode(self.mode, t, |t| {
431 let t = t.clamp(0.0, 1.0);
432 1.0 - (1.0 - t * t).sqrt()
433 })
434 }
435}
436
437value_easing!(CircleEasingFunction);
438
439#[derive(Clone, Copy, Debug, PartialEq)]
442pub struct ElasticEasingFunction {
443 mode: CompositionEasingFunctionMode,
444 oscillations: i32,
445 springiness: f32,
446}
447
448impl ElasticEasingFunction {
449 pub fn new(mode: CompositionEasingFunctionMode, oscillations: i32, springiness: f32) -> ElasticEasingFunction {
450 ElasticEasingFunction { mode, oscillations, springiness }
451 }
452
453 pub fn mode(&self) -> CompositionEasingFunctionMode {
455 self.mode
456 }
457
458 pub fn oscillations(&self) -> i32 {
460 self.oscillations
461 }
462
463 pub fn springiness(&self) -> f32 {
466 self.springiness
467 }
468
469 fn ease_core(&self, t: f64) -> f64 {
470 let oscillations = (self.oscillations as f64).max(0.0);
471 let springiness = (self.springiness as f64).max(0.0);
472 ease_with_mode(self.mode, t, |t| {
473 let expo = if springiness == 0.0 { t } else { ((springiness * t).exp() - 1.0) / (springiness.exp() - 1.0) };
474 expo * ((PI * 2.0 * oscillations + PI * 0.5) * t).sin()
475 })
476 }
477}
478
479value_easing!(ElasticEasingFunction);
480
481#[derive(Clone, Copy, Debug, PartialEq)]
484pub struct ExponentialEasingFunction {
485 mode: CompositionEasingFunctionMode,
486 exponent: f32,
487}
488
489impl ExponentialEasingFunction {
490 pub fn new(mode: CompositionEasingFunctionMode, exponent: f32) -> ExponentialEasingFunction {
491 ExponentialEasingFunction { mode, exponent }
492 }
493
494 pub fn mode(&self) -> CompositionEasingFunctionMode {
496 self.mode
497 }
498
499 pub fn exponent(&self) -> f32 {
501 self.exponent
502 }
503
504 fn ease_core(&self, t: f64) -> f64 {
505 let factor = self.exponent as f64;
506 ease_with_mode(self.mode, t, |t| if factor == 0.0 { t } else { ((factor * t).exp() - 1.0) / (factor.exp() - 1.0) })
507 }
508}
509
510value_easing!(ExponentialEasingFunction);
511
512#[derive(Clone, Copy, Debug, PartialEq)]
515pub struct PowerEasingFunction {
516 mode: CompositionEasingFunctionMode,
517 power: f32,
518}
519
520impl PowerEasingFunction {
521 pub fn new(mode: CompositionEasingFunctionMode, power: f32) -> PowerEasingFunction {
522 PowerEasingFunction { mode, power }
523 }
524
525 pub fn mode(&self) -> CompositionEasingFunctionMode {
527 self.mode
528 }
529
530 pub fn power(&self) -> f32 {
532 self.power
533 }
534
535 fn ease_core(&self, t: f64) -> f64 {
536 let power = (self.power as f64).max(0.0);
537 ease_with_mode(self.mode, t, |t| t.powf(power))
538 }
539}
540
541value_easing!(PowerEasingFunction);
542
543#[derive(Clone, Copy, Debug, PartialEq, Eq)]
545pub struct SineEasingFunction {
546 mode: CompositionEasingFunctionMode,
547}
548
549impl SineEasingFunction {
550 pub fn new(mode: CompositionEasingFunctionMode) -> SineEasingFunction {
551 SineEasingFunction { mode }
552 }
553
554 pub fn mode(&self) -> CompositionEasingFunctionMode {
556 self.mode
557 }
558
559 fn ease_core(&self, t: f64) -> f64 {
560 ease_with_mode(self.mode, t, |t| 1.0 - (PI * 0.5 * (1.0 - t)).sin())
561 }
562}
563
564value_easing!(SineEasingFunction);
565
566pub struct CompositionEasingFunction;
573
574impl CompositionEasingFunction {
575 pub fn create_cubic_bezier_easing_function(
577 _owner: &Compositor,
578 control_point1: Vector2,
579 control_point2: Vector2,
580 ) -> Easing {
581 CubicBezierEasingFunction::new(control_point1, control_point2).into()
582 }
583
584 pub fn create_linear_easing_function(_owner: &Compositor) -> Easing {
586 LinearEasingFunction::new().into()
587 }
588
589 pub fn create_step_easing_function(_owner: &Compositor) -> Easing {
591 StepEasingFunction::new().into()
592 }
593
594 pub fn create_step_easing_function_with_step_count(_owner: &Compositor, step_count: i32) -> Easing {
596 StepEasingFunction::with_step_count(step_count).into()
597 }
598
599 pub fn create_back_easing_function(
601 _owner: &Compositor,
602 mode: CompositionEasingFunctionMode,
603 amplitude: f32,
604 ) -> Easing {
605 BackEasingFunction::new(mode, amplitude).into()
606 }
607
608 pub fn create_bounce_easing_function(
610 _owner: &Compositor,
611 mode: CompositionEasingFunctionMode,
612 bounces: i32,
613 bounciness: f32,
614 ) -> Easing {
615 BounceEasingFunction::new(mode, bounces, bounciness).into()
616 }
617
618 pub fn create_circle_easing_function(_owner: &Compositor, mode: CompositionEasingFunctionMode) -> Easing {
620 CircleEasingFunction::new(mode).into()
621 }
622
623 pub fn create_elastic_easing_function(
625 _owner: &Compositor,
626 mode: CompositionEasingFunctionMode,
627 oscillations: i32,
628 springiness: f32,
629 ) -> Easing {
630 ElasticEasingFunction::new(mode, oscillations, springiness).into()
631 }
632
633 pub fn create_exponential_easing_function(
635 _owner: &Compositor,
636 mode: CompositionEasingFunctionMode,
637 exponent: f32,
638 ) -> Easing {
639 ExponentialEasingFunction::new(mode, exponent).into()
640 }
641
642 pub fn create_power_easing_function(_owner: &Compositor, mode: CompositionEasingFunctionMode, power: f32) -> Easing {
644 PowerEasingFunction::new(mode, power).into()
645 }
646
647 pub fn create_sine_easing_function(_owner: &Compositor, mode: CompositionEasingFunctionMode) -> Easing {
649 SineEasingFunction::new(mode).into()
650 }
651}
652
653impl Compositor {
657 pub fn create_cubic_bezier_easing_function(
660 &self,
661 control_point1: Vector2,
662 control_point2: Vector2,
663 ) -> Rc<CubicBezierEasingFunction> {
664 Rc::new(CubicBezierEasingFunction::new(control_point1, control_point2))
665 }
666
667 pub fn create_linear_easing_function(&self) -> Rc<LinearEasingFunction> {
669 Rc::new(LinearEasingFunction::new())
670 }
671
672 pub fn create_step_easing_function(&self) -> Rc<StepEasingFunction> {
674 Rc::new(StepEasingFunction::new())
675 }
676
677 pub fn create_step_easing_function_with_step_count(&self, step_count: i32) -> Rc<StepEasingFunction> {
680 Rc::new(StepEasingFunction::with_step_count(step_count))
681 }
682}