1#![deny(missing_docs)]
10
11use crate::values::CSSFloat;
12
13const NEWTON_METHOD_ITERATIONS: u8 = 8;
14
15pub struct Bezier {
17 ax: f64,
18 bx: f64,
19 cx: f64,
20 ay: f64,
21 by: f64,
22 cy: f64,
23}
24
25impl Bezier {
26 pub fn calculate_bezier_output(
34 progress: f64,
35 epsilon: f64,
36 x1: f32,
37 y1: f32,
38 x2: f32,
39 y2: f32,
40 ) -> f64 {
41 if x1 == y1 && x2 == y2 {
43 return progress;
44 }
45
46 if progress == 0.0 {
48 return 0.0;
49 }
50 if progress == 1.0 {
51 return 1.0;
52 }
53
54 if progress < 0.0 {
57 if x1 > 0.0 {
58 return progress * y1 as f64 / x1 as f64;
59 }
60 if y1 == 0.0 && x2 > 0.0 {
61 return progress * y2 as f64 / x2 as f64;
62 }
63 return 0.0;
65 }
66
67 if progress > 1.0 {
70 if x2 < 1.0 {
71 return 1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0);
72 }
73 if y2 == 1.0 && x1 < 1.0 {
74 return 1.0 + (progress - 1.0) * (y1 as f64 - 1.0) / (x1 as f64 - 1.0);
75 }
76 return 1.0;
78 }
79
80 Bezier::new(x1, y1, x2, y2).solve(progress, epsilon)
81 }
82
83 #[inline]
84 fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier {
85 let cx = 3. * x1 as f64;
86 let bx = 3. * (x2 as f64 - x1 as f64) - cx;
87
88 let cy = 3. * y1 as f64;
89 let by = 3. * (y2 as f64 - y1 as f64) - cy;
90
91 Bezier {
92 ax: 1.0 - cx - bx,
93 bx: bx,
94 cx: cx,
95 ay: 1.0 - cy - by,
96 by: by,
97 cy: cy,
98 }
99 }
100
101 #[inline]
102 fn sample_curve_x(&self, t: f64) -> f64 {
103 ((self.ax * t + self.bx) * t + self.cx) * t
105 }
106
107 #[inline]
108 fn sample_curve_y(&self, t: f64) -> f64 {
109 ((self.ay * t + self.by) * t + self.cy) * t
110 }
111
112 #[inline]
113 fn sample_curve_derivative_x(&self, t: f64) -> f64 {
114 (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
115 }
116
117 #[inline]
118 fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {
119 let mut t = x;
121 for _ in 0..NEWTON_METHOD_ITERATIONS {
122 let x2 = self.sample_curve_x(t);
123 if x2.approx_eq(x, epsilon) {
124 return t;
125 }
126 let dx = self.sample_curve_derivative_x(t);
127 if dx.approx_eq(0.0, 1e-6) {
128 break;
129 }
130 t -= (x2 - x) / dx;
131 }
132
133 let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
135
136 if t < lo {
137 return lo;
138 }
139 if t > hi {
140 return hi;
141 }
142
143 while lo < hi {
144 let x2 = self.sample_curve_x(t);
145 if x2.approx_eq(x, epsilon) {
146 return t;
147 }
148 if x > x2 {
149 lo = t
150 } else {
151 hi = t
152 }
153 t = (hi - lo) / 2.0 + lo
154 }
155
156 t
157 }
158
159 #[inline]
162 fn solve(&self, x: f64, epsilon: f64) -> f64 {
163 self.sample_curve_y(self.solve_curve_x(x, epsilon))
164 }
165}
166
167trait ApproxEq {
168 fn approx_eq(self, value: Self, epsilon: Self) -> bool;
169}
170
171impl ApproxEq for f64 {
172 #[inline]
173 fn approx_eq(self, value: f64, epsilon: f64) -> bool {
174 (self - value).abs() < epsilon
175 }
176}