3b1b-manim/manimlib/mobject/mobject_update_utils.py
Grant Sanderson 744e695340
Misc. clean up (#2269)
* Comment tweak

* Directly print traceback

Since the shell.showtraceback is giving some issues

* Make InteracrtiveSceneEmbed into a class

This way it can keep track of it's internal shell; use of get_ipython has a finicky relationship with reloading.

* Move remaining checkpoint_paste logic into scene_embed.py

This involved making a few context managers for Scene: temp_record, temp_skip, temp_progress_bar, which seem useful in and of themselves.

* Change null key to be the empty string

* Ensure temporary svg paths for Text are deleted

* Remove unused dict_ops.py functions

* Remove break_into_partial_movies from file_writer configuration

* Rewrite guarantee_existence using Path

* Clean up SceneFileWriter

It had a number of vestigial functions no longer used, and some setup that could be made more organized.

* Remove --save_pngs CLI arg (which did nothing)

* Add --subdivide CLI arg

* Remove add_extension_if_not_present

* Remove get_sorted_integer_files

* Have find_file return Path

* Minor clean up

* Clean up num_tex_symbols

* Fix find_file

* Minor cleanup for extract_scene.py

* Add preview_frame_while_skipping option to scene config

* Use shell.showtraceback function

* Move keybindings to config, instead of in-place constants

* Replace DEGREES -> DEG
2024-12-12 08:39:54 -08:00

123 lines
3 KiB
Python

from __future__ import annotations
import inspect
from manimlib.constants import DEG
from manimlib.constants import RIGHT
from manimlib.mobject.mobject import Mobject
from manimlib.utils.simple_functions import clip
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Callable
import numpy as np
from manimlib.animation.animation import Animation
def assert_is_mobject_method(method):
assert inspect.ismethod(method)
mobject = method.__self__
assert isinstance(mobject, Mobject)
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))
return mobject
def f_always(method, *arg_generators, **kwargs):
"""
More functional version of always, where instead
of taking in args, it takes in functions which output
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)))
return mob
def always_shift(
mobject: Mobject,
direction: np.ndarray = RIGHT,
rate: float = 0.1
) -> Mobject:
mobject.add_updater(
lambda m, dt: m.shift(dt * rate * direction)
)
return mobject
def always_rotate(
mobject: Mobject,
rate: float = 20 * DEG,
**kwargs
) -> Mobject:
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:
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
)