From 514a3a3e4892f450e85940f3774ad461503e22c7 Mon Sep 17 00:00:00 2001 From: Anton Ballmaier Date: Mon, 17 May 2021 01:27:28 +0200 Subject: [PATCH 1/6] remove turn_animation_into_updater and reuse logic --- manim/mobject/mobject_update_utils.py | 33 ------------------ manim/scene/scene.py | 50 +++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/manim/mobject/mobject_update_utils.py b/manim/mobject/mobject_update_utils.py index 90658b53aa..b8e35c9abc 100644 --- a/manim/mobject/mobject_update_utils.py +++ b/manim/mobject/mobject_update_utils.py @@ -7,7 +7,6 @@ "always_redraw", "always_shift", "always_rotate", - "turn_animation_into_updater", "cycle_animation", ] @@ -74,37 +73,5 @@ def always_rotate(mobject, rate=20 * DEGREES, **kwargs): return mobject -def turn_animation_into_updater(animation, cycle=False, **kwargs): - """ - Add an updater to the animation's mobject which applies - the interpolation and update functions of the animation - - If cycle is True, this repeats over and over. Otherwise, - the updater will be popped upon completion - """ - mobject = animation.mobject - animation.suspend_mobject_updating = False - animation.begin() - animation.total_time = 0 - - def update(m, dt): - run_time = animation.get_run_time() - time_ratio = animation.total_time / run_time - if cycle: - alpha = time_ratio % 1 - else: - alpha = np.clip(time_ratio, 0, 1) - if alpha >= 1: - animation.finish() - m.remove_updater(update) - return - animation.interpolate(alpha) - animation.update_mobjects(dt) - animation.total_time += dt - - mobject.add_updater(update) - return mobject - - def cycle_animation(animation, **kwargs): return turn_animation_into_updater(animation, cycle=True, **kwargs) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 8f9caabafa..dfb4de62fa 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -849,8 +849,53 @@ def get_run_time(self, animations): else: return np.max([animation.run_time for animation in animations]) - def play(self, *args, **kwargs): - self.renderer.play(self, *args, **kwargs) + def play(self, *args, run_async=None, **kwargs): + sync_animation_running = bool(self.animations) + + if config["renderer"] != "opengl": + run_async = False + + if sync_animation_running: + if run_async is None: + run_async = True + if not run_async: + raise ValueError( + "Cannot start a synchronous animation while another is running." + ) + elif run_async is None: + run_async = False + + if run_async: + animations = self.compile_animations(*args, **kwargs) + + self.add_mobjects_from_animations(animations) + for animation in animations: + self._turn_animation_to_updater(animation) + return + else: + self.renderer.play(self, *args, **kwargs) + + def _turn_animation_to_updater(self, animation: Animation): + mobject = animation.mobject + + animation.suspend_mobject_updating = False + animation.begin() + + def update(mob, dt): + run_time = animation.get_run_time() + alpha = update.total_time / run_time + if alpha >= 1: + animation.finish() + animation.clean_up_from_scene(self) + self.remove_updater(update) + return + animation.interpolate(alpha) + animation.update_mobjects(dt) + update.total_time += dt + + update.total_time = 0 + mobject.add_updater(update) + return self def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None): self.play(Wait(run_time=duration, stop_condition=stop_condition)) @@ -966,6 +1011,7 @@ def play_internal(self, skip_rendering=False): self.renderer.static_image = None # Closing the progress bar at the end of the play. self.time_progression.close() + self.animations = None def interactive_embed(self): """ From 928abf13cadc71c7570391bfde9ce0aa1b599c44 Mon Sep 17 00:00:00 2001 From: Anton Ballmaier Date: Mon, 17 May 2021 01:36:00 +0200 Subject: [PATCH 2/6] fix bug --- 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 dfb4de62fa..ff91eebbe4 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -887,7 +887,7 @@ def update(mob, dt): if alpha >= 1: animation.finish() animation.clean_up_from_scene(self) - self.remove_updater(update) + mob.remove_updater(update) return animation.interpolate(alpha) animation.update_mobjects(dt) From e38b1d751d92ae2540faf2b575d881835012ca54 Mon Sep 17 00:00:00 2001 From: Anton Ballmaier Date: Mon, 17 May 2021 02:18:55 +0200 Subject: [PATCH 3/6] add Scene.play docs --- manim/scene/scene.py | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index ff91eebbe4..d7ba5c61f3 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -10,6 +10,7 @@ import random import threading import types +from typing import Any, Optional import warnings from queue import Queue @@ -91,6 +92,7 @@ def __init__( self.random_seed = random_seed self.animations = None + self.sync_play_running = False self.stop_condition = None self.moving_mobjects = None self.static_mobjects = None @@ -849,13 +851,42 @@ def get_run_time(self, animations): else: return np.max([animation.run_time for animation in animations]) - def play(self, *args, run_async=None, **kwargs): - sync_animation_running = bool(self.animations) + def play( + self, *animations: Animation, run_async: Optional[bool] = None, **kwargs: Any + ): + """Play :class:`Animations<.Animation>` in the scene. + + Parameters + ---------- + animations + The animations to be played. They are started at the same timed and played + in parallel. + run_async + Whether to play the animations asynchronously. Asynchronous animations don't + interrupt the execution of :meth:`construct` and can be started while + synchronous animations are running. If ``run_async`` is not defined, it + defaults to ``True`` if called while a synchronous animation is running, + to ``False`` otherwise. + + .. note:: + + This feature is only available when using the OpenGL-Renderer. The + parameter will be ignored otherwise. + kwargs + Attributes of all passed animations to be set. Typical usecases are setting + `run_time` or `rate_func`. + + Raises + ------ + ValueError + If an animation is played synchronously while another synchronous animation + is running. + """ if config["renderer"] != "opengl": run_async = False - if sync_animation_running: + if self.sync_play_running: if run_async is None: run_async = True if not run_async: @@ -866,14 +897,14 @@ def play(self, *args, run_async=None, **kwargs): run_async = False if run_async: - animations = self.compile_animations(*args, **kwargs) + animations = self.compile_animations(*animations, **kwargs) self.add_mobjects_from_animations(animations) for animation in animations: self._turn_animation_to_updater(animation) return else: - self.renderer.play(self, *args, **kwargs) + self.renderer.play(self, *animations, **kwargs) def _turn_animation_to_updater(self, animation: Animation): mobject = animation.mobject @@ -991,6 +1022,7 @@ def play_internal(self, skip_rendering=False): named parameters affecting what was passed in ``args``, e.g. ``run_time``, ``lag_ratio`` and so on. """ + self.sync_play_running = True self.duration = self.get_run_time(self.animations) self.time_progression = self._get_animation_time_progression( self.animations, self.duration @@ -1011,7 +1043,7 @@ def play_internal(self, skip_rendering=False): self.renderer.static_image = None # Closing the progress bar at the end of the play. self.time_progression.close() - self.animations = None + self.sync_play_running = False def interactive_embed(self): """ From 03271d5fa5fcb5bde542feb5e5e8a3b0b8178a15 Mon Sep 17 00:00:00 2001 From: Anton Ballmaier Date: Mon, 17 May 2021 02:20:58 +0200 Subject: [PATCH 4/6] add docs to _turn_animation_to_updater --- manim/scene/scene.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index d7ba5c61f3..3445b4f6b2 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -907,6 +907,16 @@ def play( self.renderer.play(self, *animations, **kwargs) def _turn_animation_to_updater(self, animation: Animation): + """Turn an :class:`~.Animation` into an updater of it's :class:`~.Mobject`. + + The updater will remove itself when the animation is over. + + Parameters + ---------- + animation + The animation to be added + + """ mobject = animation.mobject animation.suspend_mobject_updating = False From 682a1007cd382507616fb0644f8c5f8e61283588 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 00:33:14 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 3445b4f6b2..85fa56bc68 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -10,9 +10,9 @@ import random import threading import types -from typing import Any, Optional import warnings from queue import Queue +from typing import Any, Optional import numpy as np from tqdm import tqdm From 236ea3e1e07538e3e8528bdcd9bd45f86fb7352b Mon Sep 17 00:00:00 2001 From: Anton Ballmaier Date: Mon, 17 May 2021 23:49:35 +0200 Subject: [PATCH 6/6] fix typo --- 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 85fa56bc68..87281c2a25 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -859,7 +859,7 @@ def play( Parameters ---------- animations - The animations to be played. They are started at the same timed and played + The animations to be played. They are started at the same time and played in parallel. run_async Whether to play the animations asynchronously. Asynchronous animations don't