Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
802d36c
Type hints in animations and rate_functions
Apr 1, 2021
689eb8d
Type Signatures on most rate functions
Apr 3, 2021
b9bf896
A rate_function should just be float -> float
Apr 3, 2021
e2226d9
Merge branch 'master' into dev
cobordism Apr 3, 2021
e65444a
Merge branch 'master' of github.com:ManimCommunity/manim into dev
Apr 4, 2021
68a32f5
black?
Apr 4, 2021
2bedb01
`self.mobject` is of type `Union[mobject, None]`
Apr 4, 2021
b6f1a1b
Typing Corrections in composition.py
Apr 4, 2021
9e8f2e3
chasing down more type errors for transformations
Apr 4, 2021
2ada98e
mobject.copy needs better typing
Apr 4, 2021
a18f695
mobject.copy preserves derived type
Apr 4, 2021
9699d8b
Every Animation has a mobject, even Wait
Apr 6, 2021
451d603
Typing errors in creation.py
Apr 6, 2021
eddf942
Typing errors in creation.py
Apr 6, 2021
7a16d44
quick fix Wait to work with GL test
Apr 6, 2021
98e183e
isort
Apr 6, 2021
bc54b95
Merge branch 'master' into dev
Apr 6, 2021
45155f8
Merge branch 'master' into dev
cobordism Apr 6, 2021
72c3033
import Callable from Typying to make test for python <3.9 work again
Apr 7, 2021
838dbb2
Merge branch 'dev' of github.com:cobordism/manim into dev
Apr 7, 2021
a20c81d
import Callable from Typying to make test for python <3.9 work again
Apr 7, 2021
6f8ac0d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 7, 2021
7f6ce48
to make flake8 happy
Apr 7, 2021
c494180
Merge branch 'dev' of github.com:cobordism/manim into dev
Apr 7, 2021
ac52947
rate functions take float to float
Apr 7, 2021
7cdfee6
re-added begin to DrawBorderTheenFill
Apr 7, 2021
7c37ea4
Merge branch 'master' into dev
Apr 7, 2021
d3640d2
Merge branch 'master' into dev
cobordism Apr 7, 2021
27b78b6
Merge branch 'master' into dev
cobordism Apr 8, 2021
2700ea5
Merge branch 'master' into dev
cobordism Apr 10, 2021
c02f25a
Merge branch 'master' into dev
cobordism Apr 14, 2021
4515e41
Merge branch 'master' into dev
cobordism Apr 15, 2021
be6832e
Merge branch 'master' into dev
jsonvillanueva Apr 15, 2021
90eef30
Merge branch 'master' into dev
cobordism Apr 15, 2021
f1d28ee
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 15, 2021
5e48caf
Merge branch 'master' into dev
kolibril13 Apr 16, 2021
98579c9
Merge branch 'master' into dev
kolibril13 Apr 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 37 additions & 35 deletions manim/animation/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@
__all__ = ["Animation", "Wait"]


import typing
from copy import deepcopy
from typing import Union
from typing import TYPE_CHECKING, Callable, Iterable, Optional, Tuple, Union

import numpy as np

if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from manim.scene.scene import Scene

from .. import logger
Expand All @@ -26,29 +23,29 @@
class Animation:
def __init__(
self,
mobject: Mobject,
mobject: Union[Mobject, None],
Copy link
Member

Choose a reason for hiding this comment

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

From https://docs.python.org/3/library/typing.html#typing.Union:

You can use Optional[X] as a shorthand for Union[X, None].

# If lag_ratio is 0, the animation is applied to all submobjects
# at the same time
# If 1, it is applied to each successively.
# If 0 < lag_ratio < 1, its applied to each
# with lagged start times
lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO,
run_time: float = DEFAULT_ANIMATION_RUN_TIME,
rate_func: typing.Callable[[float, float], np.ndarray] = smooth,
rate_func: Callable[[float], float] = smooth,
name: str = None,
remover: bool = False, # remove a mobject from the screen?
suspend_mobject_updating: bool = True,
**kwargs,
) -> None:
self._typecheck_input(mobject)
self.run_time = run_time
self.rate_func = rate_func
self.name = name
self.remover = remover
self.suspend_mobject_updating = suspend_mobject_updating
self.lag_ratio = lag_ratio
self.starting_mobject = None
self.mobject = mobject
self.run_time: float = run_time
self.rate_func: Callable[[float], float] = rate_func
self.name: Optional[str] = name
self.remover: bool = remover
self.suspend_mobject_updating: bool = suspend_mobject_updating
self.lag_ratio: float = lag_ratio
self.starting_mobject: Mobject = Mobject()
self.mobject: Mobject = mobject if mobject is not None else Mobject()
if kwargs:
logger.debug("Animation received extra kwargs: %s", kwargs)

Expand All @@ -60,9 +57,9 @@ def __init__(
)
)

