From cd4befb9aff8c15e2cc9546240026c5674ec4d79 Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sat, 9 Sep 2023 14:19:16 +0200 Subject: [PATCH 1/7] adding the ability to pass lists and generators to .play() --- manim/scene/scene.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 5f3fb99247..b2ae800156 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -883,6 +883,9 @@ def compile_animations(self, *args: Animation, **kwargs): Animations to be played. """ animations = [] + # Allow passing a generator to self.play instead of comma separated arguments + if isinstance(args[0], types.GeneratorType) or hasattr(args[0], "__iter__"): + args = args[0] for arg in args: try: animations.append(prepare_animation(arg)) From d2ff557af67ff32b301fec1700c15a80bef8a33d Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sat, 9 Sep 2023 14:35:52 +0200 Subject: [PATCH 2/7] fix for _AnimationBuilder --- manim/scene/scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index b2ae800156..02306f9ece 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -884,7 +884,7 @@ def compile_animations(self, *args: Animation, **kwargs): """ animations = [] # Allow passing a generator to self.play instead of comma separated arguments - if isinstance(args[0], types.GeneratorType) or hasattr(args[0], "__iter__"): + if isinstance(args[0], (types.GeneratorType, list, tuple)): args = args[0] for arg in args: try: From d7c429034eae1d84bb17f450754d45aa7991c679 Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sun, 10 Dec 2023 03:54:26 +0100 Subject: [PATCH 3/7] Changed handling of generators to accept lists of generators and normal arguments at the same time --- manim/renderer/cairo_renderer.py | 7 ++++++- manim/scene/scene.py | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/manim/renderer/cairo_renderer.py b/manim/renderer/cairo_renderer.py index f11b7ea7ca..f0a4dbbd8f 100644 --- a/manim/renderer/cairo_renderer.py +++ b/manim/renderer/cairo_renderer.py @@ -15,6 +15,9 @@ from ..utils.iterables import list_update if typing.TYPE_CHECKING: + import types + + from manim.animation.animation import Animation from manim.scene.scene import Scene @@ -51,7 +54,9 @@ def init_scene(self, scene): scene.__class__.__name__, ) - def play(self, scene, *args, **kwargs): + def play( + self, scene: Scene, *args: Animation | types.GeneratorType[Animation], **kwargs + ): # Reset skip_animations to the original state. # Needed when rendering only some animations, and skipping others. self.skip_animations = self._original_skipping_status diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 02306f9ece..98e4562b39 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -865,7 +865,9 @@ def get_moving_and_static_mobjects(self, animations): ) return all_moving_mobject_families, static_mobjects - def compile_animations(self, *args: Animation, **kwargs): + def compile_animations( + self, *args: Animation | types.GeneratorType[Animation], **kwargs + ): """ Creates _MethodAnimations from any _AnimationBuilders and updates animation kwargs with kwargs passed to play(). @@ -883,10 +885,14 @@ def compile_animations(self, *args: Animation, **kwargs): Animations to be played. """ animations = [] + arg_anims = [] # Allow passing a generator to self.play instead of comma separated arguments - if isinstance(args[0], (types.GeneratorType, list, tuple)): - args = args[0] for arg in args: + if isinstance(arg, (types.GeneratorType, list, tuple)): + arg_anims.extend(arg) + else: + arg_anims.append(arg) + for arg in arg_anims: try: animations.append(prepare_animation(arg)) except TypeError: @@ -1030,7 +1036,7 @@ def get_run_time(self, animations: list[Animation]): def play( self, - *args, + *args: Animation | types.GeneratorType[Animation], subcaption=None, subcaption_duration=None, subcaption_offset=0, @@ -1160,7 +1166,9 @@ def wait_until(self, stop_condition: Callable[[], bool], max_time: float = 60): """ self.wait(max_time, stop_condition=stop_condition) - def compile_animation_data(self, *animations: Animation, **play_kwargs): + def compile_animation_data( + self, *animations: Animation | types.GeneratorType[Animation], **play_kwargs + ): """Given a list of animations, compile the corresponding static and moving mobjects, and gather the animation durations. From b46d60c6e8d2a46b9c7c990b1b6bf1cd906758b3 Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sun, 10 Dec 2023 04:16:14 +0100 Subject: [PATCH 4/7] Animation group handles generators --- manim/animation/composition.py | 14 +++++++++++--- manim/renderer/cairo_renderer.py | 7 +++++-- manim/scene/scene.py | 18 +++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/manim/animation/composition.py b/manim/animation/composition.py index cc6fa2ca7e..187ee80f38 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -3,7 +3,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence +from typing import TYPE_CHECKING, Callable, Iterable, Sequence import numpy as np @@ -18,6 +18,8 @@ from ..utils.rate_functions import linear if TYPE_CHECKING: + import types + from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup from ..mobject.types.vectorized_mobject import VGroup @@ -54,14 +56,20 @@ class AnimationGroup(Animation): def __init__( self, - *animations: Animation, + *animations: Animation | Iterable[Animation] | types.GeneratorType[Animation], group: Group | VGroup | OpenGLGroup | OpenGLVGroup = None, run_time: float | None = None, rate_func: Callable[[float], float] = linear, lag_ratio: float = 0, **kwargs, ) -> None: - self.animations = [prepare_animation(anim) for anim in animations] + arg_anim = [] + for anim in animations: + if isinstance(anim, (types.GeneratorType, Iterable)): + arg_anim.extend(anim) + else: + arg_anim.append(anim) + self.animations = [prepare_animation(anim) for anim in arg_anim] self.rate_func = rate_func self.group = group if self.group is None: diff --git a/manim/renderer/cairo_renderer.py b/manim/renderer/cairo_renderer.py index f0a4dbbd8f..e9d79b109f 100644 --- a/manim/renderer/cairo_renderer.py +++ b/manim/renderer/cairo_renderer.py @@ -1,7 +1,6 @@ from __future__ import annotations import typing -from typing import Any import numpy as np @@ -16,6 +15,7 @@ if typing.TYPE_CHECKING: import types + from typing import Any, Iterable from manim.animation.animation import Animation from manim.scene.scene import Scene @@ -55,7 +55,10 @@ def init_scene(self, scene): ) def play( - self, scene: Scene, *args: Animation | types.GeneratorType[Animation], **kwargs + self, + scene: Scene, + *args: Animation | Iterable[Animation] | types.GeneratorType[Animation], + **kwargs, ): # Reset skip_animations to the original state. # Needed when rendering only some animations, and skipping others. diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 98e4562b39..5c76a1026d 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -13,7 +13,6 @@ import time import types from queue import Queue -from typing import Callable import srt @@ -25,6 +24,8 @@ dearpygui_imported = True except ImportError: dearpygui_imported = False +import typing + import numpy as np from tqdm import tqdm from watchdog.events import FileSystemEventHandler @@ -48,6 +49,9 @@ from ..utils.file_ops import open_media_file from ..utils.iterables import list_difference_update, list_update +if typing.TYPE_CHECKING: + from typing import Callable, Iterable + class RerunSceneHandler(FileSystemEventHandler): """A class to handle rerunning a Scene after the input file is modified.""" @@ -866,7 +870,9 @@ def get_moving_and_static_mobjects(self, animations): return all_moving_mobject_families, static_mobjects def compile_animations( - self, *args: Animation | types.GeneratorType[Animation], **kwargs + self, + *args: Animation | Iterable[Animation] | types.GeneratorType[Animation], + **kwargs, ): """ Creates _MethodAnimations from any _AnimationBuilders and updates animation @@ -888,7 +894,7 @@ def compile_animations( arg_anims = [] # Allow passing a generator to self.play instead of comma separated arguments for arg in args: - if isinstance(arg, (types.GeneratorType, list, tuple)): + if isinstance(arg, (types.GeneratorType, Iterable)): arg_anims.extend(arg) else: arg_anims.append(arg) @@ -1036,7 +1042,7 @@ def get_run_time(self, animations: list[Animation]): def play( self, - *args: Animation | types.GeneratorType[Animation], + *args: Animation | Iterable[Animation] | types.GeneratorType[Animation], subcaption=None, subcaption_duration=None, subcaption_offset=0, @@ -1167,7 +1173,9 @@ def wait_until(self, stop_condition: Callable[[], bool], max_time: float = 60): self.wait(max_time, stop_condition=stop_condition) def compile_animation_data( - self, *animations: Animation | types.GeneratorType[Animation], **play_kwargs + self, + *animations: Animation | Iterable[Animation] | types.GeneratorType[Animation], + **play_kwargs, ): """Given a list of animations, compile the corresponding static and moving mobjects, and gather the animation durations. From 06b138da049b3a2e680bb9e92371b81b493f6078 Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sun, 10 Dec 2023 04:36:36 +0100 Subject: [PATCH 5/7] Refactored into own function for reusability --- manim/animation/composition.py | 11 +++------ manim/mobject/types/vectorized_mobject.py | 6 ++++- manim/scene/scene.py | 12 ++++------ manim/utils/parameter_parsing.py | 29 +++++++++++++++++++++++ 4 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 manim/utils/parameter_parsing.py diff --git a/manim/animation/composition.py b/manim/animation/composition.py index 187ee80f38..b5a421576f 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -3,11 +3,13 @@ from __future__ import annotations +import types from typing import TYPE_CHECKING, Callable, Iterable, Sequence import numpy as np from manim.mobject.opengl.opengl_mobject import OpenGLGroup +from manim.utils.parameter_parsing import flatten_iterable_parameters from .._config import config from ..animation.animation import Animation, prepare_animation @@ -18,8 +20,6 @@ from ..utils.rate_functions import linear if TYPE_CHECKING: - import types - from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup from ..mobject.types.vectorized_mobject import VGroup @@ -63,12 +63,7 @@ def __init__( lag_ratio: float = 0, **kwargs, ) -> None: - arg_anim = [] - for anim in animations: - if isinstance(anim, (types.GeneratorType, Iterable)): - arg_anim.extend(anim) - else: - arg_anim.append(anim) + arg_anim = flatten_iterable_parameters(animations) self.animations = [prepare_animation(anim) for anim in arg_anim] self.rate_func = rate_func self.group = group diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 6048fe4c67..7aff4fe9ac 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -2,6 +2,8 @@ from __future__ import annotations +from manim.utils.parameter_parsing import flatten_iterable_parameters + __all__ = [ "VMobject", "VGroup", @@ -65,6 +67,7 @@ Vector3, Zeros, ) +from types import GeneratorType # TODO # - Change cubic curve groups to have 4 points instead of 3 @@ -1903,8 +1906,9 @@ def construct(self): """ def __init__(self, *vmobjects, **kwargs): + vm_arg = flatten_iterable_parameters(vmobjects) super().__init__(**kwargs) - self.add(*vmobjects) + self.add(*vm_arg) def __repr__(self) -> str: return f'{self.__class__.__name__}({", ".join(str(mob) for mob in self.submobjects)})' diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 5c76a1026d..cf3510aad6 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -2,6 +2,8 @@ from __future__ import annotations +from manim.utils.parameter_parsing import flatten_iterable_parameters + __all__ = ["Scene"] import copy @@ -25,6 +27,7 @@ except ImportError: dearpygui_imported = False import typing +from typing import Iterable import numpy as np from tqdm import tqdm @@ -50,7 +53,7 @@ from ..utils.iterables import list_difference_update, list_update if typing.TYPE_CHECKING: - from typing import Callable, Iterable + from typing import Callable class RerunSceneHandler(FileSystemEventHandler): @@ -891,13 +894,8 @@ def compile_animations( Animations to be played. """ animations = [] - arg_anims = [] + arg_anims = flatten_iterable_parameters(args) # Allow passing a generator to self.play instead of comma separated arguments - for arg in args: - if isinstance(arg, (types.GeneratorType, Iterable)): - arg_anims.extend(arg) - else: - arg_anims.append(arg) for arg in arg_anims: try: animations.append(prepare_animation(arg)) diff --git a/manim/utils/parameter_parsing.py b/manim/utils/parameter_parsing.py new file mode 100644 index 0000000000..bb220da66e --- /dev/null +++ b/manim/utils/parameter_parsing.py @@ -0,0 +1,29 @@ +from types import GeneratorType +from typing import Iterable, TypeVar + +T = TypeVar("T") + + +def flatten_iterable_parameters( + args: Iterable[T | Iterable[T] | GeneratorType], +) -> list[T]: + """Flattens an iterable of parameters into a list of parameters. + + Parameters + ---------- + args + The iterable of parameters to flatten. + [(generator), [], (), ...] + + Returns + ------- + :class:`list` + The flattened list of parameters. + """ + flattened_parameters = [] + for arg in args: + if isinstance(arg, (Iterable, GeneratorType)): + flattened_parameters.extend(arg) + else: + flattened_parameters.append(arg) + return flattened_parameters From 422a66b6841d181d807a48e0919e0295a560d7c0 Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sun, 10 Dec 2023 04:43:45 +0100 Subject: [PATCH 6/7] Fix typing --- manim/scene/scene.py | 7 +++---- manim/utils/parameter_parsing.py | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index cf3510aad6..2a3b37d76f 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -26,8 +26,7 @@ dearpygui_imported = True except ImportError: dearpygui_imported = False -import typing -from typing import Iterable +from typing import TYPE_CHECKING import numpy as np from tqdm import tqdm @@ -52,8 +51,8 @@ from ..utils.file_ops import open_media_file from ..utils.iterables import list_difference_update, list_update -if typing.TYPE_CHECKING: - from typing import Callable +if TYPE_CHECKING: + from typing import Callable, Iterable class RerunSceneHandler(FileSystemEventHandler): diff --git a/manim/utils/parameter_parsing.py b/manim/utils/parameter_parsing.py index bb220da66e..458885a5b3 100644 --- a/manim/utils/parameter_parsing.py +++ b/manim/utils/parameter_parsing.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from types import GeneratorType from typing import Iterable, TypeVar From 6ea39f9f8f1331b33b5f073391f1ba390a24476c Mon Sep 17 00:00:00 2001 From: MrDiver Date: Sun, 10 Dec 2023 04:52:52 +0100 Subject: [PATCH 7/7] Fix typing --- manim/mobject/types/vectorized_mobject.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 7aff4fe9ac..6048fe4c67 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -2,8 +2,6 @@ from __future__ import annotations -from manim.utils.parameter_parsing import flatten_iterable_parameters - __all__ = [ "VMobject", "VGroup", @@ -67,7 +65,6 @@ Vector3, Zeros, ) -from types import GeneratorType # TODO # - Change cubic curve groups to have 4 points instead of 3 @@ -1906,9 +1903,8 @@ def construct(self): """ def __init__(self, *vmobjects, **kwargs): - vm_arg = flatten_iterable_parameters(vmobjects) super().__init__(**kwargs) - self.add(*vm_arg) + self.add(*vmobjects) def __repr__(self) -> str: return f'{self.__class__.__name__}({", ".join(str(mob) for mob in self.submobjects)})'