Compare commits

..

10 Commits

Author SHA1 Message Date
528952dbc3 chore(deps): bump version from 5.4.1 to 5.4.2 2025-01-22 21:43:00 +01:00
dbced6e62e fix(lib): pass skip argument to ManimCE (#524)
* fix(lib): pass skip argument to ManimCE

* fix(lint): make ruff happy
2025-01-22 21:41:22 +01:00
941b895083 chore(deps): bump version from 5.4.0 to 5.4.1 2025-01-22 21:12:20 +01:00
289b7c1683 fix(ci): lockfile 2025-01-22 21:12:14 +01:00
b07a83898b feat(lib): add start_skip_animations and stop_skip_animations meth. (#523) 2025-01-22 21:10:37 +01:00
074a029759 chore(deps): bump version from 5.3.1 to 5.4.0 2025-01-21 13:41:51 +01:00
b4af76050e fix(ci): lock 2025-01-21 13:41:43 +01:00
adce58e1b7 fix(lib): bump Manim and fix OpenGL renderer (#522)
* fix(lib): bump Manim and fix OpenGL renderer

* fix(tests): correctly change cwd
2025-01-21 13:38:41 +01:00
32ab690932 feat(lib): add skip_animations compatibility (#516)
* 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>
2025-01-21 11:27:21 +01:00
df31345f83 chore(deps): pre-commit autoupdate (#521)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.1 → v0.9.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.1...v0.9.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-01-21 10:11:13 +01:00
13 changed files with 826 additions and 416 deletions

View File

@ -21,7 +21,7 @@ repos:
exclude: poetry.lock
args: [--autofix, --trailing-commas]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.1
rev: v0.9.2
hooks:
- id: ruff
args: [--fix]

View File

@ -8,7 +8,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- start changelog -->
(unreleased)=
## [Unreleased](https://github.com/jeertmans/manim-slides/compare/v5.3.1...HEAD)
## [Unreleased](https://github.com/jeertmans/manim-slides/compare/v5.4.2...HEAD)
(v5.4.2)=
## [v5.4.2](https://github.com/jeertmans/manim-slides/compare/v5.4.1...v5.4.2)
(v5.4.2-fixed)=
### Fixed
- Fixed `start_skip_animations` to actually pass argument to ManimCE,
otherwise video animations were still rendered, just excluded from
the final output.
[#524](https://github.com/jeertmans/manim-slides/pull/524)
(v5.4.1)=
## [v5.4.1](https://github.com/jeertmans/manim-slides/compare/v5.4.0...v5.4.1)
(v5.4.1-added)=
### Added
- Added `start_skip_animations` and `stop_skip_animations` methods.
[#523](https://github.com/jeertmans/manim-slides/pull/523)
(v5.4.0)=
## [v5.4.0](https://github.com/jeertmans/manim-slides/compare/v5.3.1...v5.4.0)
(v5.4.0-added)=
### Added
- Added `skip_animations` compatibility with ManimCE.
[@Rapsssito](https://github.com/Rapsssito) [#516](https://github.com/jeertmans/manim-slides/pull/516)
(v5.4.0-chore)=
### Chore
- Bumped Manim to `>=0.19`, as it fixed OpenGL renderer issue.
[#522](https://github.com/jeertmans/manim-slides/pull/522)
(v5.4.0-fixed)=
### Fixed
- Fixed OpenGL renderer having no partial movie files with Manim bindings.
[#522](https://github.com/jeertmans/manim-slides/pull/522)
- Fixed `ConvertExample` example as `manim>=0.19` changed the `Code` class.
[#522](https://github.com/jeertmans/manim-slides/pull/522)
(v5.3.1)=
## [v5.3.1](https://github.com/jeertmans/manim-slides/compare/v5.3.0...v5.3.1)
@ -54,11 +97,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(v5.2.0-chore)=
### Chore
- Bump ManimGL to `>=1.7.1`, to remove conflicting dependencies
- Bumped ManimGL to `>=1.7.1`, to remove conflicting dependencies
with Manim's.
[#499](https://github.com/jeertmans/manim-slides/pull/499)
- Bump ManimGL to `>=1.7.2`, to remove `pyrr` from dependencies,
- Bumped ManimGL to `>=1.7.2`, to remove `pyrr` from dependencies,
and to avoid complex code for supporting both `1.7.1` and `>=1.7.2`,
as the latter includes many breaking changes.
[#506](https://github.com/jeertmans/manim-slides/pull/506)
@ -121,7 +164,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(v5.1.8-chore)=
### Chore
- Pin `rtoml==0.9.0` on Windows platforms,
- Pinned `rtoml==0.9.0` on Windows platforms,
see [#398](https://github.com/jeertmans/manim-slides/pull/398),
until
[samuelcolvin/rtoml#74](https://github.com/samuelcolvin/rtoml/issues/74)

View File

@ -26,7 +26,7 @@ keywords:
- PowerPoint
- Python
license: MIT
version: v5.3.1
version: v5.4.2
preferred-citation:
publisher:
name: The Open Journal

View File

@ -18,6 +18,8 @@ use, not the methods used internally when rendering.
next_section,
next_slide,
remove_from_canvas,
start_skip_animations,
stop_skip_animations,
wait_time_between_slides,
wipe,
zoom,

View File

@ -53,7 +53,7 @@ class ConvertExample(Slide):
self.next_slide()
code = Code(
code="""from manim import *
code_string="""from manim import *
class Example(Scene):
@ -72,7 +72,7 @@ class Example(Scene):
)
code_step_1 = Code(
code="""from manim import *
code_string="""from manim import *
from manim_slides import Slide
class Example(Scene):
@ -91,7 +91,7 @@ class Example(Scene):
)
code_step_2 = Code(
code="""from manim import *
code_string="""from manim import *
from manim_slides import Slide
class Example(Slide):
@ -110,7 +110,7 @@ class Example(Slide):
)
code_step_3 = Code(
code="""from manim import *
code_string="""from manim import *
from manim_slides import Slide
class Example(Slide):
@ -129,7 +129,7 @@ class Example(Slide):
)
code_step_4 = Code(
code="""from manim import *
code_string="""from manim import *
from manim_slides import Slide
class Example(Slide):
@ -148,19 +148,19 @@ class Example(Slide):
)
code_step_5 = Code(
code="manim-slide render example.py Example",
code_string="manim-slide render example.py Example",
language="console",
)
code_step_6 = Code(
code="manim-slides Example",
code_string="manim-slides Example",
language="console",
)
or_text = Text("or generate HTML presentation").scale(0.5)
code_step_7 = Code(
code="manim-slides convert Example slides.html --open",
code_string="manim-slides convert Example slides.html --open",
language="console",
).shift(DOWN)

View File

@ -1 +1 @@
__version__ = "5.3.1"
__version__ = "5.4.2"

View File

@ -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]:

View File

@ -49,6 +49,7 @@ class BaseSlide:
self._start_animation = 0
self._canvas: MutableMapping[str, Mobject] = {}
self._wait_time_between_slides = 0.0
self._skip_animations = False
@property
@abstractmethod
@ -277,7 +278,7 @@ class BaseSlide:
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
@ -299,6 +300,16 @@ class BaseSlide:
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.
.. seealso::
:meth:`start_skip_animations`
:meth:`stop_skip_animations`
:param loop:
If set, next slide will be looping.
:param auto_next:
@ -458,6 +469,9 @@ class BaseSlide:
self._current_slide += 1
if self._skip_animations:
base_slide_config.skip_animations = True
self._base_slide_config = base_slide_config
self._start_animation = self._current_animation
@ -521,9 +535,16 @@ class BaseSlide:
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}"
@ -560,6 +581,22 @@ class BaseSlide:
f"Slide '{scene_name}' configuration written in '{slide_path.absolute()}'"
)
def start_skip_animations(self) -> None:
"""
Start skipping animations.
This automatically applies ``skip_animations=True``
to all subsequent calls to :meth:`next_slide`.
This is useful when you want to skip animations from multiple slides in a row,
without having to manually set ``skip_animations=True``.
"""
self._skip_animations = True
def stop_skip_animations(self) -> None:
"""Stop skipping animations."""
self._skip_animations = False
def wipe(
self,
*args: Any,

View File

@ -31,6 +31,12 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
for the current slide config.
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
# OpenGL renderer disables 'write_to_movie' by default
# which is required for saving the animations
config["write_to_movie"] = True
super().__init__(*args, **kwargs)
@property
def _frame_shape(self) -> tuple[float, float]:
if isinstance(self.renderer, OpenGLRenderer):
@ -89,6 +95,15 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
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`.
@ -111,7 +126,12 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
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 | self._skip_animations,
**kwargs,
)
BaseSlide.next_slide.__wrapped__(
self,
base_slide_config=base_slide_config,

View File

@ -61,7 +61,7 @@ full = [
"manim-slides[magic,manim,sphinx-directive]",
]
magic = ["manim-slides[manim]", "ipython>=8.12.2"]
manim = ["manim>=0.17"]
manim = ["manim>=0.19"]
manimgl = ["manimgl>=1.7.2"]
pyqt6 = ["pyqt6>=6.7.0"]
pyqt6-full = ["manim-slides[full,pyqt6]"]
@ -91,7 +91,7 @@ Repository = "https://github.com/jeertmans/manim-slides"
allow_dirty = false
commit = true
commit_args = ""
current_version = "5.3.1"
current_version = "5.4.2"
ignore_missing_version = false
message = "chore(deps): bump version from {current_version} to {new_version}"
parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-rc(?P<release>\d+))?'
@ -137,13 +137,6 @@ replace = '''<!-- start changelog -->
## [Unreleased](https://github.com/jeertmans/manim-slides/compare/v{new_version}...HEAD)'''
search = "<!-- start changelog -->"
[[tool.bumpversion.files]]
filename = "uv.lock"
replace = '''name = "manim-slides"
version = "{new_version}"'''
search = '''name = "manim-slides"
version = "{current_version}"'''
[tool.codespell]
builtin = "clear,rare,informal,usage,names,en-GB_to_en-US"
check-hidden = true

View File

@ -86,6 +86,15 @@ class TestBaseSlide:
assert base_slide.wait_time_between_slides == 0.0
def test_skip_animations(self, base_slide: BaseSlide) -> None:
assert not base_slide._skip_animations
def test_start_and_stop_skip_animations(self, base_slide: BaseSlide) -> None:
base_slide.start_skip_animations()
assert base_slide._skip_animations
base_slide.stop_skip_animations()
assert not base_slide._skip_animations
def test_play(self) -> None:
pass # This method should be tested in test_slide.py

View File

@ -1,6 +1,10 @@
import contextlib
import os
import random
import shutil
import sys
import tempfile
from collections.abc import Iterator
from pathlib import Path
from typing import Any, Union
@ -17,6 +21,7 @@ from manim import (
Dot,
FadeIn,
GrowFromCenter,
Square,
Text,
)
from manim.renderer.opengl_renderer import OpenGLRenderer
@ -65,7 +70,9 @@ Slide = Union[CESlide, _GLSlide, CEGLSlide]
reason="See https://github.com/3b1b/manim/issues/2263.",
),
),
"--CE --renderer=opengl",
],
ids=("CE", "GL", "CE(GL)"),
)
def test_render_basic_slide(
renderer: str,
@ -78,7 +85,7 @@ def test_render_basic_slide(
with runner.isolated_filesystem() as tmp_dir:
shutil.copy(manimgl_config, tmp_dir)
results = runner.invoke(
render, [renderer, str(slides_file), "BasicSlide", "-ql"]
render, [*renderer.split(" "), str(slides_file), "BasicSlide", "-ql"]
)
assert results.exit_code == 0, results
@ -229,8 +236,22 @@ def assert_constructs(cls: SlideType) -> None:
init_slide(cls).construct()
@contextlib.contextmanager
def tmp_cwd() -> Iterator[str]:
cwd = os.getcwd()
tmp_dir = tempfile.mkdtemp()
os.chdir(tmp_dir)
try:
yield tmp_dir
finally:
os.chdir(cwd)
def assert_renders(cls: SlideType) -> None:
init_slide(cls).render()
with tmp_cwd():
init_slide(cls).render()
class TestSlide:
@ -479,6 +500,75 @@ class TestSlide:
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))
class Baz(CESlide):
def construct(self) -> None:
circle = Circle(color=BLUE)
self.play(GrowFromCenter(circle))
assert not self._base_slide_config.skip_animations
self.start_skip_animations()
self.next_slide()
square = Square(color=BLUE)
self.play(GrowFromCenter(square))
assert self._base_slide_config.skip_animations
self.next_slide()
assert self._base_slide_config.skip_animations
self.play(GrowFromCenter(square))
self.stop_skip_animations()
with tmp_cwd() as tmp_dir:
init_slide(Foo).render()
init_slide(Bar).render()
init_slide(Baz).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
slide_file = slides_folder / "Baz.json"
config = PresentationConfig.from_file(slide_file)
assert len(config.slides) == 1
def test_canvas(self) -> None:
@assert_constructs
class _(CESlide):

989
uv.lock generated

File diff suppressed because it is too large Load Diff