1use std::f32::consts::{FRAC_PI_2, TAU};
4
5use super::*;
6
7pub type EasingStep = Factor;
27
28#[derive(Debug, PartialEq, Copy, Clone, Hash, PartialOrd)]
34pub struct EasingTime(Factor);
35impl_from_and_into_var! {
36 fn from(factor: Factor) -> EasingTime {
37 EasingTime::new(factor)
38 }
39}
40impl EasingTime {
41 pub fn new(factor: Factor) -> Self {
47 EasingTime(factor.clamp_range())
48 }
49
50 pub fn elapsed(duration: Duration, elapsed: Duration, time_scale: Factor) -> Self {
54 EasingTime::new(elapsed.as_secs_f32().fct() / duration.as_secs_f32().fct() * time_scale)
55 }
56
57 pub fn start() -> Self {
59 EasingTime(0.fct())
60 }
61
62 pub fn end() -> Self {
64 EasingTime(1.fct())
65 }
66
67 pub fn is_start(self) -> bool {
69 self == Self::start()
70 }
71
72 pub fn is_end(self) -> bool {
74 self == Self::end()
75 }
76
77 pub fn fct(self) -> Factor {
81 self.0
82 }
83
84 pub fn pct(self) -> FactorPercent {
88 self.0.0.pct()
89 }
90
91 pub fn reverse(self) -> Self {
95 EasingTime(self.0.flip())
96 }
97}
98impl ops::Add for EasingTime {
99 type Output = Self;
100
101 fn add(self, rhs: Self) -> Self::Output {
102 Self(self.0 + rhs.0)
103 }
104}
105impl ops::AddAssign for EasingTime {
106 fn add_assign(&mut self, rhs: Self) {
107 self.0 += rhs.0;
108 }
109}
110impl ops::Sub for EasingTime {
111 type Output = Self;
112
113 fn sub(self, rhs: Self) -> Self::Output {
114 Self(self.0 - rhs.0)
115 }
116}
117impl ops::SubAssign for EasingTime {
118 fn sub_assign(&mut self, rhs: Self) {
119 self.0 -= rhs.0;
120 }
121}
122
123#[derive(Clone)]
125pub enum EasingFn {
126 Linear,
128 Sine,
130 Quad,
132 Cubic,
134 Quart,
136 Quint,
138 Expo,
140 Circ,
142 Back,
144 Elastic,
146 Bounce,
148 None,
150 Custom(Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>),
152}
153impl PartialEq for EasingFn {
154 fn eq(&self, other: &Self) -> bool {
155 match (self, other) {
156 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
157 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
158 }
159 }
160}
161impl Eq for EasingFn {}
162impl fmt::Debug for EasingFn {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 Self::Linear => write!(f, "linear"),
166 Self::Sine => write!(f, "sine"),
167 Self::Quad => write!(f, "quad"),
168 Self::Cubic => write!(f, "cubic"),
169 Self::Quart => write!(f, "quart"),
170 Self::Quint => write!(f, "quint"),
171 Self::Expo => write!(f, "expo"),
172 Self::Circ => write!(f, "circ"),
173 Self::Back => write!(f, "back"),
174 Self::Elastic => write!(f, "elastic"),
175 Self::Bounce => write!(f, "bounce"),
176 Self::None => write!(f, "none"),
177 Self::Custom(_) => f.debug_tuple("Custom").finish(),
178 }
179 }
180}
181impl EasingFn {
182 pub fn ease_fn(&self) -> impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static {
184 let me = self.clone();
185 move |t| me(t)
186 }
187
188 pub fn custom(f: impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
190 Self::Custom(Arc::new(f))
191 }
192
193 pub fn modified(self, modifier: impl Fn(&dyn Fn(EasingTime) -> EasingStep, EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
195 Self::custom(move |t| modifier(&*self, t))
196 }
197
198 pub fn ease_out(self) -> Self {
200 self.modified(|f, t| easing::ease_out(f, t))
201 }
202
203 pub fn ease_in_out(self) -> Self {
205 self.modified(|f, t| easing::ease_in_out(f, t))
206 }
207
208 pub fn ease_out_in(self) -> Self {
210 self.modified(|f, t| easing::ease_out_in(f, t))
211 }
212
213 pub fn reverse(self) -> Self {
215 self.modified(|f, t| easing::reverse(f, t))
216 }
217
218 pub fn reverse_out(self) -> Self {
220 self.modified(|f, t| easing::reverse_out(f, t))
221 }
222}
223impl ops::Deref for EasingFn {
224 type Target = dyn Fn(EasingTime) -> EasingStep + Send + Sync;
225
226 fn deref(&self) -> &Self::Target {
227 match self {
228 EasingFn::Linear => &easing::linear,
229 EasingFn::Sine => &easing::sine,
230 EasingFn::Quad => &easing::quad,
231 EasingFn::Cubic => &easing::cubic,
232 EasingFn::Quart => &easing::quad,
233 EasingFn::Quint => &easing::quint,
234 EasingFn::Expo => &easing::expo,
235 EasingFn::Circ => &easing::circ,
236 EasingFn::Back => &easing::back,
237 EasingFn::Elastic => &easing::elastic,
238 EasingFn::Bounce => &easing::bounce,
239 EasingFn::None => &easing::none,
240 EasingFn::Custom(c) => &**c,
241 }
242 }
243}
244
245pub fn linear(time: EasingTime) -> EasingStep {
247 time.fct()
248}
249
250pub fn quad(time: EasingTime) -> EasingStep {
252 let f = time.fct();
253 f * f
254}
255
256pub fn cubic(time: EasingTime) -> EasingStep {
258 let f = time.fct();
259 f * f * f
260}
261
262pub fn quart(time: EasingTime) -> EasingStep {
264 let f = time.fct();
265 f * f * f * f
266}
267
268pub fn quint(time: EasingTime) -> EasingStep {
270 let f = time.fct();
271 f * f * f * f * f
272}
273
274pub fn sine(time: EasingTime) -> EasingStep {
276 let f = time.fct().0;
277 (1.0 - (f * FRAC_PI_2).cos()).fct()
278}
279
280pub fn expo(time: EasingTime) -> EasingStep {
282 let f = time.fct();
283 if f == 0.fct() {
284 0.fct()
285 } else {
286 2.0_f32.powf(10.0 * f.0 - 10.0).fct()
287 }
288}
289
290pub fn circ(time: EasingTime) -> EasingStep {
292 let f = time.fct().0;
293 (1.0 - (1.0 - f.powf(2.0)).sqrt()).fct()
294}
295
296pub fn back(time: EasingTime) -> EasingStep {
300 let f = time.fct().0;
301 (f * f * (2.70158 * f - 1.70158)).fct()
302}
303
304pub fn elastic(time: EasingTime) -> EasingStep {
306 let t = time.fct();
307
308 const C: f32 = TAU / 3.0;
309
310 if t == 0.fct() || t == 1.fct() {
311 t
312 } else {
313 let t = t.0;
314 let s = -(2.0_f32.powf(10.0 * t - 10.0)) * ((t * 10.0 - 10.75) * C).sin();
315 s.fct()
316 }
317}
318
319pub fn bounce(time: EasingTime) -> EasingStep {
322 const N: f32 = 7.5625;
323 const D: f32 = 2.75;
324
325 let mut t = 1.0 - time.fct().0;
326
327 let f = if t < 1.0 / D {
328 N * t * t
329 } else if t < 2.0 / D {
330 t -= 1.5 / D;
331 N * t * t + 0.75
332 } else if t < 2.5 / D {
333 t -= 2.25 / D;
334 N * t * t + 0.9375
335 } else {
336 t -= 2.625 / D;
337 N * t * t + 0.984375
338 };
339
340 (1.0 - f).fct()
341}
342
343pub fn cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32, time: EasingTime) -> EasingStep {
349 let f = time.fct().0 as f64;
350 (Bezier::new(x1, y1, x2, y2).solve(f, 0.00001) as f32).fct()
351}
352
353pub fn step_ceil(steps: u32, time: EasingTime) -> EasingStep {
357 let steps = steps as f32;
358 let step = (steps * time.fct().0).ceil();
359 (step / steps).fct()
360}
361
362pub fn step_floor(steps: u32, time: EasingTime) -> EasingStep {
366 let steps = steps as f32;
367 let step = (steps * time.fct().0).floor();
368 (step / steps).fct()
369}
370
371pub fn none(_: EasingTime) -> EasingStep {
373 1.fct()
374}
375
376pub fn ease_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
378 ease_fn(time)
379}
380
381pub fn ease_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
383 ease_fn(time.reverse()).flip()
384}
385
386pub fn ease_in_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
388 let t = time.fct();
389 if t <= 0.5.fct() {
390 ease_in(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
391 } else {
392 ease_out(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
393 }
394}
395
396pub fn ease_out_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
398 let t = time.fct();
399 if t <= 0.5.fct() {
400 ease_out(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
401 } else {
402 ease_in(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
403 }
404}
405
406pub fn reverse(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
408 ease_fn(time.reverse())
409}
410
411pub fn reverse_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
413 ease_fn(time).flip()
414}
415
416pub use bezier::*;
417use zng_unit::FactorPercent;
418
419mod bezier {
420 const NEWTON_METHOD_ITERATIONS: u8 = 8;
425
426 pub struct Bezier {
428 ax: f64,
429 bx: f64,
430 cx: f64,
431 ay: f64,
432 by: f64,
433 cy: f64,
434 }
435
436 impl Bezier {
437 pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Bezier {
445 let cx = 3. * x1 as f64;
446 let bx = 3. * (x2 as f64 - x1 as f64) - cx;
447
448 let cy = 3. * y1 as f64;
449 let by = 3. * (y2 as f64 - y1 as f64) - cy;
450
451 Bezier {
452 ax: 1.0 - cx - bx,
453 bx,
454 cx,
455 ay: 1.0 - cy - by,
456 by,
457 cy,
458 }
459 }
460
461 fn sample_curve_x(&self, t: f64) -> f64 {
462 ((self.ax * t + self.bx) * t + self.cx) * t
464 }
465
466 fn sample_curve_y(&self, t: f64) -> f64 {
467 ((self.ay * t + self.by) * t + self.cy) * t
468 }
469
470 fn sample_curve_derivative_x(&self, t: f64) -> f64 {
471 (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
472 }
473
474 fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {
475 let mut t = x;
477 for _ in 0..NEWTON_METHOD_ITERATIONS {
478 let x2 = self.sample_curve_x(t);
479 if x2.approx_eq(x, epsilon) {
480 return t;
481 }
482 let dx = self.sample_curve_derivative_x(t);
483 if dx.approx_eq(0.0, 1e-6) {
484 break;
485 }
486 t -= (x2 - x) / dx;
487 }
488
489 let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
491
492 if t < lo {
493 return lo;
494 }
495 if t > hi {
496 return hi;
497 }
498
499 while lo < hi {
500 let x2 = self.sample_curve_x(t);
501 if x2.approx_eq(x, epsilon) {
502 return t;
503 }
504 if x > x2 {
505 lo = t
506 } else {
507 hi = t
508 }
509 t = (hi - lo) / 2.0 + lo
510 }
511
512 t
513 }
514
515 pub fn solve(&self, x: f64, epsilon: f64) -> f64 {
518 self.sample_curve_y(self.solve_curve_x(x, epsilon))
519 }
520 }
521
522 trait ApproxEq {
523 fn approx_eq(self, value: Self, epsilon: Self) -> bool;
524 }
525
526 impl ApproxEq for f64 {
527 fn approx_eq(self, value: f64, epsilon: f64) -> bool {
528 (self - value).abs() < epsilon
529 }
530 }
531}