-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathanimation_easing.v
More file actions
204 lines (175 loc) · 5.5 KB
/
Copy pathanimation_easing.v
File metadata and controls
204 lines (175 loc) · 5.5 KB
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
module gui
import math
// LUT size for bezier precomputation (257 entries for 0..256 inclusive)
const bezier_lut_size = 257
// EasingFn maps progress t (0.0 to 1.0) to an eased value.
// Use with TweenAnimation to control motion curves.
pub type EasingFn = fn (f32) f32
// ease_linear returns constant-speed motion with no acceleration.
pub fn ease_linear(t f32) f32 {
return t
}
// ease_in_quad starts slow and accelerates (quadratic curve).
pub fn ease_in_quad(t f32) f32 {
return t * t
}
// ease_out_quad starts fast and decelerates (quadratic curve).
pub fn ease_out_quad(t f32) f32 {
return 1 - (1 - t) * (1 - t)
}
// ease_in_out_quad accelerates then decelerates (quadratic curve).
pub fn ease_in_out_quad(t f32) f32 {
return if t < 0.5 { 2 * t * t } else { 1 - f32(math.pow(-2 * t + 2, 2)) / 2 }
}
// ease_in_cubic starts slow and accelerates (cubic curve, smoother than quad).
pub fn ease_in_cubic(t f32) f32 {
return t * t * t
}
// ease_out_cubic starts fast and decelerates (cubic curve, smoother than quad).
pub fn ease_out_cubic(t f32) f32 {
u := 1 - t
return 1 - u * u * u
}
// ease_in_out_cubic accelerates then decelerates (cubic curve).
pub fn ease_in_out_cubic(t f32) f32 {
return if t < 0.5 { 4 * t * t * t } else { 1 - f32(math.pow(-2 * t + 2, 3)) / 2 }
}
// ease_in_back pulls back slightly before accelerating forward.
pub fn ease_in_back(t f32) f32 {
c1 := f32(1.70158)
c3 := c1 + 1
return c3 * t * t * t - c1 * t * t
}
// ease_out_back overshoots the target then settles back.
pub fn ease_out_back(t f32) f32 {
c1 := f32(1.70158)
c3 := c1 + 1
return 1 + c3 * f32(math.pow(t - 1, 3)) + c1 * f32(math.pow(t - 1, 2))
}
// ease_out_elastic overshoots and oscillates like a released spring.
pub fn ease_out_elastic(t f32) f32 {
if t == 0 {
return 0
}
if t == 1 {
return 1
}
c4 := f32(2 * math.pi) / 3
return f32(math.pow(2, -10 * t)) * f32(math.sin((t * 10 - 0.75) * c4)) + 1
}
// ease_out_bounce simulates a bouncing ball settling to rest.
pub fn ease_out_bounce(t f32) f32 {
n1 := f32(7.5625)
d1 := f32(2.75)
mut t_ := t
if t_ < 1 / d1 {
return n1 * t_ * t_
} else if t_ < 2 / d1 {
t_ -= 1.5 / d1
return n1 * t_ * t_ + 0.75
} else if t_ < 2.5 / d1 {
t_ -= 2.25 / d1
return n1 * t_ * t_ + 0.9375
} else {
t_ -= 2.625 / d1
return n1 * t_ * t_ + 0.984375
}
}
// BezierLUT stores precomputed bezier values for O(1) lookup
struct BezierLUT {
mut:
values [bezier_lut_size]f32
}
// build_bezier_lut precomputes bezier curve values
fn build_bezier_lut(x1 f32, y1 f32, x2 f32, y2 f32) BezierLUT {
mut lut := BezierLUT{}
for i in 0 .. bezier_lut_size {
t := f32(i) / f32(bezier_lut_size - 1)
lut.values[i] = bezier_calc(t, x1, y1, x2, y2)
}
return lut
}
// lookup_bezier returns interpolated value from LUT
@[inline]
fn (lut &BezierLUT) lookup(t f32) f32 {
if t <= 0 {
return 0
}
if t >= 1 {
return 1
}
// Map t to LUT index with interpolation
idx_f := t * f32(bezier_lut_size - 1)
idx := int(idx_f)
frac := idx_f - f32(idx)
if idx >= bezier_lut_size - 1 {
return lut.values[bezier_lut_size - 1]
}
// Linear interpolation between LUT entries
return lut.values[idx] + frac * (lut.values[idx + 1] - lut.values[idx])
}
// Precomputed LUTs for common CSS bezier curves
const ease_lut = build_bezier_lut(0.25, 0.1, 0.25, 1.0)
const ease_in_lut = build_bezier_lut(0.42, 0, 1.0, 1.0)
const ease_out_lut = build_bezier_lut(0, 0, 0.58, 1.0)
const ease_in_out_lut = build_bezier_lut(0.42, 0, 0.58, 1.0)
// ease_css returns CSS "ease" curve (fast start, slow end). Uses precomputed LUT.
pub fn ease_css(t f32) f32 {
return ease_lut.lookup(t)
}
// ease_in_css returns CSS "ease-in" curve. Uses precomputed LUT.
pub fn ease_in_css(t f32) f32 {
return ease_in_lut.lookup(t)
}
// ease_out_css returns CSS "ease-out" curve. Uses precomputed LUT.
pub fn ease_out_css(t f32) f32 {
return ease_out_lut.lookup(t)
}
// ease_in_out_css returns CSS "ease-in-out" curve. Uses precomputed LUT.
pub fn ease_in_out_css(t f32) f32 {
return ease_in_out_lut.lookup(t)
}
// cubic_bezier creates a custom easing function from bezier control points.
// Works like CSS cubic-bezier(). For standard CSS curves, prefer the precomputed
// LUT versions (ease_css, ease_in_css, ease_out_css, ease_in_out_css) which are O(1).
// Common presets:
// ease: cubic_bezier(0.25, 0.1, 0.25, 1.0) or ease_css
// ease-in: cubic_bezier(0.42, 0, 1.0, 1.0) or ease_in_css
// ease-out: cubic_bezier(0, 0, 0.58, 1.0) or ease_out_css
// ease-in-out: cubic_bezier(0.42, 0, 0.58, 1.0) or ease_in_out_css
pub fn cubic_bezier(x1 f32, y1 f32, x2 f32, y2 f32) EasingFn {
return fn [x1, y1, x2, y2] (t f32) f32 {
return bezier_calc(t, x1, y1, x2, y2)
}
}
// bezier_calc approximates cubic bezier curve value
fn bezier_calc(t f32, x1 f32, y1 f32, x2 f32, y2 f32) f32 {
// Newton-Raphson iteration to find t for x
mut guess := t
for _ in 0 .. 8 {
x := bezier_x(guess, x1, x2) - t
if f32_abs(x) < 0.001 {
break
}
dx := bezier_dx(guess, x1, x2)
if f32_abs(dx) < 0.000001 {
break
}
guess -= x / dx
}
return bezier_y(guess, y1, y2)
}
fn bezier_x(t f32, x1 f32, x2 f32) f32 {
return 3 * (1 - t) * (1 - t) * t * x1 + 3 * (1 - t) * t * t * x2 + t * t * t
}
fn bezier_y(t f32, y1 f32, y2 f32) f32 {
return 3 * (1 - t) * (1 - t) * t * y1 + 3 * (1 - t) * t * t * y2 + t * t * t
}
fn bezier_dx(t f32, x1 f32, x2 f32) f32 {
return 3 * (1 - t) * (1 - t) * x1 + 6 * (1 - t) * t * (x2 - x1) + 3 * t * t * (1 - x2)
}
// lerp linearly interpolates between a and b by t
@[inline]
pub fn lerp(a f32, b f32, t f32) f32 {
return a + (b - a) * t
}