diff --git a/manim/animation/creation.py b/manim/animation/creation.py index 3a2709ad23..42ce65babd 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -62,6 +62,7 @@ def construct(self): "Uncreate", "DrawBorderThenFill", "Write", + "Unwrite", "ShowIncreasingSubsets", "AddTextLetterByLetter", "ShowSubmobjectsOneByOne", @@ -177,7 +178,7 @@ def __init__( 1 - t ), remover: bool = True, - **kwargs + **kwargs, ) -> None: super().__init__(mobject, rate_func=rate_func, remover=remover, **kwargs) @@ -203,7 +204,7 @@ def __init__( stroke_color: str = None, draw_border_animation_config: typing.Dict = {}, # what does this dict accept? fill_animation_config: typing.Dict = {}, - **kwargs + **kwargs, ) -> None: self._typecheck_input(vmobject) super().__init__(vmobject, run_time=run_time, rate_func=rate_func, **kwargs) @@ -267,7 +268,7 @@ def __init__( run_time: float = None, lag_ratio: float = None, rate_func: typing.Callable[[float], np.ndarray] = linear, - **kwargs + **kwargs, ) -> None: self.run_time = run_time self.lag_ratio = lag_ratio @@ -291,6 +292,74 @@ def _set_default_config_from_length(self, vmobject: VMobject) -> None: self.lag_ratio = min(4.0 / length, 0.2) +class Unwrite(Write): + """Simulate erasing by hand a :class:`~.Text` or a :class:`~.VMobject`. + + Parameters + ---------- + reverse : :class:`bool` + Set True to have the animation start erasing from the last submobject first. + + Examples + -------- + + .. manim:: UnwriteReverseFalse + + class UnwriteReverseFalse(Scene): + def construct(self): + text = Tex("Alice and Bob").scale(3) + self.add(text) + self.play(Unwrite(text)) + + .. manim :: UnwriteReverseTrue + + class UnwriteReverseTrue(Scene): + def construct(self): + text = Tex("Alice and Bob").scale(3) + self.add(text) + self.play(Unwrite(text,reverse=True)) + + """ + + def __init__( + self, + vmobject: VMobject, + run_time: float = None, + lag_ratio: float = None, + rate_func: typing.Callable[[float], np.ndarray] = linear, + reverse: bool = False, + **kwargs, + ) -> None: + + backwards_rate_func = lambda t: -rate_func(t) + 1 + + self.vmobject = vmobject + self.run_time = run_time + self.lag_ratio = lag_ratio + self.reverse = reverse + self._set_default_config_from_length(vmobject) + super().__init__( + vmobject, + run_time=run_time, + lag_ratio=lag_ratio, + rate_func=backwards_rate_func, + **kwargs, + ) + + def begin(self) -> None: + if not self.reverse: + self.reverse_submobjects() + super().begin() + + def finish(self) -> None: + if not self.reverse: + self.reverse_submobjects() + super().finish() + + def reverse_submobjects(self) -> None: + self.vmobject.invert(recursive=True) + + class ShowIncreasingSubsets(Animation): """Show one submobject at a time, leaving all previous ones displayed on screen. @@ -312,7 +381,7 @@ def __init__( group: Mobject, suspend_mobject_updating: bool = False, int_func: typing.Callable[[np.ndarray], np.ndarray] = np.floor, - **kwargs + **kwargs, ) -> None: self.all_submobs = list(group.submobjects) self.int_func = int_func @@ -351,7 +420,7 @@ def __init__( rate_func: typing.Callable[[float], float] = linear, time_per_char: float = 0.1, run_time: typing.Optional[float] = None, - **kwargs + **kwargs, ) -> None: # time_per_char must be above 0.06, or the animation won't finish self.time_per_char = time_per_char @@ -376,7 +445,7 @@ def __init__( self, group: typing.Iterable[Mobject], int_func: typing.Callable[[np.ndarray], np.ndarray] = np.ceil, - **kwargs + **kwargs, ) -> None: new_group = Group(*group) super().__init__(new_group, int_func=int_func, **kwargs) @@ -397,7 +466,7 @@ def __init__( text_mobject: "Text", run_time: float = None, time_per_char: float = 0.06, - **kwargs + **kwargs, ) -> None: self.time_per_char = time_per_char tpc = self.time_per_char diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index ff621d1de1..fb54d6ae86 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -1569,6 +1569,12 @@ def shuffle(self, recursive=False): submob.shuffle(recursive=True) random.shuffle(self.submobjects) + def invert(self, recursive=False): + if recursive: + for submob in self.submobjects: + submob.invert(recursive=True) + list.reverse(self.submobjects) + # Just here to keep from breaking old scenes. def arrange_submobjects(self, *args, **kwargs): return self.arrange(*args, **kwargs)