3b1b-manim/manimlib/mobject/mobject_update_utils.py

124 lines
3 KiB
Python
Raw Normal View History

from __future__ import annotations
2019-02-11 13:17:23 -08:00
import inspect
from manimlib.constants import DEG
2019-02-11 13:17:23 -08:00
from manimlib.constants import RIGHT
from manimlib.mobject.mobject import Mobject
2020-02-18 22:32:15 -08:00
from manimlib.utils.simple_functions import clip
2019-02-11 13:17:23 -08:00
from typing import TYPE_CHECKING
if TYPE_CHECKING:
2022-04-12 19:19:59 +08:00
from typing import Callable
import numpy as np
2022-04-12 19:19:59 +08:00
from manimlib.animation.animation import Animation
2019-02-11 13:17:23 -08:00
2019-02-15 15:16:11 -08:00
def assert_is_mobject_method(method):
assert inspect.ismethod(method)
mobject = method.__self__
assert isinstance(mobject, Mobject)
2019-02-15 15:16:11 -08:00
def always(method, *args, **kwargs):
assert_is_mobject_method(method)
mobject = method.__self__
func = method.__func__
mobject.add_updater(lambda m: func(m, *args, **kwargs))
2019-02-15 15:16:11 -08:00
return mobject
def f_always(method, *arg_generators, **kwargs):
"""
2019-03-30 13:22:24 -07:00
More functional version of always, where instead
2021-08-07 22:25:26 +07:00
of taking in args, it takes in functions which output
2019-02-15 15:16:11 -08:00
the relevant arguments.
"""
assert_is_mobject_method(method)
mobject = method.__self__
func = method.__func__
def updater(mob):
args = [
arg_generator()
for arg_generator in arg_generators
]
func(mob, *args, **kwargs)
mobject.add_updater(updater)
return mobject
def always_redraw(func: Callable[..., Mobject], *args, **kwargs) -> Mobject:
mob = func(*args, **kwargs)
mob.add_updater(lambda m: mob.become(func(*args, **kwargs)))
2018-10-05 17:17:55 -07:00
return mob
2019-02-11 13:17:23 -08:00
def always_shift(
mobject: Mobject,
direction: np.ndarray = RIGHT,
rate: float = 0.1
) -> Mobject:
2019-02-11 13:17:23 -08:00
mobject.add_updater(
lambda m, dt: m.shift(dt * rate * direction)
)
return mobject
2019-02-11 13:17:23 -08:00
def always_rotate(
mobject: Mobject,
rate: float = 20 * DEG,
**kwargs
) -> Mobject:
2019-02-11 13:17:23 -08:00
mobject.add_updater(
lambda m, dt: m.rotate(dt * rate, **kwargs)
)
return mobject
def turn_animation_into_updater(
animation: Animation,
cycle: bool = False,
**kwargs
) -> Mobject:
"""
Add an updater to the animation's mobject which applies
the interpolation and update functions of the animation
If cycle is True, this repeats over and over. Otherwise,
the updater will be popped uplon completion
"""
mobject = animation.mobject
animation.update_rate_info(**kwargs)
animation.suspend_mobject_updating = False
animation.begin()
animation.total_time = 0
def update(m, dt):
run_time = animation.get_run_time()
time_ratio = animation.total_time / run_time
if cycle:
alpha = time_ratio % 1
else:
2020-02-18 22:32:15 -08:00
alpha = clip(time_ratio, 0, 1)
if alpha >= 1:
animation.finish()
m.remove_updater(update)
return
animation.interpolate(alpha)
animation.update_mobjects(dt)
animation.total_time += dt
mobject.add_updater(update)
return mobject
def cycle_animation(animation: Animation, **kwargs) -> Mobject:
return turn_animation_into_updater(
animation, cycle=True, **kwargs
)