Skip to content

Commit

Permalink
Bugfix: allow new-style method animation to be used in animation grou…
Browse files Browse the repository at this point in the history
…ps (#906)

* make sure animations from _AnimationBuilder are built before adding to AnimationGroup

* add test for _AnimationBuilder in group

* black

* delegate building of animation to prepare_animation

* black

* remove unused import

* rewrite loop as list comprehension

Co-authored-by: friedkeenan <friedkeenan@protonmail.com>

Co-authored-by: friedkeenan <friedkeenan@protonmail.com>
  • Loading branch information
behackl and friedkeenan committed Jan 9, 2021
1 parent 2ed00b6 commit c25b89c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 21 deletions.
46 changes: 43 additions & 3 deletions manim/animation/animation.py
Expand Up @@ -5,6 +5,7 @@


import typing
from typing import Union
from copy import deepcopy

import numpy as np
Expand All @@ -13,7 +14,7 @@
from manim.scene.scene import Scene

from .. import logger
from ..mobject.mobject import Mobject
from ..mobject.mobject import Mobject, _AnimationBuilder
from ..utils.rate_functions import smooth

DEFAULT_ANIMATION_RUN_TIME: float = 1.0
Expand All @@ -35,7 +36,7 @@ def __init__(
name: str = None,
remover: bool = False, # remove a mobject from the screen?
suspend_mobject_updating: bool = True,
**kwargs
**kwargs,
) -> None:
self._typecheck_input(mobject)
self.run_time = run_time
Expand Down Expand Up @@ -66,7 +67,10 @@ def _typecheck_input(self, mobject: Mobject) -> None:
def __str__(self) -> str:
if self.name:
return self.name
return self.__class__.__name__ + str(self.mobject)
return f"{self.__class__.__name__}({str(self.mobject)})"

def __repr__(self) -> str:
return str(self)

def begin(self) -> None:
# This is called right as an animation is being
Expand Down Expand Up @@ -191,6 +195,42 @@ def is_remover(self) -> bool:
return self.remover


def prepare_animation(anim: Union["Animation", "_AnimationBuilder"]) -> "Animation":
r"""Returns either an unchanged animation, or the animation built
from a passed animation factory.
Examples
--------
::
>>> from manim import Square, FadeIn
>>> s = Square()
>>> prepare_animation(FadeIn(s))
FadeIn(Square)
::
>>> prepare_animation(s.animate.scale(2).rotate(42))
_MethodAnimation(Square)
::
>>> prepare_animation(42)
Traceback (most recent call last):
...
TypeError: Object 42 cannot be converted to an animation
"""
if isinstance(anim, _AnimationBuilder):
return anim.build()

if isinstance(anim, Animation):
return anim

raise TypeError(f"Object {anim} cannot be converted to an animation")


class Wait(Animation):
def __init__(
self, duration: float = 1, stop_condition=None, **kwargs
Expand Down
4 changes: 2 additions & 2 deletions manim/animation/composition.py
Expand Up @@ -3,7 +3,7 @@

import numpy as np

from ..animation.animation import Animation
from ..animation.animation import Animation, prepare_animation
from ..mobject.mobject import Group, Mobject
from ..scene.scene import Scene
from ..utils.bezier import interpolate
Expand All @@ -29,7 +29,7 @@ def __init__(
lag_ratio: float = 0,
**kwargs
) -> None:
self.animations = animations
self.animations = [prepare_animation(anim) for anim in animations]
self.group = group
if self.group is None:
self.group = Group(
Expand Down
25 changes: 13 additions & 12 deletions manim/scene/scene.py
Expand Up @@ -16,7 +16,7 @@
import numpy as np

from .. import config, logger
from ..animation.animation import Animation, Wait
from ..animation.animation import Animation, Wait, prepare_animation
from ..animation.transform import MoveToTarget, _MethodAnimation
from ..camera.camera import Camera
from ..constants import *
Expand Down Expand Up @@ -618,17 +618,18 @@ def compile_animations(self, *args, **kwargs):
"""
animations = []
for arg in args:
if isinstance(arg, _AnimationBuilder):
animations.append(arg.build())
elif isinstance(arg, Animation):
animations.append(arg)
elif inspect.ismethod(arg):
raise TypeError(
"Passing Mobject methods to Scene.play is no longer supported. Use "
"Mobject.animate instead."
)
else:
raise TypeError(f"Unexpected argument {arg} passed to Scene.play().")
try:
animations.append(prepare_animation(arg))
except TypeError:
if inspect.ismethod(arg):
raise TypeError(
"Passing Mobject methods to Scene.play is no longer"
" supported. Use Mobject.animate instead."
)
else:
raise TypeError(
f"Unexpected argument {arg} passed to Scene.play()."
)

for animation in animations:
for k, v in kwargs.items():
Expand Down
17 changes: 13 additions & 4 deletions tests/test_composition.py
@@ -1,8 +1,8 @@
import pytest
from manim.animation.composition import Succession
from manim.animation.fading import FadeInFrom, FadeOutAndShift
from manim.animation.animation import Animation
from manim.animation.composition import AnimationGroup, Succession
from manim.animation.fading import FadeIn, FadeInFrom, FadeOutAndShift
from manim.constants import DOWN
from manim.mobject.geometry import Line
from manim.mobject.geometry import Line, Square, Circle


def test_succession_timing():
Expand Down Expand Up @@ -76,3 +76,12 @@ def test_succession_in_succession_timing():
assert succession.active_animation is None
assert nested_succession.active_index == 2
assert nested_succession.active_animation is None


def test_animationbuilder_in_group():
sqr = Square()
circ = Circle()
animation_group = AnimationGroup(sqr.animate.shift(DOWN).scale(2), FadeIn(circ))
assert all(isinstance(anim, Animation) for anim in animation_group.animations)
succession = Succession(sqr.animate.shift(DOWN).scale(2), FadeIn(circ))
assert all(isinstance(anim, Animation) for anim in succession.animations)

0 comments on commit c25b89c

Please sign in to comment.