Skip to content

Commit 6a96e2a

Browse files
committed
Remove Scene decorators and consolidate play() and wait()
1 parent 5d4dfcf commit 6a96e2a

File tree

2 files changed

+74
-158
lines changed

2 files changed

+74
-158
lines changed

manim/scene/js_scene.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ def progress_through_animations(self):
109109
logger.error(e)
110110
self.animation_finished.wait()
111111

112-
@scene.handle_play_like_call
113112
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
114113
self.update_mobjects(dt=0) # Any problems with this?
115114
self.animations = []

manim/scene/scene.py

Lines changed: 74 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,6 @@
2525
from ..utils.hashing import get_hash_from_play_call, get_hash_from_wait_call
2626

2727

28-
def handle_play_like_call(func):
29-
"""
30-
This method is used internally to wrap the
31-
passed function, into a function that
32-
actually writes to the video stream.
33-
Simultaneously, it also adds to the number
34-
of animations played.
35-
36-
Parameters
37-
----------
38-
func : function
39-
The play() like function that has to be
40-
written to the video file stream.
41-
42-
Returns
43-
-------
44-
function
45-
The play() like function that can now write
46-
to the video file stream.
47-
"""
48-
49-
def wrapper(self, *args, **kwargs):
50-
allow_write = not file_writer_config["skip_animations"]
51-
if not self.camera.use_js_renderer:
52-
self.file_writer.begin_animation(allow_write)
53-
func(self, *args, **kwargs)
54-
if not self.camera.use_js_renderer:
55-
self.file_writer.end_animation(allow_write)
56-
self.num_plays += 1
57-
58-
return wrapper
59-
60-
6128
class Scene(Container):
6229
"""A Scene is the canvas of your animation.
6330
@@ -832,82 +799,6 @@ def update_skipping_status(self):
832799
file_writer_config["skip_animations"] = True
833800
raise EndSceneEarlyException()
834801

835-
def handle_caching_play(func):
836-
"""
837-
Decorator that returns a wrapped version of func that will compute the hash of the play invocation.
838-
839-
The returned function will act according to the computed hash: either skip the animation because it's already cached, or let the invoked function play normally.
840-
841-
Parameters
842-
----------
843-
func : Callable[[...], None]
844-
The play like function that has to be written to the video file stream. Take the same parameters as `scene.play`.
845-
"""
846-
847-
def wrapper(self, *args, **kwargs):
848-
self.revert_to_original_skipping_status()
849-
self.update_skipping_status()
850-
animations = self.compile_play_args_to_animation_list(*args, **kwargs)
851-
self.add_mobjects_from_animations(animations)
852-
if file_writer_config["skip_animations"]:
853-
logger.debug(f"Skipping animation {self.num_plays}")
854-
func(self, *args, **kwargs)
855-
return
856-
if not file_writer_config["disable_caching"]:
857-
mobjects_on_scene = self.get_mobjects()
858-
hash_play = get_hash_from_play_call(
859-
self, self.camera, animations, mobjects_on_scene
860-
)
861-
self.play_hashes_list.append(hash_play)
862-
if self.file_writer.is_already_cached(hash_play):
863-
logger.info(
864-
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
865-
{"hash_play": hash_play},
866-
)
867-
file_writer_config["skip_animations"] = True
868-
else:
869-
hash_play = "uncached_{:05}".format(self.num_plays)
870-
self.play_hashes_list.append(hash_play)
871-
func(self, *args, **kwargs)
872-
873-
return wrapper
874-
875-
def handle_caching_wait(func):
876-
"""
877-
Decorator that returns a wrapped version of func that will compute the hash of the wait invocation.
878-
879-
The returned function will act according to the computed hash: either skip the animation because it's already cached, or let the invoked function play normally.
880-
881-
Parameters
882-
----------
883-
func : Callable[[...], None]
884-
The wait like function that has to be written to the video file stream. Take the same parameters as `scene.wait`.
885-
"""
886-
887-
def wrapper(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
888-
self.revert_to_original_skipping_status()
889-
self.update_skipping_status()
890-
if file_writer_config["skip_animations"]:
891-
logger.debug(f"Skipping wait {self.num_plays}")
892-
func(self, duration, stop_condition)
893-
return
894-
if not file_writer_config["disable_caching"]:
895-
hash_wait = get_hash_from_wait_call(
896-
self, self.camera, duration, stop_condition, self.get_mobjects()
897-
)
898-
self.play_hashes_list.append(hash_wait)
899-
if self.file_writer.is_already_cached(hash_wait):
900-
logger.info(
901-
f"Wait {self.num_plays} : Using cached data (hash : {hash_wait})"
902-
)
903-
file_writer_config["skip_animations"] = True
904-
else:
905-
hash_wait = "uncached_{:05}".format(self.num_plays)
906-
self.play_hashes_list.append(hash_wait)
907-
func(self, duration, stop_condition)
908-
909-
return wrapper
910-
911802
def begin_animations(self, animations):
912803
"""
913804
This method begins the list of animations that is passed,
@@ -972,9 +863,54 @@ def finish_animations(self, animations):
972863
else:
973864
self.update_mobjects(0)
974865

