From af3c4971aecd812e14762e6e5560a7ca9a0724b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Tue, 5 Dec 2023 13:29:16 +0100 Subject: [PATCH] feat(lib): add `return_animation` option (#331) * feat(lib): add `return_animation` option Allow to return animation instead of playing it. * fix(lib): docs issue * fix(ci): build with Python 3.10 --- .bumpversion.cfg | 2 +- .github/workflows/pages.yml | 2 +- CHANGELOG.md | 3 ++ docs/source/conf.py | 4 +++ manim_slides/slide/base.py | 60 ++++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7b47902..5062c50 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,7 +1,7 @@ [bumpversion] current_version = 5.1.0-rc1 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-rc(?P\d+))? -serialize = +serialize = {major}.{minor}.{patch}-rc{release} {major}.{minor}.{patch} commit = True diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 2874980..a7ad4d8 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -38,7 +38,7 @@ jobs: - name: Install Python uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.10' cache: poetry - name: Setup Pages uses: actions/configure-pages@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index e19392c..a076a45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This is somewhat a **breaking change**, but changes to the CLI API are not considered to be very important. [#325](https://github.com/jeertmans/manim-slides/pull/325) +- Added `return_animation` option to slide animations `self.wipe` + and `self.zoom`. + [#331](https://github.com/jeertmans/manim-slides/pull/331) (v5.1-modified)= ### Modified diff --git a/docs/source/conf.py b/docs/source/conf.py index d40ede9..9a52a34 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -4,6 +4,10 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +import sys + +assert sys.version_info >= (3, 10), "Building docs requires Python 3.10" + # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information diff --git a/manim_slides/slide/base.py b/manim_slides/slide/base.py index 88c2af1..8583d2a 100644 --- a/manim_slides/slide/base.py +++ b/manim_slides/slide/base.py @@ -1,9 +1,17 @@ +from __future__ import annotations + __all__ = ["BaseSlide"] import platform from abc import abstractmethod from pathlib import Path -from typing import Any, List, MutableMapping, Optional, Sequence, Tuple, ValuesView +from typing import ( + TYPE_CHECKING, + Any, + MutableMapping, + Sequence, + ValuesView, +) import numpy as np from tqdm import tqdm @@ -14,6 +22,9 @@ from ..logger import logger from ..utils import concatenate_video_files, merge_basenames, reverse_video_file from . import MANIM +if TYPE_CHECKING: + from .animation import Wipe, Zoom + if MANIM: from manim.mobject.mobject import Mobject else: @@ -28,7 +39,7 @@ class BaseSlide: ) -> None: super().__init__(*args, **kwargs) self._output_folder: Path = output_folder - self._slides: List[PreSlideConfig] = [] + self._slides: list[PreSlideConfig] = [] self._base_slide_config: BaseSlideConfig = BaseSlideConfig() self._current_slide = 1 self._current_animation = 0 @@ -61,13 +72,13 @@ class BaseSlide: @property @abstractmethod - def _resolution(self) -> Tuple[int, int]: + def _resolution(self) -> tuple[int, int]: """Return the scene's resolution used during rendering.""" raise NotImplementedError @property @abstractmethod - def _partial_movie_files(self) -> List[Path]: + def _partial_movie_files(self) -> list[Path]: """Return a list of partial movie files, a.k.a animations.""" raise NotImplementedError @@ -85,7 +96,7 @@ class BaseSlide: @property @abstractmethod - def _start_at_animation_number(self) -> Optional[int]: + def _start_at_animation_number(self) -> int | None: """If set, return the animation number at which rendering start.""" raise NotImplementedError @@ -453,7 +464,7 @@ class BaseSlide: scene_files_folder.mkdir(parents=True, exist_ok=True) - files: List[Path] = self._partial_movie_files + files: list[Path] = self._partial_movie_files # We must filter slides that end before the animation offset if offset := self._start_at_animation_number: @@ -464,7 +475,7 @@ class BaseSlide: slide.start_animation = max(0, slide.start_animation - offset) slide.end_animation -= offset - slides: List[SlideConfig] = [] + slides: list[SlideConfig] = [] for pre_slide_config in tqdm( self._slides, @@ -513,8 +524,9 @@ class BaseSlide: self, *args: Any, direction: np.ndarray = LEFT, + return_animation: bool = False, **kwargs: Any, - ) -> None: + ) -> Wipe | None: """ Play a wipe animation that will shift all the current objects outside of the current scene's scope, and all the future objects inside. @@ -522,6 +534,8 @@ class BaseSlide: :param args: Positional arguments passed to :class:`Wipe`. :param direction: The wipe direction, that will be scaled by the scene size. + :param return_animation: If set, return the animation instead of + playing it. This is useful to combine multiple animations with this one. :param kwargs: Keyword arguments passed to :class:`Wipe`. @@ -548,7 +562,13 @@ class BaseSlide: self.wipe(Group(square, text), beautiful, direction=UP) self.next_slide() - self.wipe(beautiful, circle, direction=DOWN + RIGHT) + anim = self.wipe( + beautiful, + circle, + direction=DOWN + RIGHT, + return_animation=True + ) + self.play(anim) """ from .animation import Wipe @@ -563,13 +583,18 @@ class BaseSlide: **kwargs, ) + if return_animation: + return animation + self.play(animation) + return None def zoom( self, *args: Any, + return_animation: bool = False, **kwargs: Any, - ) -> None: + ) -> Zoom | None: """ Play a zoom animation that will fade out all the current objects, and fade in all the future objects. Objects are faded in a direction that goes towards the @@ -577,6 +602,8 @@ class BaseSlide: :param args: Positional arguments passed to :class:`Zoom`. + :param return_animation: If set, return the animation instead of + playing it. This is useful to combine multiple animations with this one. :param kwargs: Keyword arguments passed to :class:`Zoom`. @@ -598,10 +625,21 @@ class BaseSlide: self.zoom(circle, square) self.next_slide() - self.zoom(square, circle, out=True, scale=10.0) + anim = self.zoom( + square, + circle, + out=True, + scale=10.0, + return_animation=True + ) + self.play(anim) """ from .animation import Zoom animation = Zoom(*args, **kwargs) + if return_animation: + return animation + self.play(animation) + return None