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
This commit is contained in:
Jérome Eertmans
2023-12-05 13:29:16 +01:00
committed by GitHub
parent b3ed127e31
commit af3c4971ae
5 changed files with 58 additions and 13 deletions

View File

@ -1,7 +1,7 @@
[bumpversion] [bumpversion]
current_version = 5.1.0-rc1 current_version = 5.1.0-rc1
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-rc(?P<release>\d+))? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-rc(?P<release>\d+))?
serialize = serialize =
{major}.{minor}.{patch}-rc{release} {major}.{minor}.{patch}-rc{release}
{major}.{minor}.{patch} {major}.{minor}.{patch}
commit = True commit = True

View File

@ -38,7 +38,7 @@ jobs:
- name: Install Python - name: Install Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.9' python-version: '3.10'
cache: poetry cache: poetry
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v3 uses: actions/configure-pages@v3

View File

@ -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 This is somewhat a **breaking change**, but changes to the CLI
API are not considered to be very important. API are not considered to be very important.
[#325](https://github.com/jeertmans/manim-slides/pull/325) [#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)= (v5.1-modified)=
### Modified ### Modified

View File

@ -4,6 +4,10 @@
# For the full list of built-in configuration values, see the documentation: # For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html # 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 ----------------------------------------------------- # -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

View File

@ -1,9 +1,17 @@
from __future__ import annotations
__all__ = ["BaseSlide"] __all__ = ["BaseSlide"]
import platform import platform
from abc import abstractmethod from abc import abstractmethod
from pathlib import Path 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 import numpy as np
from tqdm import tqdm from tqdm import tqdm
@ -14,6 +22,9 @@ from ..logger import logger
from ..utils import concatenate_video_files, merge_basenames, reverse_video_file from ..utils import concatenate_video_files, merge_basenames, reverse_video_file
from . import MANIM from . import MANIM
if TYPE_CHECKING:
from .animation import Wipe, Zoom
if MANIM: if MANIM:
from manim.mobject.mobject import Mobject from manim.mobject.mobject import Mobject
else: else:
@ -28,7 +39,7 @@ class BaseSlide:
) -> None: ) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._output_folder: Path = output_folder self._output_folder: Path = output_folder
self._slides: List[PreSlideConfig] = [] self._slides: list[PreSlideConfig] = []
self._base_slide_config: BaseSlideConfig = BaseSlideConfig() self._base_slide_config: BaseSlideConfig = BaseSlideConfig()
self._current_slide = 1 self._current_slide = 1
self._current_animation = 0 self._current_animation = 0
@ -61,13 +72,13 @@ class BaseSlide:
@property @property
@abstractmethod @abstractmethod
def _resolution(self) -> Tuple[int, int]: def _resolution(self) -> tuple[int, int]:
"""Return the scene's resolution used during rendering.""" """Return the scene's resolution used during rendering."""
raise NotImplementedError raise NotImplementedError
@property @property
@abstractmethod @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.""" """Return a list of partial movie files, a.k.a animations."""
raise NotImplementedError raise NotImplementedError
@ -85,7 +96,7 @@ class BaseSlide:
@property @property
@abstractmethod @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.""" """If set, return the animation number at which rendering start."""
raise NotImplementedError raise NotImplementedError
@ -453,7 +464,7 @@ class BaseSlide:
scene_files_folder.mkdir(parents=True, exist_ok=True) 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 # We must filter slides that end before the animation offset
if offset := self._start_at_animation_number: if offset := self._start_at_animation_number:
@ -464,7 +475,7 @@ class BaseSlide:
slide.start_animation = max(0, slide.start_animation - offset) slide.start_animation = max(0, slide.start_animation - offset)
slide.end_animation -= offset slide.end_animation -= offset
slides: List[SlideConfig] = [] slides: list[SlideConfig] = []
for pre_slide_config in tqdm( for pre_slide_config in tqdm(
self._slides, self._slides,
@ -513,8 +524,9 @@ class BaseSlide:
self, self,
*args: Any, *args: Any,
direction: np.ndarray = LEFT, direction: np.ndarray = LEFT,
return_animation: bool = False,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> Wipe | None:
""" """
Play a wipe animation that will shift all the current objects outside of the Play a wipe animation that will shift all the current objects outside of the
current scene's scope, and all the future objects inside. current scene's scope, and all the future objects inside.
@ -522,6 +534,8 @@ class BaseSlide:
:param args: Positional arguments passed to :param args: Positional arguments passed to
:class:`Wipe<manim_slides.slide.animation.Wipe>`. :class:`Wipe<manim_slides.slide.animation.Wipe>`.
:param direction: The wipe direction, that will be scaled by the scene size. :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 :param kwargs: Keyword arguments passed to
:class:`Wipe<manim_slides.slide.animation.Wipe>`. :class:`Wipe<manim_slides.slide.animation.Wipe>`.
@ -548,7 +562,13 @@ class BaseSlide:
self.wipe(Group(square, text), beautiful, direction=UP) self.wipe(Group(square, text), beautiful, direction=UP)
self.next_slide() 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 from .animation import Wipe
@ -563,13 +583,18 @@ class BaseSlide:
**kwargs, **kwargs,
) )
if return_animation:
return animation
self.play(animation) self.play(animation)
return None
def zoom( def zoom(
self, self,
*args: Any, *args: Any,
return_animation: bool = False,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> Zoom | None:
""" """
Play a zoom animation that will fade out all the current objects, and fade in 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 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 :param args: Positional arguments passed to
:class:`Zoom<manim_slides.slide.animation.Zoom>`. :class:`Zoom<manim_slides.slide.animation.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 :param kwargs: Keyword arguments passed to
:class:`Zoom<manim_slides.slide.animation.Zoom>`. :class:`Zoom<manim_slides.slide.animation.Zoom>`.
@ -598,10 +625,21 @@ class BaseSlide:
self.zoom(circle, square) self.zoom(circle, square)
self.next_slide() 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 from .animation import Zoom
animation = Zoom(*args, **kwargs) animation = Zoom(*args, **kwargs)
if return_animation:
return animation
self.play(animation) self.play(animation)
return None