def _typecheck_input(self, mobject: Mobject) -> None:
def _typecheck_input(self, mobject: Union[Mobject, None]) -> None:
if mobject is None:
logger.debug("creating dummy animation")
logger.debug("Animation with empty mobject")
elif not isinstance(mobject, Mobject) and not isinstance(
mobject, OpenGLMobject
):
Expand Down Expand Up @@ -94,7 +91,7 @@ def begin(self) -> None:

def finish(self) -> None:
self.interpolate(1)
if self.suspend_mobject_updating:
if self.suspend_mobject_updating and self.mobject is not None:
self.mobject.resume_updating()

def clean_up_from_scene(self, scene: "Scene") -> None:
Expand All @@ -105,13 +102,13 @@ def create_starting_mobject(self) -> Mobject:
# Keep track of where the mobject starts
return self.mobject.copy()

def get_all_mobjects(self) -> typing.Tuple[Mobject, typing.Union[Mobject, None]]:
def get_all_mobjects(self) -> Tuple[Mobject, Mobject]:
"""
Ordering must match the ordering of arguments to interpolate_submobject
"""
return self.mobject, self.starting_mobject

def get_all_families_zipped(self) -> typing.Iterator[typing.Tuple]:
def get_all_families_zipped(self) -> Iterable[Tuple]:
return zip(
*[mob.family_members_with_points() for mob in self.get_all_mobjects()]
)
Expand All @@ -138,7 +135,7 @@ def copy(self) -> "Animation":

# Methods for interpolation, the mean of an Animation
def interpolate(self, alpha: float) -> None:
alpha = np.clip(alpha, 0, 1)
alpha = min(max(alpha, 0), 1)
self.interpolate_mobject(self.rate_func(alpha))

def update(self, alpha: float) -> None:
Expand All @@ -159,20 +156,24 @@ def interpolate_mobject(self, alpha: float) -> None:
self.interpolate_submobject(*mobs, sub_alpha)

def interpolate_submobject(
self, submobject: Mobject, starting_submobject: Mobject, alpha: float
) -> None:
self,
submobject: Mobject,
starting_submobject: Mobject,
# target_copy: Mobject, #Todo: fix - signature of interpolate_submobject differes in Transform().
alpha: float,
) -> "Animation":
# Typically implemented by subclass
pass

def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int):
def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int) -> float:
# TODO, make this more understandable, and/or combine
# its functionality with AnimationGroup's method
# build_animations_with_timings
lag_ratio = self.lag_ratio
full_length = (num_submobjects - 1) * lag_ratio + 1
value = alpha * full_length
lower = index * lag_ratio
return np.clip((value - lower), 0, 1)
return min(max((value - lower), 0), 1)

# Getters and setters
def set_run_time(self, run_time: float) -> "Animation":
Expand All @@ -183,12 +184,15 @@ def get_run_time(self) -> float:
return self.run_time

def set_rate_func(
self, rate_func: typing.Callable[[float, float], np.ndarray]
self,
rate_func: Callable[[float], float],
) -> "Animation":
self.rate_func = rate_func
return self

def get_rate_func(self) -> typing.Callable[[float, float], np.ndarray]:
def get_rate_func(
self,
) -> Callable[[float], float]:
return self.rate_func

def set_name(self, name: str) -> "Animation":
Expand All @@ -198,9 +202,6 @@ def set_name(self, name: str) -> "Animation":
def is_remover(self) -> bool:
return self.remover

def is_dummy(self) -> bool:
return self.mobject is None
Copy link
Member Author

Choose a reason for hiding this comment

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

This is really not needed imo.



