123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- from __future__ import annotations
- from abc import ABC, abstractmethod
- import numpy as np
- from manimlib.animation.animation import Animation
- from manimlib.constants import WHITE
- from manimlib.mobject.svg.string_mobject import StringMobject
- from manimlib.mobject.types.vectorized_mobject import VMobject
- from manimlib.utils.bezier import integer_interpolate
- from manimlib.utils.rate_functions import linear
- from manimlib.utils.rate_functions import double_smooth
- from manimlib.utils.rate_functions import smooth
- from manimlib.utils.simple_functions import clip
- from typing import TYPE_CHECKING
- if TYPE_CHECKING:
- from typing import Callable
- from manimlib.mobject.mobject import Mobject
- from manimlib.scene.scene import Scene
- from manimlib.typing import ManimColor
- class ShowPartial(Animation, ABC):
- """
- Abstract class for ShowCreation and ShowPassingFlash
- """
- def __init__(self, mobject: Mobject, should_match_start: bool = False, **kwargs):
- self.should_match_start = should_match_start
- super().__init__(mobject, **kwargs)
- def interpolate_submobject(
- self,
- submob: VMobject,
- start_submob: VMobject,
- alpha: float
- ) -> None:
- submob.pointwise_become_partial(
- start_submob, *self.get_bounds(alpha)
- )
- @abstractmethod
- def get_bounds(self, alpha: float) -> tuple[float, float]:
- raise Exception("Not Implemented")
- class ShowCreation(ShowPartial):
- def __init__(self, mobject: Mobject, lag_ratio: float = 1.0, **kwargs):
- super().__init__(mobject, lag_ratio=lag_ratio, **kwargs)
- def get_bounds(self, alpha: float) -> tuple[float, float]:
- return (0, alpha)
- class Uncreate(ShowCreation):
- def __init__(
- self,
- mobject: Mobject,
- rate_func: Callable[[float], float] = lambda t: smooth(1 - t),
- remover: bool = True,
- should_match_start: bool = True,
- **kwargs,
- ):
- super().__init__(
- mobject,
- rate_func=rate_func,
- remover=remover,
- should_match_start=should_match_start,
- **kwargs,
- )
- class DrawBorderThenFill(Animation):
- def __init__(
- self,
- vmobject: VMobject,
- run_time: float = 2.0,
- rate_func: Callable[[float], float] = double_smooth,
- stroke_width: float = 2.0,
- stroke_color: ManimColor = None,
- draw_border_animation_config: dict = {},
- fill_animation_config: dict = {},
- **kwargs
- ):
- assert isinstance(vmobject, VMobject)
- self.sm_to_index = {hash(sm): 0 for sm in vmobject.get_family()}
- self.stroke_width = stroke_width
- self.stroke_color = stroke_color
- self.draw_border_animation_config = draw_border_animation_config
- self.fill_animation_config = fill_animation_config
- super().__init__(
- vmobject,
- run_time=run_time,
- rate_func=rate_func,
- **kwargs
- )
- self.mobject = vmobject
- def begin(self) -> None:
- self.mobject.set_animating_status(True)
- self.outline = self.get_outline()
- super().begin()
- self.mobject.match_style(self.outline)
- def finish(self) -> None:
- super().finish()
- self.mobject.refresh_joint_angles()
- def get_outline(self) -> VMobject:
- outline = self.mobject.copy()
- outline.set_fill(opacity=0)
- for sm in outline.family_members_with_points():
- sm.set_stroke(
- color=self.stroke_color or sm.get_stroke_color(),
- width=self.stroke_width,
- behind=self.mobject.stroke_behind,
- )
- return outline
- def get_all_mobjects(self) -> list[Mobject]:
- return [*super().get_all_mobjects(), self.outline]
- def interpolate_submobject(
- self,
- submob: VMobject,
- start: VMobject,
- outline: VMobject,
- alpha: float
- ) -> None:
- index, subalpha = integer_interpolate(0, 2, alpha)
- if index == 1 and self.sm_to_index[hash(submob)] == 0:
- # First time crossing over
- submob.set_data(outline.data)
- self.sm_to_index[hash(submob)] = 1
- if index == 0:
- submob.pointwise_become_partial(outline, 0, subalpha)
- else:
- submob.interpolate(outline, start, subalpha)
- class Write(DrawBorderThenFill):
- def __init__(
- self,
- vmobject: VMobject,
- run_time: float = -1, # If negative, this will be reassigned
- lag_ratio: float = -1, # If negative, this will be reassigned
- rate_func: Callable[[float], float] = linear,
- stroke_color: ManimColor = None,
- **kwargs
- ):
- if stroke_color is None:
- stroke_color = vmobject.get_color()
- family_size = len(vmobject.family_members_with_points())
- super().__init__(
- vmobject,
- run_time=self.compute_run_time(family_size, run_time),
- lag_ratio=self.compute_lag_ratio(family_size, lag_ratio),
- rate_func=rate_func,
- stroke_color=stroke_color,
- **kwargs
- )
- def compute_run_time(self, family_size: int, run_time: float):
- if run_time < 0:
- return 1 if family_size < 15 else 2
- return run_time
- def compute_lag_ratio(self, family_size: int, lag_ratio: float):
- if lag_ratio < 0:
- return min(4.0 / (family_size + 1.0), 0.2)
- return lag_ratio
- class ShowIncreasingSubsets(Animation):
- def __init__(
- self,
- group: Mobject,
- int_func: Callable[[float], float] = np.round,
- suspend_mobject_updating: bool = False,
- **kwargs
- ):
- self.all_submobs = list(group.submobjects)
- self.int_func = int_func
- super().__init__(
- group,
- suspend_mobject_updating=suspend_mobject_updating,
- **kwargs
- )
- def interpolate_mobject(self, alpha: float) -> None:
- n_submobs = len(self.all_submobs)
- alpha = self.rate_func(alpha)
- index = int(self.int_func(alpha * n_submobs))
- self.update_submobject_list(index)
- def update_submobject_list(self, index: int) -> None:
- self.mobject.set_submobjects(self.all_submobs[:index])
- class ShowSubmobjectsOneByOne(ShowIncreasingSubsets):
- def __init__(
- self,
- group: Mobject,
- int_func: Callable[[float], float] = np.ceil,
- **kwargs
- ):
- super().__init__(group, int_func=int_func, **kwargs)
- def update_submobject_list(self, index: int) -> None:
- index = int(clip(index, 0, len(self.all_submobs) - 1))
- if index == 0:
- self.mobject.set_submobjects([])
- else:
- self.mobject.set_submobjects([self.all_submobs[index - 1]])
- class AddTextWordByWord(ShowIncreasingSubsets):
- def __init__(
- self,
- string_mobject: StringMobject,
- time_per_word: float = 0.2,
- run_time: float = -1.0, # If negative, it will be recomputed with time_per_word
- rate_func: Callable[[float], float] = linear,
- **kwargs
- ):
- assert isinstance(string_mobject, StringMobject)
- grouped_mobject = string_mobject.build_groups()
- if run_time < 0:
- run_time = time_per_word * len(grouped_mobject)
- super().__init__(
- grouped_mobject,
- run_time=run_time,
- rate_func=rate_func,
- **kwargs
- )
- self.string_mobject = string_mobject
- def clean_up_from_scene(self, scene: Scene) -> None:
- scene.remove(self.mobject)
- if not self.is_remover():
- scene.add(self.string_mobject)
|