Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed animations introducing or removing objects #2396

Merged
merged 11 commits into from
Jan 23, 2022
32 changes: 32 additions & 0 deletions manim/animation/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,20 @@ def __init__(
name: str = None,
remover: bool = False, # remove a mobject from the screen?
suspend_mobject_updating: bool = True,
introducer: bool = False,
*,
_on_finish: Callable[[], None] = lambda _: None,
**kwargs,
) -> None:
self._typecheck_input(mobject)
self.run_time: float = run_time
self.rate_func: Callable[[float], float] = rate_func
self.name: str | None = name
self.remover: bool = remover
self.introducer: bool = introducer
self.suspend_mobject_updating: bool = suspend_mobject_updating
self.lag_ratio: float = lag_ratio
self._on_finish: Callable[[Scene], None] = _on_finish
if config["renderer"] == "opengl":
self.starting_mobject: OpenGLMobject = OpenGLMobject()
self.mobject: OpenGLMobject = (
Expand Down Expand Up @@ -219,9 +224,26 @@ def clean_up_from_scene(self, scene: Scene) -> None:
scene
The scene the animation should be cleaned up from.
"""
self._on_finish(scene)
if self.is_remover():
scene.remove(self.mobject)

def _setup_scene(self, scene: Scene) -> None:
"""Setup up the :class:`~.Scene` before starting the animation.

This includes to :meth:`~.Scene.add` the Animation's
:class:`~.Mobject` if the animation is an introducer.

Parameters
----------
scene
The scene the animation should be cleaned up from.
"""
if scene is None:
return
if self.is_introducer():
scene.add(self.mobject)

def create_starting_mobject(self) -> Mobject:
# Keep track of where the mobject starts
return self.mobject.copy()
Expand Down Expand Up @@ -436,6 +458,16 @@ def is_remover(self) -> bool:
"""
return self.remover

def is_introducer(self) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this method only returns the value of an attribute, is it really needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's made this way to mirror the is_remover method.

"""Test if a the animation is a remover.

Returns
-------
bool
``True`` if the animation is a remover, ``False`` otherwise.
"""
return self.introducer


def prepare_animation(
anim: Animation | mobject._AnimationBuilder,
Expand Down
18 changes: 17 additions & 1 deletion manim/animation/composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(
self.group = group
if self.group is None:
mobjects = remove_list_redundancies(
[anim.mobject for anim in self.animations],
[anim.mobject for anim in self.animations if not anim.is_introducer()],
)
if config["renderer"] == "opengl":
self.group = OpenGLGroup(*mobjects)
Expand All @@ -57,13 +57,18 @@ def begin(self) -> None:
for anim in self.animations:
anim.begin()

def _setup_scene(self, scene) -> None:
for anim in self.animations:
anim._setup_scene(scene)

def finish(self) -> None:
for anim in self.animations:
anim.finish()
if self.suspend_mobject_updating:
self.group.resume_updating()

def clean_up_from_scene(self, scene: Scene) -> None:
self._on_finish(scene)
for anim in self.animations:
if self.remover:
anim.remover = self.remover
Expand Down Expand Up @@ -127,6 +132,16 @@ def update_mobjects(self, dt: float) -> None:
if self.active_animation:
self.active_animation.update_mobjects(dt)

def _setup_scene(self, scene) -> None:
if scene is None:
return
if self.is_introducer():
for anim in self.animations:
if not anim.is_introducer() and anim.mobject is not None:
scene.add(anim.mobject)

self.scene = scene

def update_active_animation(self, index: int) -> None:
self.active_index = index
if index >= len(self.animations):
Expand All @@ -135,6 +150,7 @@ def update_active_animation(self, index: int) -> None:
self.active_end_time: float | None = None
else:
self.active_animation = self.animations[index]
self.active_animation._setup_scene(self.scene)
self.active_animation.begin()
self.active_start_time = self.anims_with_timings[index][1]
self.active_end_time = self.anims_with_timings[index][2]
Expand Down
27 changes: 23 additions & 4 deletions manim/animation/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ class ShowPartial(Animation):
"""

def __init__(
self, mobject: VMobject | OpenGLVMobject | OpenGLSurface | None, **kwargs
self,
mobject: VMobject | OpenGLVMobject | OpenGLSurface | None,
**kwargs,
):
pointwise = getattr(mobject, "pointwise_become_partial", None)
if not callable(pointwise):
Expand Down Expand Up @@ -167,9 +169,10 @@ def __init__(
self,
mobject: VMobject | OpenGLVMobject | OpenGLSurface,
lag_ratio: float = 1.0,
introducer: bool = True,
**kwargs,
) -> None:
super().__init__(mobject, lag_ratio=lag_ratio, **kwargs)
super().__init__(mobject, lag_ratio=lag_ratio, introducer=introducer, **kwargs)

def _get_bounds(self, alpha: float) -> tuple[int, float]:
return (0, alpha)
Expand Down Expand Up @@ -199,7 +202,13 @@ def __init__(
remover: bool = True,
**kwargs,
) -> None:
super().__init__(mobject, rate_func=rate_func, remover=remover, **kwargs)
super().__init__(
mobject,
rate_func=rate_func,
introducer=False,
remover=remover,
**kwargs,
)


class DrawBorderThenFill(Animation):
Expand All @@ -223,10 +232,17 @@ def __init__(
stroke_color: str = None,
draw_border_animation_config: dict = {}, # what does this dict accept?
fill_animation_config: dict = {},
introducer: bool = True,
**kwargs,
) -> None:
self._typecheck_input(vmobject)
super().__init__(vmobject, run_time=run_time, rate_func=rate_func, **kwargs)
super().__init__(
vmobject,
run_time=run_time,
introducer=introducer,
rate_func=rate_func,
**kwargs,
)
self.stroke_width = stroke_width
self.stroke_color = stroke_color
self.draw_border_animation_config = draw_border_animation_config
Expand Down Expand Up @@ -308,11 +324,14 @@ def __init__(
lag_ratio,
)
self.reverse = reverse
if "remover" not in kwargs:
kwargs["remover"] = reverse
super().__init__(
vmobject,
rate_func=rate_func,
run_time=run_time,
lag_ratio=lag_ratio,
introducer=not reverse,
**kwargs,
)

Expand Down
3 changes: 3 additions & 0 deletions manim/animation/fading.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ def construct(self):

"""

def __init__(self, *mobjects: Mobject, **kwargs) -> None:
super().__init__(*mobjects, introducer=True, **kwargs)

def create_target(self):
return self.mobject

Expand Down
2 changes: 1 addition & 1 deletion manim/animation/growing.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def __init__(
) -> None:
self.point = point
self.point_color = point_color
super().__init__(mobject, **kwargs)
super().__init__(mobject, introducer=True, **kwargs)

def create_target(self) -> Mobject:
return self.mobject
Expand Down
6 changes: 3 additions & 3 deletions manim/animation/indication.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def construct(self):

def __init__(self, mobject: "VMobject", time_width: float = 0.1, **kwargs) -> None:
self.time_width = time_width
super().__init__(mobject, remover=True, **kwargs)
super().__init__(mobject, remover=True, introducer=True, **kwargs)

def _get_bounds(self, alpha: float) -> Tuple[float]:
tw = self.time_width
Expand All @@ -310,8 +310,8 @@ def _get_bounds(self, alpha: float) -> Tuple[float]:
lower = max(lower, 0)
return (lower, upper)

def finish(self) -> None:
super().finish()
def clean_up_from_scene(self, scene: "Scene") -> None:
super().clean_up_from_scene(scene)
for submob, start in self.get_all_families_zipped():
submob.pointwise_become_partial(start, 0, 1)

Expand Down
Loading