|
1 |
| -"""A selection of rate functions, i.e., *speed curves* for animations.""" |
| 1 | +"""A selection of rate functions, i.e., *speed curves* for animations. |
| 2 | +Please find a standard list at https://easings.net/. Here is a picture for the non-standard ones |
| 3 | +
|
| 4 | +.. image:: /_static/non_standard_rate_funcs.png |
| 5 | + :alt: Non-standard rate functions |
| 6 | +
|
| 7 | +
|
| 8 | +There are primarily 3 kinds of standard easing functions: |
| 9 | +
|
| 10 | +#. Ease In - The animation has a smooth start. |
| 11 | +#. Ease Out - The animation has a smooth end. |
| 12 | +#. Ease In Out - The animation has a smooth start as well as smooth end. |
| 13 | +
|
| 14 | +.. note:: The standard functions are not exported, so to use them you do something like this: |
| 15 | + rate_func=rate_functions.ease_in_sine |
| 16 | + On the other hand, the non-standard functions, which are used more commonly, are exported and can be used directly. |
| 17 | +
|
| 18 | +.. manim:: RateFunctions1Example |
| 19 | +
|
| 20 | + class RateFunctions1Example(Scene): |
| 21 | + def construct(self): |
| 22 | + line1 = Line(3*LEFT, 3*RIGHT).shift(UP).set_color(RED) |
| 23 | + line2 = Line(3*LEFT, 3*RIGHT).set_color(GREEN) |
| 24 | + line3 = Line(3*LEFT, 3*RIGHT).shift(DOWN).set_color(BLUE) |
| 25 | +
|
| 26 | + dot1 = Dot().move_to(line1.get_left()) |
| 27 | + dot2 = Dot().move_to(line2.get_left()) |
| 28 | + dot3 = Dot().move_to(line3.get_left()) |
| 29 | +
|
| 30 | + label1 = Tex("Ease In").next_to(line1, RIGHT) |
| 31 | + label2 = Tex("Ease out").next_to(line2, RIGHT) |
| 32 | + label3 = Tex("Ease In Out").next_to(line3, RIGHT) |
| 33 | +
|
| 34 | + self.play( |
| 35 | + FadeIn(VGroup(line1, line2, line3), |
| 36 | + FadeIn(VGroup(dot1, dot2, dot3))), |
| 37 | + Write(VGroup(label1, label2, label3)), |
| 38 | + ) |
| 39 | + self.play( |
| 40 | + MoveAlongPath(dot1, line1, rate_func=rate_functions.ease_in_sine), |
| 41 | + MoveAlongPath(dot2, line2, rate_func=rate_functions.ease_out_sine), |
| 42 | + MoveAlongPath(dot3, line3, rate_func=rate_functions.ease_in_out_sine), |
| 43 | + run_time=7 |
| 44 | + ) |
| 45 | + self.wait() |
| 46 | +""" |
| 47 | + |
2 | 48 |
|
3 | 49 | __all__ = [
|
4 | 50 | "linear",
|
|
18 | 64 | ]
|
19 | 65 |
|
20 | 66 |
|
| 67 | +from math import sqrt |
| 68 | + |
21 | 69 | import numpy as np
|
22 | 70 |
|
23 | 71 | from ..utils.bezier import bezier
|
@@ -115,3 +163,180 @@ def exponential_decay(t, half_life=0.1):
|
115 | 163 | # The half-life should be rather small to minimize
|
116 | 164 | # the cut-off error at the end
|
117 | 165 | return 1 - np.exp(-t / half_life)
|
| 166 | + |
| 167 | + |
| 168 | +def ease_in_sine(t): |
| 169 | + return 1 - np.cos((t * np.pi) / 2) |
| 170 | + |
| 171 | + |
| 172 | +def ease_out_sine(t): |
| 173 | + return np.sin((t * np.pi) / 2) |
| 174 | + |
| 175 | + |
| 176 | +def ease_in_out_sine(t): |
| 177 | + return -(np.cos(np.pi * t) - 1) / 2 |
| 178 | + |
| 179 | + |
| 180 | +def ease_in_quad(t): |
| 181 | + return t * t |
| 182 | + |
| 183 | + |
| 184 | +def ease_out_quad(t): |
| 185 | + return 1 - (1 - t) * (1 - t) |
| 186 | + |
| 187 | + |
| 188 | +def ease_in_out_quad(t): |
| 189 | + return 2 * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 2) / 2 |
| 190 | + |
| 191 | + |
| 192 | +def ease_in_cubic(t): |
| 193 | + return t * t * t |
| 194 | + |
| 195 | + |
| 196 | +def ease_out_cubic(t): |
| 197 | + return 1 - pow(1 - t, 3) |
| 198 | + |
| 199 | + |
| 200 | +def ease_in_out_cubic(t): |
| 201 | + return 4 * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 3) / 2 |
| 202 | + |
| 203 | + |
| 204 | +def ease_in_quart(t): |
| 205 | + return t * t * t * t |
| 206 | + |
| 207 | + |
| 208 | +def ease_out_quart(t): |
| 209 | + return 1 - pow(1 - t, 4) |
| 210 | + |
| 211 | + |
| 212 | +def ease_in_out_quart(t): |
| 213 | + return 8 * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 4) / 2 |
| 214 | + |
| 215 | + |
| 216 | +def ease_in_quint(t): |
| 217 | + return t * t * t * t * t |
| 218 | + |
| 219 | + |
| 220 | +def ease_out_quint(t): |
| 221 | + return 1 - pow(1 - t, 5) |
| 222 | + |
| 223 | + |
| 224 | +def ease_in_out_quint(t): |
| 225 | + return 16 * t * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 5) / 2 |
| 226 | + |
| 227 | + |
| 228 | +def ease_in_expo(t): |
| 229 | + return 0 if t == 0 else pow(2, 10 * t - 10) |
| 230 | + |
| 231 | + |
| 232 | +def ease_out_expo(t): |
| 233 | + return 1 if t == 1 else 1 - pow(2, -10 * t) |
| 234 | + |
| 235 | + |
| 236 | +def ease_in_out_expo(t): |
| 237 | + if t == 0: |
| 238 | + return 0 |
| 239 | + elif t == 1: |
| 240 | + return 1 |
| 241 | + elif t < 0.5: |
| 242 | + return pow(2, 20 * t - 10) / 2 |
| 243 | + else: |
| 244 | + return 2 - pow(2, -20 * t + 10) / 2 |
| 245 | + |
| 246 | + |
| 247 | +def ease_in_circ(t): |
| 248 | + return 1 - sqrt(1 - pow(t, 2)) |
| 249 | + |
| 250 | + |
| 251 | +def ease_out_circ(t): |
| 252 | + return sqrt(1 - pow(t - 1, 2)) |
| 253 | + |
| 254 | + |
| 255 | +def ease_in_out_circ(t): |
| 256 | + return ( |
| 257 | + (1 - sqrt(1 - pow(2 * t, 2))) / 2 |
| 258 | + if t < 0.5 |
| 259 | + else (sqrt(1 - pow(-2 * t + 2, 2)) + 1) / 2 |
| 260 | + ) |
| 261 | + |
| 262 | + |
| 263 | +def ease_in_back(t): |
| 264 | + c1 = 1.70158 |
| 265 | + c3 = c1 + 1 |
| 266 | + return c3 * t * t * t - c1 * t * t |
| 267 | + |
| 268 | + |
| 269 | +def ease_out_back(t): |
| 270 | + c1 = 1.70158 |
| 271 | + c3 = c1 + 1 |
| 272 | + return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2) |
| 273 | + |
| 274 | + |
| 275 | +def ease_in_out_back(t): |
| 276 | + c1 = 1.70158 |
| 277 | + c2 = c1 * 1.525 |
| 278 | + return ( |
| 279 | + (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2 |
| 280 | + if t < 0.5 |
| 281 | + else (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2 |
| 282 | + ) |
| 283 | + |
| 284 | + |
| 285 | +def ease_in_elastic(t): |
| 286 | + c4 = (2 * np.pi) / 3 |
| 287 | + if t == 0: |
| 288 | + return 0 |
| 289 | + elif t == 1: |
| 290 | + return 1 |
| 291 | + else: |
| 292 | + return -pow(2, 10 * t - 10) * np.sin((t * 10 - 10.75) * c4) |
| 293 | + |
| 294 | + |
| 295 | +def ease_out_elastic(t): |
| 296 | + c4 = (2 * np.pi) / 3 |
| 297 | + if t == 0: |
| 298 | + return 0 |
| 299 | + elif t == 1: |
| 300 | + return 1 |
| 301 | + else: |
| 302 | + return pow(2, -10 * t) * np.sin((t * 10 - 0.75) * c4) + 1 |
| 303 | + |
| 304 | + |
| 305 | +def ease_in_out_elastic(t): |
| 306 | + c5 = (2 * np.pi) / 4.5 |
| 307 | + if t == 0: |
| 308 | + return 0 |
| 309 | + elif t == 1: |
| 310 | + return 1 |
| 311 | + elif t < 0.5: |
| 312 | + return -(pow(2, 20 * t - 10) * np.sin((20 * t - 11.125) * c5)) / 2 |
| 313 | + else: |
| 314 | + return (pow(2, -20 * t + 10) * np.sin((20 * t - 11.125) * c5)) / 2 + 1 |
| 315 | + |
| 316 | + |
| 317 | +def ease_in_bounce(t): |
| 318 | + return 1 - ease_out_bounce(1 - t) |
| 319 | + |
| 320 | + |
| 321 | +def ease_out_bounce(t): |
| 322 | + n1 = 7.5625 |
| 323 | + d1 = 2.75 |
| 324 | + |
| 325 | + if t < 1 / d1: |
| 326 | + return n1 * t * t |
| 327 | + elif t < 2 / d1: |
| 328 | + return n1 * (t - 1.5 / d1) * t + 0.75 |
| 329 | + elif t < 2.5 / d1: |
| 330 | + return n1 * (t - 2.25 / d1) * t + 0.9375 |
| 331 | + else: |
| 332 | + return n1 * (t - 2.625 / d1) * t + 0.984375 |
| 333 | + |
| 334 | + |
| 335 | +def ease_in_out_bounce(t): |
| 336 | + c1 = 1.70158 |
| 337 | + c2 = c1 * 1.525 |
| 338 | + return ( |
| 339 | + (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2 |
| 340 | + if t < 0.5 |
| 341 | + else (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2 |
| 342 | + ) |
0 commit comments