def prepare_animation(
anim: Union["Animation", "mobject._AnimationBuilder"]
Expand Down Expand Up @@ -245,13 +246,14 @@ def prepare_animation(

class Wait(Animation):
def __init__(
self, duration: float = 1, stop_condition=None, **kwargs
self, run_time: float = 1, stop_condition=None, **kwargs
): # what is stop_condition?
self.duration = duration
self.mobject = None
self.duration: float = run_time
self.stop_condition = stop_condition
self.is_static_wait = False
super().__init__(None, **kwargs)
self.is_static_wait: bool = False
super().__init__(None, run_time=run_time, **kwargs)
# quick fix to work in opengl setting:
self.mobject.shader_wrapper_list = []

def begin(self) -> None:
pass
Expand Down
56 changes: 25 additions & 31 deletions manim/animation/composition.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tools for displaying multiple animations at once."""
import typing


from typing import TYPE_CHECKING, Callable, Optional, Union

import numpy as np

Expand All @@ -8,11 +10,10 @@
from ..mobject.mobject import Group, Mobject
from ..mobject.opengl_mobject import OpenGLGroup
from ..scene.scene import Scene
from ..utils.bezier import interpolate
from ..utils.iterables import remove_list_redundancies
from ..utils.rate_functions import linear

if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from ..mobject.types.opengl_vectorized_mobject import OpenGLVGroup
from ..mobject.types.vectorized_mobject import VGroup

Expand All @@ -26,34 +27,28 @@ class AnimationGroup(Animation):
def __init__(
self,
*animations: Animation,
group: typing.Union[Group, "VGroup", OpenGLGroup, "OpenGLVGroup"] = None,
run_time: float = None,
rate_func: typing.Callable[[float], float] = linear,
group: Union[Group, "VGroup", OpenGLGroup, "OpenGLVGroup"] = None,
run_time: Optional[float] = None,
rate_func: Callable[[float], float] = linear,
lag_ratio: float = 0,
**kwargs
) -> None:
self.animations = [prepare_animation(anim) for anim in animations]
self.group = group
if self.group is None:
mobjects = remove_list_redundancies(
[anim.mobject for anim in self.animations if not anim.is_dummy()]
[anim.mobject for anim in self.animations]
)
if config["renderer"] == "opengl":
self.group = OpenGLGroup(*mobjects)
else:
self.group = Group(*mobjects)
super().__init__(self.group, rate_func=rate_func, lag_ratio=lag_ratio, **kwargs)
self.run_time = run_time
self.init_run_time()
self.run_time: float = self.init_run_time(run_time)

def get_all_mobjects(self) -> Group:
return self.group

def get_run_time(self) -> float:
if super().get_run_time() is None:
self.init_run_time()
return super().get_run_time()

def begin(self) -> None:
for anim in self.animations:
anim.begin()
Expand All @@ -70,28 +65,27 @@ def update_mobjects(self, dt: float) -> None:
for anim in self.animations:
anim.update_mobjects(dt)

def init_run_time(self) -> None:
def init_run_time(self, run_time) -> float:
self.build_animations_with_timings()
if self.anims_with_timings:
self.max_end_time = np.max([awt[2] for awt in self.anims_with_timings])
else:
self.max_end_time = 0
self.run_time = self.max_end_time if self.run_time is None else self.run_time
return self.max_end_time if run_time is None else run_time

def build_animations_with_timings(self) -> None:
"""
Creates a list of triplets of the form
(anim, start_time, end_time)
"""
self.anims_with_timings = []
curr_time = 0
curr_time: float = 0
for anim in self.animations:
start_time = curr_time
end_time = start_time + anim.get_run_time()
start_time: float = curr_time
end_time: float = start_time + anim.get_run_time()
self.anims_with_timings.append((anim, start_time, end_time))
# Start time of next animation is based on
# the lag_ratio
curr_time = interpolate(start_time, end_time, self.lag_ratio)
# Start time of next animation is based on the lag_ratio
curr_time = (1 - self.lag_ratio) * start_time + self.lag_ratio * end_time

def interpolate(self, alpha: float) -> None:
# Note, if the run_time of AnimationGroup has been
Expand All @@ -115,7 +109,6 @@ def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs) -> No

def begin(self) -> None:
assert len(self.animations) > 0
self.init_run_time()
self.update_active_animation(0)

def finish(self) -> None:
Expand All @@ -129,24 +122,25 @@ def update_mobjects(self, dt: float) -> None:
def update_active_animation(self, index: int) -> None:
self.active_index = index
if index >= len(self.animations):
self.active_animation = None
self.active_start_time = None
self.active_end_time = None
self.active_animation: Optional[Animation] = None
self.active_start_time: Optional[float] = None
self.active_end_time: Optional[float] = None
else:
self.active_animation = self.animations[index]
self.active_animation.begin()
self.active_start_time = self.anims_with_timings[index][1]
self.active_end_time = self.anims_with_timings[index][2]

def next_animation(self) -> None:
self.active_animation.finish()
if self.active_animation is not None:
self.active_animation.finish()
self.update_active_animation(self.active_index + 1)

def interpolate(self, alpha: float) -> None:
current_time = interpolate(0, self.run_time, alpha)
current_time = alpha * self.run_time
while self.active_end_time is not None and current_time >= self.active_end_time:
self.next_animation()
if self.active_animation:
if self.active_animation is not None and self.active_start_time is not None:
elapsed = current_time - self.active_start_time
active_run_time = self.active_animation.get_run_time()
subalpha = elapsed / active_run_time if active_run_time != 0.0 else 1.0
Expand All @@ -166,9 +160,9 @@ def __init__(
class LaggedStartMap(LaggedStart):
def __init__(
self,
AnimationClass: Animation,
AnimationClass: Callable[..., Animation],
mobject: Mobject,
arg_creator: typing.Callable[[Mobject], str] = None,
arg_creator: Callable[[Mobject], str] = None,
run_time: float = 2,
**kwargs
) -> None:
Expand Down
Loading