1use std::{
4 f32::consts::{FRAC_PI_2, TAU},
5 fmt, ops,
6};
7
8use crate::impl_from_and_into_var;
9
10use super::*;
11
12pub type EasingStep = Factor;
32
33#[derive(Debug, PartialEq, Copy, Clone, Hash, PartialOrd)]
39pub struct EasingTime(Factor);
40impl_from_and_into_var! {
41 fn from(factor: Factor) -> EasingTime {
42 EasingTime::new(factor)
43 }
44}
45impl EasingTime {
46 pub fn new(factor: Factor) -> Self {
52 EasingTime(factor.clamp_range())
53 }
54
55 pub fn elapsed(duration: Duration, elapsed: Duration, time_scale: Factor) -> Self {
59 EasingTime::new(elapsed.as_secs_f32().fct() / duration.as_secs_f32().fct() * time_scale)
60 }
61
62 pub fn start() -> Self {
64 EasingTime(0.fct())
65 }
66
67 pub fn end() -> Self {
69 EasingTime(1.fct())
70 }
71
72 pub fn is_start(self) -> bool {
74 self == Self::start()
75 }
76
77 pub fn is_end(self) -> bool {
79 self == Self::end()
80 }
81
82 pub fn fct(self) -> Factor {
86 self.0
87 }
88
89 pub fn pct(self) -> FactorPercent {
93 self.0.0.pct()
94 }
95
96 pub fn reverse(self) -> Self {
100 EasingTime(self.0.flip())
101 }
102}
103impl ops::Add for EasingTime {
104 type Output = Self;
105
106 fn add(self, rhs: Self) -> Self::Output {
107 Self(self.0 + rhs.0)
108 }
109}
110impl ops::AddAssign for EasingTime {
111 fn add_assign(&mut self, rhs: Self) {
112 self.0 += rhs.0;
113 }
114}
115impl ops::Sub for EasingTime {
116 type Output = Self;
117
118 fn sub(self, rhs: Self) -> Self::Output {
119 Self(self.0 - rhs.0)
120 }
121}
122impl ops::SubAssign for EasingTime {
123 fn sub_assign(&mut self, rhs: Self) {
124 self.0 -= rhs.0;
125 }
126}
127
128#[derive(Clone)]
130pub enum EasingFn {
131 Linear,
133 Sine,
135 Quad,
137 Cubic,
139 Quart,
141 Quint,
143 Expo,
145 Circ,
147 Back,
149 Elastic,
151 Bounce,
153 None,
155 Custom(Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>),
157}
158impl PartialEq for EasingFn {
159 fn eq(&self, other: &Self) -> bool {
160 match (self, other) {
161 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
162 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
163 }
164 }
165}
166impl Eq for EasingFn {}
167impl fmt::Debug for EasingFn {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 Self::Linear => write!(f, "linear"),
171 Self::Sine => write!(f, "sine"),
172 Self::Quad => write!(f, "quad"),
173 Self::Cubic => write!(f, "cubic"),
174 Self::Quart => write!(f, "quart"),
175 Self::Quint => write!(f, "quint"),
176 Self::Expo => write!(f, "expo"),
177 Self::Circ => write!(f, "circ"),
178 Self::Back => write!(f, "back"),
179 Self::Elastic => write!(f, "elastic"),
180 Self::Bounce => write!(f, "bounce"),
181 Self::None => write!(f, "none"),
182 Self::Custom(_) => f.debug_tuple("Custom").finish(),
183 }
184 }
185}
186impl EasingFn {
187 pub fn ease_fn(&self) -> impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static {
189 let me = self.clone();
190 move |t| me(t)
191 }
192
193 pub fn custom(f: impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
195 Self::Custom(Arc::new(f))
196 }
197
198 pub fn modified(self, modifier: impl Fn(&dyn Fn(EasingTime) -> EasingStep, EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
200 Self::custom(move |t| modifier(&*self, t))
201 }
202
203 pub fn ease_out(self) -> Self {
205 self.modified(|f, t| easing::ease_out(f, t))
206 }
207
208 pub fn ease_in_out(self) -> Self {
210 self.modified(|f, t| easing::ease_in_out(f, t))
211 }
212
213 pub fn ease_out_in(self) -> Self {
215 self.modified(|f, t| easing::ease_out_in(f, t))
216 }
217
218 pub fn reverse(self) -> Self {
220 self.modified(|f, t| easing::reverse(f, t))
221 }
222
223 pub fn reverse_out(self) -> Self {
225 self.modified(|f, t| easing::reverse_out(f, t))
226 }
227}
228impl ops::Deref for EasingFn {
229 type Target = dyn Fn(EasingTime) -> EasingStep + Send + Sync;
230
231 fn deref(&self) -> &Self::Target {
232 match self {
233 EasingFn::Linear => &easing::linear,
234 EasingFn::Sine => &easing::sine,
235 EasingFn::Quad => &easing::quad,
236 EasingFn::Cubic => &easing::cubic,
237 EasingFn::Quart => &easing::quad,
238 EasingFn::Quint => &easing::quint,
239 EasingFn::Expo => &easing::expo,
240 EasingFn::Circ => &easing::circ,
241 EasingFn::Back => &easing::back,
242 EasingFn::Elastic => &easing::elastic,
243 EasingFn::Bounce => &easing::bounce,
244 EasingFn::None => &easing::none,
245 EasingFn::Custom(c) => &**c,
246 }
247 }
248}
249
250pub fn linear(time: EasingTime) -> EasingStep {
252 time.fct()
253}
254
255pub fn quad(time: EasingTime) -> EasingStep {
257 let f = time.fct();
258 f * f
259}
260
261pub fn cubic(time: EasingTime) -> EasingStep {
263 let f = time.fct();
264 f * f * f
265}
266
267pub fn quart(time: EasingTime) -> EasingStep {
269 let f = time.fct();
270 f * f * f * f
271}
272
273pub fn quint(time: EasingTime) -> EasingStep {
275 let f = time.fct();
276 f * f * f * f * f
277}
278
279pub fn sine(time: EasingTime) -> EasingStep {
281 let f = time.fct().0;
282 (1.0 - (f * FRAC_PI_2).cos()).fct()
283}
284
285pub fn expo(time: EasingTime) -> EasingStep {
287 let f = time.fct();
288 if f == 0.fct() {
289 0.fct()
290 } else {
291 2.0_f32.powf(10.0 * f.0 - 10.0).fct()
292 }
293}
294
295pub fn circ(time: EasingTime) -> EasingStep {
297 let f = time.fct().0;
298 (1.0 - (1.0 - f.powf(2.0)).sqrt()).fct()
299}
300
301pub fn back(time: EasingTime) -> EasingStep {
305 let f = time.fct().0;
306 (f * f * (2.70158 * f - 1.70158)).fct()
307}
308
309pub fn elastic(time: EasingTime) -> EasingStep {
311 let t = time.fct();
312
313 const C: f32 = TAU / 3.0;
314
315 if t == 0.fct() || t == 1.fct() {
316 t
317 } else {
318 let t = t.0;
319 let s = -(2.0_f32.powf(10.0 * t - 10.0)) * ((t * 10.0 - 10.75) * C).sin();
320 s.fct()
321 }
322}
323
324pub fn bounce(time: EasingTime) -> EasingStep {
327 const N: f32 = 7.5625;
328 const D: f32 = 2.75;
329
330 let mut t = 1.0 - time.fct().0;
331
332 let f = if t < 1.0 / D {
333 N * t * t
334 } else if t < 2.0 / D {
335 t -= 1.5 / D;
336 N * t * t + 0.75
337 } else if t < 2.5 / D {
338 t -= 2.25 / D;
339 N * t * t + 0.9375
340 } else {
341 t -= 2.625 / D;
342 N * t * t + 0.984375
343 };
344
345 (1.0 - f).fct()
346}
347
348pub fn cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32, time: EasingTime) -> EasingStep {
354 let f = time.fct().0 as f64;
355 (Bezier::new(x1, y1, x2, y2).solve(f, 0.00001) as f32).fct()
356}
357
358pub fn step_ceil(steps: u32, time: EasingTime) -> EasingStep {
362 let steps = steps as f32;
363 let step = (steps * time.fct().0).ceil();
364 (step / steps).fct()
365}
366
367pub fn step_floor(steps: u32, time: EasingTime) -> EasingStep {
371 let steps = steps as f32;
372 let step = (steps * time.fct().0).floor();
373 (step / steps).fct()
374}
375
376pub fn none(_: EasingTime) -> EasingStep {
378 1.fct()
379}
380
381pub fn ease_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
383 ease_fn(time)
384}
385
386pub fn ease_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
388 ease_fn(time.reverse()).flip()
389}
390
391pub fn ease_in_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
393 let t = time.fct();
394 if t <= 0.5.fct() {
395 ease_in(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
396 } else {
397 ease_out(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
398 }
399}
400
401pub fn ease_out_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
403 let t = time.fct();
404 if t <= 0.5.fct() {
405 ease_out(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
406 } else {
407 ease_in(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
408 }
409}
410
411pub fn reverse(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
413 ease_fn(time.reverse())
414}
415
416pub fn reverse_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
418 ease_fn(time).flip()
419}
420
421pub use bezier::*;
422use zng_unit::{FactorPercent, FactorUnits as _};
423
424mod bezier {
425 const NEWTON_METHOD_ITERATIONS: u8 = 8;
430
431 pub struct Bezier {
433 ax: f64,
434 bx: f64,
435 cx: f64,
436 ay: f64,
437 by: f64,
438 cy: f64,
439 }
440
441 impl Bezier {
442 pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Bezier {
450 let cx = 3. * x1 as f64;
451 let bx = 3. * (x2 as f64 - x1 as f64) - cx;
452
453 let cy = 3. * y1 as f64;
454 let by = 3. * (y2 as f64 - y1 as f64) - cy;
455
456 Bezier {
457 ax: 1.0 - cx - bx,
458 bx,
459 cx,
460 ay: 1.0 - cy - by,
461 by,
462 cy,
463 }
464 }
465
466 fn sample_curve_x(&self, t: f64) -> f64 {
467 ((self.ax * t + self.bx) * t + self.cx) * t
469 }
470
471 fn sample_curve_y(&self, t: f64) -> f64 {
472 ((self.ay * t + self.by) * t + self.cy) * t
473 }
474
475 fn sample_curve_derivative_x(&self, t: f64) -> f64 {
476 (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
477 }
478
479 fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {
480 let mut t = x;
482 for _ in 0..NEWTON_METHOD_ITERATIONS {
483 let x2 = self.sample_curve_x(t);
484 if x2.approx_eq(x, epsilon) {
485 return t;
486 }
487 let dx = self.sample_curve_derivative_x(t);
488 if dx.approx_eq(0.0, 1e-6) {
489 break;
490 }
491 t -= (x2 - x) / dx;
492 }
493
494 let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
496
497 if t < lo {
498 return lo;
499 }
500 if t > hi {
501 return hi;
502 }
503
504 while lo < hi {
505 let x2 = self.sample_curve_x(t);
506 if x2.approx_eq(x, epsilon) {
507 return t;
508 }
509 if x > x2 {
510 lo = t
511 } else {
512 hi = t
513 }
514 t = (hi - lo) / 2.0 + lo
515 }
516
517 t
518 }
519
520 pub fn solve(&self, x: f64, epsilon: f64) -> f64 {
523 self.sample_curve_y(self.solve_curve_x(x, epsilon))
524 }
525 }
526
527 trait ApproxEq {
528 fn approx_eq(self, value: Self, epsilon: Self) -> bool;
529 }
530
531 impl ApproxEq for f64 {
532 fn approx_eq(self, value: f64, epsilon: f64) -> bool {
533 (self - value).abs() < epsilon
534 }
535 }
536}