Skip to content

Commit 4e6af02

Browse files
authored
Experimental: fix 11 tests (#3979)
* Fix tests and verify Animation run time inside new run_time property setter * test_boolean_ops * Add run_time validation from main branch * Fix 2 tests in tests/module/mobject/mobject/test_mobject.py * Fix 2 tests in tests/module/mobject/text/test_text_mobject.py * Manager in test_file_writer.py
1 parent 7631878 commit 4e6af02

File tree

12 files changed

+82
-45
lines changed

12 files changed

+82
-45
lines changed

manim/animation/animation.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
1414

15-
from .. import logger
15+
from .. import config, logger
1616
from ..mobject import mobject
1717
from ..mobject.mobject import Mobject
1818
from ..mobject.opengl import opengl_mobject
@@ -209,6 +209,7 @@ def begin(self) -> None:
209209
method.
210210
211211
"""
212+
self.run_time = validate_run_time(self.run_time, str(self))
212213
self.starting_mobject = self.create_starting_mobject()
213214
if self.suspend_mobject_updating:
214215
# All calls to self.mobject's internal updaters
@@ -551,6 +552,33 @@ def prepare_animation(
551552
raise TypeError(f"Object {anim} cannot be converted to an animation") from None
552553

553554

555+
def validate_run_time(
556+
run_time: float, caller_name: str, parameter_name: str = "run_time"
557+
) -> float:
558+
if run_time <= 0:
559+
raise ValueError(
560+
f"{caller_name} has a {parameter_name} of {run_time:g} <= 0 "
561+
f"seconds which Manim cannot render. Please set the "
562+
f"{parameter_name} to a positive number."
563+
)
564+
565+
# config.frame_rate holds the number of frames per second
566+
fps = config.frame_rate
567+
seconds_per_frame = 1 / fps
568+
if run_time < seconds_per_frame:
569+
logger.warning(
570+
f"The original {parameter_name} of {caller_name}, {run_time:g} "
571+
f"seconds, is too short for the current frame rate of {fps:g} "
572+
f"FPS. Rendering with the shortest possible {parameter_name} of "
573+
f"{seconds_per_frame:g} seconds instead."
574+
)
575+
new_run_time = seconds_per_frame
576+
else:
577+
new_run_time = run_time
578+
579+
return new_run_time
580+
581+
554582
class Wait(Animation):
555583
"""A "no operation" animation.
556584
@@ -590,7 +618,7 @@ def __init__(
590618
super().__init__(None, run_time=run_time, rate_func=rate_func, **kwargs)
591619

592620
def begin(self) -> None:
593-
pass
621+
self.run_time = validate_run_time(self.run_time, str(self))
594622

595623
def finish(self) -> None:
596624
pass

manim/animation/composition.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy as np
99

1010
from manim._config import config
11-
from manim.animation.animation import Animation, prepare_animation
11+
from manim.animation.animation import Animation, prepare_animation, validate_run_time
1212
from manim.constants import RendererType
1313
from manim.mobject.mobject import Group, Mobject
1414
from manim.mobject.opengl.opengl_mobject import OpenGLGroup
@@ -90,6 +90,7 @@ def begin(self) -> None:
9090
anim.begin()
9191
self.process_subanimation_buffer(anim.buffer)
9292

93+
self.run_time = validate_run_time(self.run_time, str(self))
9394
self.anim_group_time = 0.0
9495
if self.suspend_mobject_updating:
9596
self.group.suspend_updating()
@@ -228,6 +229,7 @@ def begin(self) -> None:
228229
f"Trying to play {self} without animations, this is not supported. "
229230
"Please add at least one subanimation."
230231
)
232+
self.run_time = validate_run_time(self.run_time, str(self))
231233
self.update_active_animation(0)
232234

233235
def finish(self) -> None:

manim/mobject/opengl/opengl_vectorized_mobject.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,13 @@ def get_grid(self, *args, **kwargs) -> OpenGLVGroup: # type: ignore
142142
def __getitem__(self, value: int | slice) -> Self: # type: ignore
143143
return super().__getitem__(value) # type: ignore
144144

145-
def add(self, *vmobjects: OpenGLVMobject): # type: ignore
145+
def add(self, *vmobjects: OpenGLVMobject) -> Self: # type: ignore
146146
if not all(isinstance(m, OpenGLVMobject) for m in vmobjects):
147147
raise Exception("All submobjects must be of type OpenGLVMobject")
148-
super().add(*vmobjects)
148+
return super().add(*vmobjects)
149149

150150
# Colors
151-
def init_colors(self):
151+
def init_colors(self) -> Self:
152152
# self.set_fill(
153153
# color=self.fill_color or self.color,
154154
# opacity=self.fill_opacity,

manim/mobject/text/text_mobject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1236,7 +1236,7 @@ def __init__(
12361236
else:
12371237
self.line_spacing = self._font_size + self._font_size * self.line_spacing
12381238

1239-
color: ManimColor = ManimColor(color) if color else VMobject().color
1239+
color: ManimColor = ManimColor(color) if color else OpenGLVMobject().color
12401240
file_name = self._text2svg(color)
12411241

12421242
PangoUtils.remove_last_M(file_name)

manim/scene/scene.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from typing_extensions import assert_never
1111

1212
from manim import config, logger
13-
from manim.animation.animation import prepare_animation
13+
from manim.animation.animation import prepare_animation, validate_run_time
1414
from manim.animation.scene_buffer import SceneBuffer, SceneOperation
1515
from manim.camera.camera import Camera
1616
from manim.constants import DEFAULT_WAIT_TIME
@@ -380,6 +380,7 @@ def wait(
380380
note: str | None = None,
381381
ignore_presenter_mode: bool = False,
382382
):
383+
duration = validate_run_time(duration, str(self) + ".wait()", "duration")
383384
self.manager._wait(duration, stop_condition=stop_condition)
384385
# if (
385386
# self.presenter_mode
@@ -391,6 +392,7 @@ def wait(
391392
# self.hold_loop()
392393

393394
def wait_until(self, stop_condition: Callable[[], bool], max_time: float = 60):
395+
max_time = validate_run_time(max_time, str(self) + ".wait_until()", "max_time")
394396
self.wait(max_time, stop_condition=stop_condition)
395397

396398
def add_sound(

manim/scene/vector_space_scene.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from manim.mobject.geometry.polygram import Rectangle
1414
from manim.mobject.graphing.coordinate_systems import Axes, NumberPlane
1515
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
16+
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
1617
from manim.mobject.text.tex_mobject import MathTex, Tex
1718
from manim.utils.config_ops import update_dict_recursively
1819

@@ -1002,7 +1003,7 @@ def get_piece_movement(self, pieces: list | tuple | np.ndarray):
10021003
Animation
10031004
The animation of the movement.
10041005
"""
1005-
v_pieces = [piece for piece in pieces if isinstance(piece, VMobject)]
1006+
v_pieces = [piece for piece in pieces if isinstance(piece, OpenGLVMobject)]
10061007
start = VGroup(*v_pieces)
10071008
target = VGroup(*(mob.target for mob in v_pieces))
10081009

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
from manim import manim_colors as col
22
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
33

4-
VMobject = OpenGLVMobject
5-
64

75
def test_vmobject_init():
8-
vm = VMobject(col.RED)
6+
vm = OpenGLVMobject()
7+
assert vm.fill_color == [col.WHITE]
8+
assert vm.stroke_color == [col.WHITE]
9+
vm = OpenGLVMobject(color=col.RED)
910
assert vm.fill_color == [col.RED]
1011
assert vm.stroke_color == [col.RED]
11-
vm = VMobject(col.GREEN, stroke_color=col.YELLOW)
12+
vm = OpenGLVMobject(fill_color=col.GREEN, stroke_color=col.YELLOW)
1213
assert vm.fill_color == [col.GREEN]
1314
assert vm.stroke_color == [col.YELLOW]
14-
vm = VMobject()
15-
assert vm.fill_color == [col.WHITE]
16-
assert vm.stroke_color == [col.WHITE]

tests/module/animation/test_animation.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,21 @@
1212
def test_animation_forbidden_run_time(run_time):
1313
manager = Manager(Scene)
1414
test_scene = manager.scene
15-
with pytest.raises(ValueError, match="Please set the run_time to be positive"):
15+
with pytest.raises(
16+
ValueError, match="Please set the run_time to a positive number."
17+
):
1618
test_scene.play(FadeIn(None, run_time=run_time))
1719

1820

1921
def test_animation_run_time_shorter_than_frame_rate(manim_caplog, config):
2022
manager = Manager(Scene)
2123
test_scene = manager.scene
2224
test_scene.play(FadeIn(None, run_time=1 / (config.frame_rate + 1)))
23-
assert (
24-
"Original run time of FadeIn(Mobject) is shorter than current frame rate"
25-
in manim_caplog.text
26-
)
25+
assert "too short for the current frame rate" in manim_caplog.text
2726

2827

29-
@pytest.mark.parametrize("frozen_frame", [False, True])
30-
def test_wait_run_time_shorter_than_frame_rate(manim_caplog, frozen_frame):
28+
def test_wait_run_time_shorter_than_frame_rate(manim_caplog):
3129
manager = Manager(Scene)
3230
test_scene = manager.scene
33-
test_scene.wait(1e-9, frozen_frame=frozen_frame)
34-
assert (
35-
"Original run time of Wait(Mobject) is shorter than current frame rate"
36-
in manim_caplog.text
37-
)
31+
test_scene.wait(1e-9)
32+
assert "too short for the current frame rate" in manim_caplog.text

tests/module/mobject/mobject/test_mobject.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,15 @@ def test_mobject_dimensions_mobjects_with_no_points_are_at_origin():
144144
assert outer_group.width == 2
145145
assert outer_group.height == 3
146146

147-
# Adding a mobject with no points has a quirk of adding a "point"
148-
# to [0, 0, 0] (the origin). This changes the size of the outer
149-
# group because now the bottom left corner is at [-5, -6.5, 0]
150-
# but the upper right corner is [0, 0, 0] instead of [-3, -3.5, 0]
147+
# TODO: remove the following 8 lines?
148+
# Originally, adding a mobject with no points had a quirk of adding a
149+
# "point" to [0, 0, 0] (the origin). This changed the size of the outer
150+
# group, because the bottom was corner is at [-5, -6.5, 0], but the
151+
# upper right corner became [0, 0, 0] instead of [-3, -3.5, 0].
152+
# However, this no longer happens.
151153
outer_group.add(VGroup())
152-
assert outer_group.width == 5
153-
assert outer_group.height == 6.5
154+
assert outer_group.width == 2
155+
assert outer_group.height == 3
154156

155157

156158
def test_mobject_dimensions_has_points_and_children():

tests/module/mobject/test_boolean_ops.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
from __future__ import annotations
22

3+
from typing import TYPE_CHECKING
4+
35
import numpy as np
46
import pytest
57

68
from manim import Circle, Square
79
from manim.mobject.geometry.boolean_ops import _BooleanOps
810

11+
if TYPE_CHECKING:
12+
from manim.mobject.types.vectorized_mobject import VMobject
13+
from manim.typing import Point2D_Array, Point3D_Array
14+
915

1016
@pytest.mark.parametrize(
1117
("test_input", "expected"),
@@ -25,15 +31,17 @@
2531
),
2632
],
2733
)
28-
def test_convert_2d_to_3d_array(test_input, expected):
34+
def test_convert_2d_to_3d_array(
35+
test_input: Point2D_Array, expected: Point3D_Array
36+
) -> None:
2937
a = _BooleanOps()
3038
result = a._convert_2d_to_3d_array(test_input)
3139
assert len(result) == len(expected)
3240
for i in range(len(result)):
3341
assert (result[i] == expected[i]).all()
3442

3543

36-
def test_convert_2d_to_3d_array_zdim():
44+
def test_convert_2d_to_3d_array_zdim() -> None:
3745
a = _BooleanOps()
3846
result = a._convert_2d_to_3d_array([(1.0, 2.0)], z_dim=1.0)
3947
assert (result[0] == np.array([1.0, 2.0, 1.0])).all()
@@ -48,11 +56,12 @@ def test_convert_2d_to_3d_array_zdim():
4856
Circle(radius=3),
4957
],
5058
)
51-
def test_vmobject_to_skia_path_and_inverse(test_input):
59+
def test_vmobject_to_skia_path_and_inverse(test_input: VMobject) -> None:
5260
a = _BooleanOps()
5361
path = a._convert_vmobject_to_skia_path(test_input)
5462
assert len(list(path.segments)) > 1
5563

5664
new_vmobject = a._convert_skia_path_to_vmobject(path)
57-
# for some reason there is an extra 4 points in new vmobject than original
58-
np.testing.assert_allclose(new_vmobject.points[:-4], test_input.points)
65+
# For some reason, there are 3 more points in the new VMobject than in the
66+
# original input.
67+
np.testing.assert_allclose(new_vmobject.points[:-3], test_input.points)

0 commit comments

Comments
 (0)