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 fn from(factor: FactorPercent) -> EasingTime {
45 EasingTime::new(factor.fct())
46 }
47}
48impl EasingTime {
49 pub fn new(factor: Factor) -> Self {
55 EasingTime(factor.clamp_range())
56 }
57
58 pub fn elapsed(duration: Duration, elapsed: Duration, time_scale: Factor) -> Self {
62 EasingTime::new(elapsed.as_secs_f32().fct() / duration.as_secs_f32().fct() * time_scale)
63 }
64
65 pub fn start() -> Self {
67 EasingTime(0.fct())
68 }
69
70 pub fn end() -> Self {
72 EasingTime(1.fct())
73 }
74
75 pub fn is_start(self) -> bool {
77 self == Self::start()
78 }
79
80 pub fn is_end(self) -> bool {
82 self == Self::end()
83 }
84
85 pub fn fct(self) -> Factor {
89 self.0
90 }
91
92 pub fn pct(self) -> FactorPercent {
96 self.0.0.pct()
97 }
98
99 pub fn reverse(self) -> Self {
103 EasingTime(self.0.flip())
104 }
105
106 pub fn seg<T: Into<EasingTime> + Clone>(self, segment_range: impl ops::RangeBounds<T>) -> Option<EasingTime> {
108 const MIN_LENGTH: f32 = EQ_GRANULARITY + 0.00001;
109
110 let start = match segment_range.start_bound() {
111 ops::Bound::Included(t) => t.clone().into().fct(),
112 ops::Bound::Excluded(t) => t.clone().into().fct() + MIN_LENGTH.fct(),
113 ops::Bound::Unbounded => 0.fct(),
114 };
115 let end = match segment_range.end_bound() {
116 ops::Bound::Included(t) => t.clone().into().fct(),
117 ops::Bound::Excluded(t) => t.clone().into().fct() - MIN_LENGTH.fct(),
118 ops::Bound::Unbounded => 1.fct(),
119 };
120 self.seg_impl(start..=end)
121 }
122
123 fn seg_impl(self, s: ops::RangeInclusive<Factor>) -> Option<EasingTime> {
124 let len = *s.end() - *s.start();
125 let v = self.0 - *s.start();
126 if v >= 0.fct() && v <= len {
127 Some(EasingTime(v / len))
128 } else {
129 None
130 }
131 }
132}
133impl ops::Add for EasingTime {
134 type Output = Self;
135
136 fn add(self, rhs: Self) -> Self::Output {
137 Self(self.0 + rhs.0)
138 }
139}
140impl ops::AddAssign for EasingTime {
141 fn add_assign(&mut self, rhs: Self) {
142 self.0 += rhs.0;
143 }
144}
145impl ops::Sub for EasingTime {
146 type Output = Self;
147
148 fn sub(self, rhs: Self) -> Self::Output {
149 Self(self.0 - rhs.0)
150 }
151}
152impl ops::SubAssign for EasingTime {
153 fn sub_assign(&mut self, rhs: Self) {
154 self.0 -= rhs.0;
155 }
156}
157
158#[derive(Clone)]
160pub enum EasingFn {
161 Linear,
163 Sine,
165 Quad,
167 Cubic,
169 Quart,
171 Quint,
173 Expo,
175 Circ,
177 Back,
179 Elastic,
181 Bounce,
183 None,
185 Custom(Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>),
187}
188impl PartialEq for EasingFn {
189 fn eq(&self, other: &Self) -> bool {
190 match (self, other) {
191 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
192 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
193 }
194 }
195}
196impl Eq for EasingFn {}
197impl fmt::Debug for EasingFn {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 match self {
200 Self::Linear => write!(f, "linear"),
201 Self::Sine => write!(f, "sine"),
202 Self::Quad => write!(f, "quad"),
203 Self::Cubic => write!(f, "cubic"),
204 Self::Quart => write!(f, "quart"),
205 Self::Quint => write!(f, "quint"),
206 Self::Expo => write!(f, "expo"),
207 Self::Circ => write!(f, "circ"),
208 Self::Back => write!(f, "back"),
209 Self::Elastic => write!(f, "elastic"),
210 Self::Bounce => write!(f, "bounce"),
211 Self::None => write!(f, "none"),
212 Self::Custom(_) => f.debug_tuple("Custom").finish(),
213 }
214 }
215}
216impl EasingFn {
217 pub fn ease_fn(&self) -> impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static {
219 let me = self.clone();
220 move |t| me(t)
221 }
222
223 pub fn custom(f: impl Fn(EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
225 Self::Custom(Arc::new(f))
226 }
227
228 pub fn modified(self, modifier: impl Fn(&dyn Fn(EasingTime) -> EasingStep, EasingTime) -> EasingStep + Send + Sync + 'static) -> Self {
230 Self::custom(move |t| modifier(&*self, t))
231 }
232
233 pub fn ease_out(self) -> Self {
235 self.modified(|f, t| easing::ease_out(f, t))
236 }
237
238 pub fn ease_in_out(self) -> Self {
240 self.modified(|f, t| easing::ease_in_out(f, t))
241 }
242
243 pub fn ease_out_in(self) -> Self {
245 self.modified(|f, t| easing::ease_out_in(f, t))
246 }
247
248 pub fn reverse(self) -> Self {
250 self.modified(|f, t| easing::reverse(f, t))
251 }
252
253 pub fn reverse_out(self) -> Self {
255 self.modified(|f, t| easing::reverse_out(f, t))
256 }
257}
258impl ops::Deref for EasingFn {
259 type Target = dyn Fn(EasingTime) -> EasingStep + Send + Sync;
260
261 fn deref(&self) -> &Self::Target {
262 match self {
263 EasingFn::Linear => &easing::linear,
264 EasingFn::Sine => &easing::sine,
265 EasingFn::Quad => &easing::quad,
266 EasingFn::Cubic => &easing::cubic,
267 EasingFn::Quart => &easing::quad,
268 EasingFn::Quint => &easing::quint,
269 EasingFn::Expo => &easing::expo,
270 EasingFn::Circ => &easing::circ,
271 EasingFn::Back => &easing::back,
272 EasingFn::Elastic => &easing::elastic,
273 EasingFn::Bounce => &easing::bounce,
274 EasingFn::None => &easing::none,
275 EasingFn::Custom(c) => &**c,
276 }
277 }
278}
279
280pub fn linear(time: EasingTime) -> EasingStep {
282 time.fct()
283}
284
285pub fn quad(time: EasingTime) -> EasingStep {
287 let f = time.fct();
288 f * f
289}
290
291pub fn cubic(time: EasingTime) -> EasingStep {
293 let f = time.fct();
294 f * f * f
295}
296
297pub fn quart(time: EasingTime) -> EasingStep {
299 let f = time.fct();
300 f * f * f * f
301}
302
303pub fn quint(time: EasingTime) -> EasingStep {
305 let f = time.fct();
306 f * f * f * f * f
307}
308
309pub fn sine(time: EasingTime) -> EasingStep {
311 let f = time.fct().0;
312 (1.0 - (f * FRAC_PI_2).cos()).fct()
313}
314
315pub fn expo(time: EasingTime) -> EasingStep {
317 let f = time.fct();
318 if f == 0.fct() {
319 0.fct()
320 } else {
321 2.0_f32.powf(10.0 * f.0 - 10.0).fct()
322 }
323}
324
325pub fn circ(time: EasingTime) -> EasingStep {
327 let f = time.fct().0;
328 (1.0 - (1.0 - f.powf(2.0)).sqrt()).fct()
329}
330
331pub fn back(time: EasingTime) -> EasingStep {
335 let f = time.fct().0;
336 (f * f * (2.70158 * f - 1.70158)).fct()
337}
338
339pub fn elastic(time: EasingTime) -> EasingStep {
341 let t = time.fct();
342
343 const C: f32 = TAU / 3.0;
344
345 if t == 0.fct() || t == 1.fct() {
346 t
347 } else {
348 let t = t.0;
349 let s = -(2.0_f32.powf(10.0 * t - 10.0)) * ((t * 10.0 - 10.75) * C).sin();
350 s.fct()
351 }
352}
353
354pub fn bounce(time: EasingTime) -> EasingStep {
357 const N: f32 = 7.5625;
358 const D: f32 = 2.75;
359
360 let mut t = 1.0 - time.fct().0;
361
362 let f = if t < 1.0 / D {
363 N * t * t
364 } else if t < 2.0 / D {
365 t -= 1.5 / D;
366 N * t * t + 0.75
367 } else if t < 2.5 / D {
368 t -= 2.25 / D;
369 N * t * t + 0.9375
370 } else {
371 t -= 2.625 / D;
372 N * t * t + 0.984375
373 };
374
375 (1.0 - f).fct()
376}
377
378pub fn cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32, time: EasingTime) -> EasingStep {
384 let f = time.fct().0 as f64;
385 (Bezier::new(x1, y1, x2, y2).solve(f, 0.00001) as f32).fct()
386}
387
388pub fn step_ceil(steps: u32, time: EasingTime) -> EasingStep {
392 let steps = steps as f32;
393 let step = (steps * time.fct().0).ceil();
394 (step / steps).fct()
395}
396
397pub fn step_floor(steps: u32, time: EasingTime) -> EasingStep {
401 let steps = steps as f32;
402 let step = (steps * time.fct().0).floor();
403 (step / steps).fct()
404}
405
406pub fn none(_: EasingTime) -> EasingStep {
408 1.fct()
409}
410
411pub fn ease_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
413 ease_fn(time)
414}
415
416pub fn ease_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
418 ease_fn(time.reverse()).flip()
419}
420
421pub fn ease_in_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
423 let t = time.fct();
424 if t <= 0.5.fct() {
425 ease_in(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
426 } else {
427 ease_out(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
428 }
429}
430
431pub fn ease_out_in(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
433 let t = time.fct();
434 if t <= 0.5.fct() {
435 ease_out(&ease_fn, EasingTime::new(t * 2.fct())) / 2.fct()
436 } else {
437 ease_in(ease_fn, EasingTime::new((t - 0.5.fct()) * 2.fct())) / 2.fct() + 0.5.fct()
438 }
439}
440
441pub fn reverse(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
443 ease_fn(time.reverse())
444}
445
446pub fn reverse_out(ease_fn: impl Fn(EasingTime) -> EasingStep, time: EasingTime) -> EasingStep {
448 ease_fn(time).flip()
449}
450
451pub use bezier::*;
452use zng_unit::{EQ_GRANULARITY, FactorPercent, FactorUnits as _};
453
454mod bezier {
455 const NEWTON_METHOD_ITERATIONS: u8 = 8;
460
461 pub struct Bezier {
463 ax: f64,
464 bx: f64,
465 cx: f64,
466 ay: f64,
467 by: f64,
468 cy: f64,
469 }
470
471 impl Bezier {
472 pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Bezier {
480 let cx = 3. * x1 as f64;
481 let bx = 3. * (x2 as f64 - x1 as f64) - cx;
482
483 let cy = 3. * y1 as f64;
484 let by = 3. * (y2 as f64 - y1 as f64) - cy;
485
486 Bezier {
487 ax: 1.0 - cx - bx,
488 bx,
489 cx,
490 ay: 1.0 - cy - by,
491 by,
492 cy,
493 }
494 }
495
496 fn sample_curve_x(&self, t: f64) -> f64 {
497 ((self.ax * t + self.bx) * t + self.cx) * t
499 }
500
501 fn sample_curve_y(&self, t: f64) -> f64 {
502 ((self.ay * t + self.by) * t + self.cy) * t
503 }
504
505 fn sample_curve_derivative_x(&self, t: f64) -> f64 {
506 (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
507 }
508
509 fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {
510 let mut t = x;
512 for _ in 0..NEWTON_METHOD_ITERATIONS {
513 let x2 = self.sample_curve_x(t);
514 if x2.approx_eq(x, epsilon) {
515 return t;
516 }
517 let dx = self.sample_curve_derivative_x(t);
518 if dx.approx_eq(0.0, 1e-6) {
519 break;
520 }
521 t -= (x2 - x) / dx;
522 }
523
524 let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
526
527 if t < lo {
528 return lo;
529 }
530 if t > hi {
531 return hi;
532 }
533
534 while lo < hi {
535 let x2 = self.sample_curve_x(t);
536 if x2.approx_eq(x, epsilon) {
537 return t;
538 }
539 if x > x2 {
540 lo = t
541 } else {
542 hi = t
543 }
544 t = (hi - lo) / 2.0 + lo
545 }
546
547 t
548 }
549
550 pub fn solve(&self, x: f64, epsilon: f64) -> f64 {
553 self.sample_curve_y(self.solve_curve_x(x, epsilon))
554 }
555 }
556
557 trait ApproxEq {
558 fn approx_eq(self, value: Self, epsilon: Self) -> bool;
559 }
560
561 impl ApproxEq for f64 {
562 fn approx_eq(self, value: f64, epsilon: f64) -> bool {
563 (self - value).abs() < epsilon
564 }
565 }
566}