1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
use keyframe::EasingFunction;
#[derive(Debug, Clone, Copy)]
pub struct CubicBezier {
x1: f64,
y1: f64,
x2: f64,
y2: f64,
}
impl CubicBezier {
pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
Self { x1, y1, x2, y2 }
}
// Based on libadwaita (LGPL-2.1-or-later):
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.7.6/src/adw-easing.c?ref_type=tags#L469-531
fn x_for_t(&self, t: f64) -> f64 {
let omt = 1. - t;
3. * omt * omt * t * self.x1 + 3. * omt * t * t * self.x2 + t * t * t
}
fn y_for_t(&self, t: f64) -> f64 {
let omt = 1. - t;
3. * omt * omt * t * self.y1 + 3. * omt * t * t * self.y2 + t * t * t
}
fn t_for_x(&self, x: f64) -> f64 {
let mut min_t = 0.;
let mut max_t = 1.;
for _ in 0..=30 {
let guess_t = (min_t + max_t) / 2.;
let guess_x = self.x_for_t(guess_t);
if x < guess_x {
max_t = guess_t;
} else {
min_t = guess_t;
}
}
(min_t + max_t) / 2.
}
}
impl EasingFunction for CubicBezier {
fn y(&self, x: f64) -> f64 {
if x <= f64::EPSILON {
return 0.;
}
if 1. - f64::EPSILON <= x {
return 1.;
}
self.y_for_t(self.t_for_x(x))
}
}
|