975-
@handle_caching_play
976-
@handle_play_like_call
866+
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
867+
self.play(duration=duration, stop_condition=stop_condition)
868+
977869
def play(self, *args, **kwargs):
870+
self.cached_play(*args, **kwargs)
871+
self.num_plays += 1
872+
873+
def cached_play(self, *args, **kwargs):
874+
self.revert_to_original_skipping_status()
875+
self.update_skipping_status()
876+
animations = self.compile_play_args_to_animation_list(*args, **kwargs)
877+
self.add_mobjects_from_animations(animations)
878+
if file_writer_config["skip_animations"]:
879+
logger.debug(f"Skipping animation {self.num_plays}")
880+
self.file_writer_wrapped_play(*args, **kwargs)
881+
return
882+
if not file_writer_config["disable_caching"]:
883+
mobjects_on_scene = self.get_mobjects()
884+
hash_play = get_hash_from_play_call(
885+
self, self.camera, animations, mobjects_on_scene
886+
)
887+
self.play_hashes_list.append(hash_play)
888+
if self.file_writer.is_already_cached(hash_play):
889+
logger.info(
890+
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
891+
{"hash_play": hash_play},
892+
)
893+
file_writer_config["skip_animations"] = True
894+
else:
895+
hash_play = "uncached_{:05}".format(self.num_plays)
896+
self.play_hashes_list.append(hash_play)
897+
self.file_writer_wrapped_play(*args, **kwargs)
898+
899+
def file_writer_wrapped_play(self, *args, **kwargs):
900+
allow_write = not file_writer_config["skip_animations"]
901+
self.file_writer.begin_animation(allow_write)
902+
903+
self.play_or_wait(*args, **kwargs)
904+
905+
self.file_writer.end_animation(allow_write)
906+
907+
def play_or_wait(self, *args, **kwargs):
908+
if "duration" in kwargs:
909+
self.wait_internal(**kwargs)
910+
else:
911+
self.play_internal(*args, **kwargs)
912+
913+
def play_internal(self, *args, **kwargs):
978914
"""
979915
This method is used to prep the animations for rendering,
980916
apply the arguments and parameters required to them,
@@ -1003,6 +939,33 @@ def play(self, *args, **kwargs):
1003939

1004940
self.finish_animations(self.animations)
1005941

942+
def wait_internal(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
943+
self.update_mobjects(dt=0) # Any problems with this?
944+
self.animations = []
945+
self.duration = duration
946+
self.stop_condition = stop_condition
947+
self.last_t = 0
948+
949+
if self.should_update_mobjects():
950+
time_progression = self.get_wait_time_progression(duration, stop_condition)
951+
# TODO, be smart about setting a static image
952+
# the same way Scene.play does
953+
for t in time_progression:
954+
self.update_animation_to_time(t)
955+
self.update_frame()
956+
self.add_frame(self.get_frame())
957+
if stop_condition is not None and stop_condition():
958+
time_progression.close()
959+
break
960+
elif self.skip_animations:
961+
# Do nothing
962+
return self
963+
else:
964+
self.update_frame()
965+
dt = 1 / self.camera.frame_rate
966+
self.add_frame(self.get_frame(), num_frames=int(duration / dt))
967+
return self
968+
1006969
def clean_up_animations(self, *animations):
1007970
"""
1008971
This method cleans up and removes from the
@@ -1073,52 +1036,6 @@ def get_wait_time_progression(self, duration, stop_condition):
10731036
time_progression.set_description("Waiting {}".format(self.num_plays))
10741037
return time_progression
10751038

1076-
@handle_caching_wait
1077-
@handle_play_like_call
1078-
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
1079-
"""
1080-
This method is used to wait, and do nothing to the scene, for some
1081-
duration.
1082-
Updaters stop updating, nothing happens.
1083-
1084-
Parameters
1085-
----------
1086-
duration : float or int, optional
1087-
The duration of wait time.
1088-
stop_condition :
1089-
A function that determines whether to stop waiting or not.
1090-
1091-
Returns
1092-
-------
1093-
Scene
1094-
The scene, after waiting.
1095-
"""
1096-
self.update_mobjects(dt=0) # Any problems with this?
1097-
self.animations = []
1098-
self.duration = duration
1099-
self.stop_condition = stop_condition
1100-
self.last_t = 0
1101-
1102-
if self.should_update_mobjects():
1103-
time_progression = self.get_wait_time_progression(duration, stop_condition)
1104-
# TODO, be smart about setting a static image
1105-
# the same way Scene.play does
1106-
for t in time_progression:
1107-
self.update_animation_to_time(t)
1108-
self.update_frame()
1109-
self.add_frame(self.get_frame())
1110-
if stop_condition is not None and stop_condition():
1111-
time_progression.close()
1112-
break
1113-
elif self.skip_animations:
1114-
# Do nothing
1115-
return self
1116-
else:
1117-
self.update_frame()
1118-
dt = 1 / self.camera.frame_rate
1119-
self.add_frame(self.get_frame(), num_frames=int(duration / dt))
1120-
return self
1121-
11221039
def wait_until(self, stop_condition, max_time=60):
11231040
"""
11241041
Like a wrapper for wait().

0 commit comments

Comments
 (0)