mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00

* 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 * Add arg to clear the cache * Separate out full_tex_to_svg from tex_to_svg And only cache to disk the results of full_tex_to_svg. Otherwise, making edits to the tex_templates would not show up without clearing the cache. * Bug fix in handling BlankScene * Make checkpoint_states an instance variable of CheckpointManager As per https://github.com/3b1b/manim/issues/2272 * Move resizing out of Window.focus, and into Window.init_for_scene * Make default output directory "." instead of "" To address https://github.com/3b1b/manim/issues/2261 * Remove input_file_path arg from SceneFileWriter * Use Dict syntax in place of dict for config more consistently across config.py * Simplify get_output_directory * Swap order of preamble and additional preamble * Minor stylistic tweak * Have UnitInterval pass on kwargs to NumberLine * Add simple get_dist function * Have TracedPath always update to the stroke configuration passed in * Have Mobject.match_points apply to all parts of data in pointlike_data_key * Always call Mobject.update upon adding an updater * Add Surface.uv_to_point * Make sure Surface.set_opacity takes in a recurse option * Update num_tex_symbols to account for \{ and \}
165 lines
5.3 KiB
Python
165 lines
5.3 KiB
Python
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from manimlib.constants import BLUE_B, BLUE_D, BLUE_E, GREY_BROWN, WHITE
|
|
from manimlib.mobject.mobject import Mobject
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
|
from manimlib.utils.rate_functions import smooth
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from typing import Callable, List, Iterable
|
|
from manimlib.typing import ManimColor, Vect3, Self
|
|
|
|
|
|
class AnimatedBoundary(VGroup):
|
|
def __init__(
|
|
self,
|
|
vmobject: VMobject,
|
|
colors: List[ManimColor] = [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
|
max_stroke_width: float = 3.0,
|
|
cycle_rate: float = 0.5,
|
|
back_and_forth: bool = True,
|
|
draw_rate_func: Callable[[float], float] = smooth,
|
|
fade_rate_func: Callable[[float], float] = smooth,
|
|
**kwargs
|
|
):
|
|
super().__init__(**kwargs)
|
|
self.vmobject: VMobject = vmobject
|
|
self.colors = colors
|
|
self.max_stroke_width = max_stroke_width
|
|
self.cycle_rate = cycle_rate
|
|
self.back_and_forth = back_and_forth
|
|
self.draw_rate_func = draw_rate_func
|
|
self.fade_rate_func = fade_rate_func
|
|
|
|
self.boundary_copies: list[VMobject] = [
|
|
vmobject.copy().set_style(
|
|
stroke_width=0,
|
|
fill_opacity=0
|
|
)
|
|
for x in range(2)
|
|
]
|
|
self.add(*self.boundary_copies)
|
|
self.total_time: float = 0
|
|
self.add_updater(
|
|
lambda m, dt: self.update_boundary_copies(dt)
|
|
)
|
|
|
|
def update_boundary_copies(self, dt: float) -> Self:
|
|
# Not actual time, but something which passes at
|
|
# an altered rate to make the implementation below
|
|
# cleaner
|
|
time = self.total_time * self.cycle_rate
|
|
growing, fading = self.boundary_copies
|
|
colors = self.colors
|
|
msw = self.max_stroke_width
|
|
vmobject = self.vmobject
|
|
|
|
index = int(time % len(colors))
|
|
alpha = time % 1
|
|
draw_alpha = self.draw_rate_func(alpha)
|
|
fade_alpha = self.fade_rate_func(alpha)
|
|
|
|
if self.back_and_forth and int(time) % 2 == 1:
|
|
bounds = (1 - draw_alpha, 1)
|
|
else:
|
|
bounds = (0, draw_alpha)
|
|
self.full_family_become_partial(growing, vmobject, *bounds)
|
|
growing.set_stroke(colors[index], width=msw)
|
|
|
|
if time >= 1:
|
|
self.full_family_become_partial(fading, vmobject, 0, 1)
|
|
fading.set_stroke(
|
|
color=colors[index - 1],
|
|
width=(1 - fade_alpha) * msw
|
|
)
|
|
|
|
self.total_time += dt
|
|
return self
|
|
|
|
def full_family_become_partial(
|
|
self,
|
|
mob1: VMobject,
|
|
mob2: VMobject,
|
|
a: float,
|
|
b: float
|
|
) -> Self:
|
|
family1 = mob1.family_members_with_points()
|
|
family2 = mob2.family_members_with_points()
|
|
for sm1, sm2 in zip(family1, family2):
|
|
sm1.pointwise_become_partial(sm2, a, b)
|
|
return self
|
|
|
|
|
|
class TracedPath(VMobject):
|
|
def __init__(
|
|
self,
|
|
traced_point_func: Callable[[], Vect3],
|
|
time_traced: float = np.inf,
|
|
time_per_anchor: float = 1.0 / 15,
|
|
stroke_width: float | Iterable[float] = 2.0,
|
|
stroke_color: ManimColor = WHITE,
|
|
**kwargs
|
|
):
|
|
super().__init__(**kwargs)
|
|
self.traced_point_func = traced_point_func
|
|
self.time_traced = time_traced
|
|
self.time_per_anchor = time_per_anchor
|
|
self.time: float = 0
|
|
self.traced_points: list[np.ndarray] = []
|
|
self.add_updater(lambda m, dt: m.update_path(dt))
|
|
self.always.set_stroke(stroke_color, stroke_width)
|
|
|
|
def update_path(self, dt: float) -> Self:
|
|
if dt == 0:
|
|
return self
|
|
point = self.traced_point_func().copy()
|
|
self.traced_points.append(point)
|
|
|
|
if self.time_traced < np.inf:
|
|
n_relevant_points = int(self.time_traced / dt + 0.5)
|
|
n_tps = len(self.traced_points)
|
|
if n_tps < n_relevant_points:
|
|
points = self.traced_points + [point] * (n_relevant_points - n_tps)
|
|
else:
|
|
points = self.traced_points[n_tps - n_relevant_points:]
|
|
# Every now and then refresh the list
|
|
if n_tps > 10 * n_relevant_points:
|
|
self.traced_points = self.traced_points[-n_relevant_points:]
|
|
else:
|
|
points = self.traced_points
|
|
|
|
if points:
|
|
self.set_points_smoothly(points)
|
|
|
|
self.time += dt
|
|
return self
|
|
|
|
|
|
class TracingTail(TracedPath):
|
|
def __init__(
|
|
self,
|
|
mobject_or_func: Mobject | Callable[[], np.ndarray],
|
|
time_traced: float = 1.0,
|
|
stroke_width: float | Iterable[float] = (0, 3),
|
|
stroke_opacity: float | Iterable[float] = (0, 1),
|
|
stroke_color: ManimColor = WHITE,
|
|
**kwargs
|
|
):
|
|
if isinstance(mobject_or_func, Mobject):
|
|
func = mobject_or_func.get_center
|
|
else:
|
|
func = mobject_or_func
|
|
super().__init__(
|
|
func,
|
|
time_traced=time_traced,
|
|
stroke_width=stroke_width,
|
|
stroke_opacity=stroke_opacity,
|
|
stroke_color=stroke_color,
|
|
**kwargs
|
|
)
|
|
self.add_updater(lambda m: m.set_stroke(width=stroke_width, opacity=stroke_opacity))
|