Skip to content

Commit

Permalink
feat(lib): add skip_animations compatibility (#516)
Browse files Browse the repository at this point in the history
* feat: Add skip_animations compatibility

* Add tests, config and changelog

* chore(fmt): auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim_slides/slide/base.py

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* chore(fmt): auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* chore(tests): implement tests

---------

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 21, 2025
1 parent df31345 commit 32ab690
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(unreleased)=
## [Unreleased](https://github.com/jeertmans/manim-slides/compare/v5.3.1...HEAD)

(unreleased-added)=
### Added

- Added `skip_animations` compatibility with ManimCE.
[@Rapsssito](https://github.com/Rapsssito) [#516](https://github.com/jeertmans/manim-slides/pull/516)

(v5.3.1)=
## [v5.3.1](https://github.com/jeertmans/manim-slides/compare/v5.3.0...v5.3.1)

Expand Down
1 change: 1 addition & 0 deletions manim_slides/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class BaseSlideConfig(BaseModel): # type: ignore
reversed_playback_rate: float = 1.0
notes: str = ""
dedent_notes: bool = True
skip_animations: bool = False

@classmethod
def wrapper(cls, arg_name: str) -> Callable[..., Any]:
Expand Down
16 changes: 14 additions & 2 deletions manim_slides/slide/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def wait_time_between_slides(self, wait_time: float) -> None:
self._wait_time_between_slides = max(wait_time, 0.0)

def play(self, *args: Any, **kwargs: Any) -> None:
"""Overload `self.play` and increment animation count."""
"""Overload 'self.play' and increment animation count."""
super().play(*args, **kwargs) # type: ignore[misc]
self._current_animation += 1

Expand All @@ -299,6 +299,11 @@ def next_slide(
Positional arguments passed to
:meth:`Scene.next_section<manim.scene.scene.Scene.next_section>`,
or ignored if `manimlib` API is used.
:param skip_animations:
Exclude the next slide from the output.
If `manim` is used, this is also passed to `:meth:`Scene.next_section<manim.scene.scene.Scene.next_section>`,
which will avoid rendering the corresponding animations.
:param loop:
If set, next slide will be looping.
:param auto_next:
Expand Down Expand Up @@ -521,9 +526,16 @@ def _save_slides(
ascii=True if platform.system() == "Windows" else None,
disable=not self._show_progress_bar,
):
if pre_slide_config.skip_animations:
continue
slide_files = files[pre_slide_config.slides_slice]

file = merge_basenames(slide_files)
try:
file = merge_basenames(slide_files)
except ValueError as e:
raise ValueError(
f"Failed to merge basenames of files for slide: {pre_slide_config!r}"
) from e
dst_file = scene_files_folder / file.name
rev_file = scene_files_folder / f"{file.stem}_reversed{file.suffix}"

Expand Down
13 changes: 12 additions & 1 deletion manim_slides/slide/manim.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ def _leave_progress_bar(self) -> bool:
def _start_at_animation_number(self) -> Optional[int]:
return config["from_animation_number"] # type: ignore

def play(self, *args: Any, **kwargs: Any) -> None:
"""Overload 'self.play' and increment animation count."""
super().play(*args, **kwargs)

if self._base_slide_config.skip_animations:
# Manim will not render the animations, so we reset the animation
# counter to the previous value
self._current_animation -= 1

def next_section(self, *args: Any, **kwargs: Any) -> None:
"""
Alias to :meth:`next_slide`.
Expand All @@ -111,7 +120,9 @@ def next_slide(
base_slide_config: BaseSlideConfig,
**kwargs: Any,
) -> None:
Scene.next_section(self, *args, **kwargs)
Scene.next_section(
self, *args, skip_animations=base_slide_config.skip_animations, **kwargs
)
BaseSlide.next_slide.__wrapped__(
self,
base_slide_config=base_slide_config,
Expand Down
73 changes: 72 additions & 1 deletion tests/test_slide.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import contextlib
import os
import random
import shutil
import sys
import tempfile
import threading
from collections.abc import Iterator
from pathlib import Path
from typing import Any, Union

Expand All @@ -17,6 +22,7 @@
Dot,
FadeIn,
GrowFromCenter,
Square,
Text,
)
from manim.renderer.opengl_renderer import OpenGLRenderer
Expand Down Expand Up @@ -229,8 +235,26 @@ def assert_constructs(cls: SlideType) -> None:
init_slide(cls).construct()


_L = (
threading.Lock()
) # We cannot change directory multiple times at once (in the same thread)


@contextlib.contextmanager
def tmp_cwd() -> Iterator[str]:
old_cwd = os.getcwd()

with tempfile.TemporaryDirectory() as tmp_dir, _L:
try:
os.chdir(tmp_dir)
yield tmp_dir
finally:
os.chdir(old_cwd)


def assert_renders(cls: SlideType) -> None:
init_slide(cls).render()
with tmp_cwd():
init_slide(cls).render()


class TestSlide:
Expand Down Expand Up @@ -479,6 +503,53 @@ def construct(self) -> None:
self.next_slide()
assert self._current_slide == 2

def test_next_slide_skip_animations(self) -> None:
class Foo(CESlide):
def construct(self) -> None:
circle = Circle(color=BLUE)
self.play(GrowFromCenter(circle))
assert not self._base_slide_config.skip_animations
self.next_slide(skip_animations=True)
square = Square(color=BLUE)
self.play(GrowFromCenter(square))
assert self._base_slide_config.skip_animations
self.next_slide()
assert not self._base_slide_config.skip_animations
self.play(GrowFromCenter(square))

class Bar(CESlide):
def construct(self) -> None:
circle = Circle(color=BLUE)
self.play(GrowFromCenter(circle))
assert not self._base_slide_config.skip_animations
self.next_slide(skip_animations=False)
square = Square(color=BLUE)
self.play(GrowFromCenter(square))
assert not self._base_slide_config.skip_animations
self.next_slide()
assert not self._base_slide_config.skip_animations
self.play(GrowFromCenter(square))

with tmp_cwd() as tmp_dir:
init_slide(Foo).render()
init_slide(Bar).render()

slides_folder = Path(tmp_dir) / "slides"

assert slides_folder.exists()

slide_file = slides_folder / "Foo.json"

config = PresentationConfig.from_file(slide_file)

assert len(config.slides) == 2

slide_file = slides_folder / "Bar.json"

config = PresentationConfig.from_file(slide_file)

assert len(config.slides) == 3

def test_canvas(self) -> None:
@assert_constructs
class _(CESlide):
Expand Down
3 changes: 2 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 32ab690

Please sign in to comment.