-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Refactored :meth:~.Scene.play
#1019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactored :meth:~.Scene.play
#1019
Conversation
Tests do not pass for Python 3.7. |
Fixed now. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only small nitpicks, this mostly looks good.
@eulertour So, I was worried that this could break the WebGL renderer. Is this ok? |
I'd greatly prefer if it didn't since I'll probably stop working on it for a while after this release. Can you just ensure it doesn't break anything obvious? |
So, I did that, the only thing I see that could break it is that now the animations are started in begin_animations, not in compile_animations_data anymore. I didn't test it though because I'm completly unfamiliar with it, but it should be good. |
It throws this error in case you can tell why that would happen:
|
@eulertour At first sight I've no idea. This didn't in theory touche the mobject processing. What was the scene ? EDIT : after testing locally, it seems that the mobject logic is broken. |
And on Windows there's a DirDeletedEvent saying that basic.py is a directory which seems strange... Edit: It also reports None issues, and I've tried reverting the raised exception from here to no avail. |
After further debugging, it seems the issue is with the attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some short feedback. The refactor seems fine otherwise.
@pytest.mark.skipif( | ||
sys.version_info < (3, 8), | ||
reason="Mock object has a different implementation in python 3.7, which makes it broken with this logic.", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unfamiliar with Mock, but if it's possible to configure this so that it runs on 3.7 (maybe using sys.version
?) then I would prefer to not skip tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will see what I can do.
To be honest, I didn't want to spend too much on investigating on this uncompataibilty, givent that this test is logically independent from the python version.
@@ -248,6 +248,7 @@ def __init__( | |||
self.duration = duration | |||
self.mobject = None | |||
self.stop_condition = stop_condition | |||
self.is_static_wait = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, but I think the name/phrase "static_wait" is misleading since a static
variable in programming (and Python more specifically) refers to a variable shared between instances of the class.
Here the name is based on whether the Animation has updaters, so a better name to use here/elsewhere in Scene.py
may be updaters
, has_updaters
, ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I agree with you, I chose this name because it is how this type of wait is called within the codenbase.
I don't really like has_udapters, because it's not really what this variable is supposed to express here.
I thinl is_frozen_wait
is fine, what do you think ?
# Save a static image, to avoid rendering non moving objects. | ||
self.static_image = self.save_static_frame_data(scene, scene.static_mobjects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Altogether, the adjective "static" isn't helpful here since an image is already understood to be non-moving media. A better adjective might be "stationary", or "frozen" so it's not confused with static (as in @staticmethod
) but it's not the image that is stationary/frozen, it's the Animation/the Mobjects in the Animation.
# Save a static image, to avoid rendering non moving objects. | |
self.static_image = self.save_static_frame_data(scene, scene.static_mobjects) | |
# Save a image, to avoid rendering non moving objects. | |
self.image = self.save_frame_data(scene, scene.static_mobjects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for the record, this code is not mine (I moved this piece from elsewhere to here), the term static_image
is braodly used to say that an image is static.
I believe this is not the right PR toc hange this, because it was not the main goal, but it's defintely an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just an issue of better variable names, but if it helps move things along quicker then I'm fine with doing this in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsonvillanueva Thanks for the review, I answered to your suggestions
@@ -248,6 +248,7 @@ def __init__( | |||
self.duration = duration | |||
self.mobject = None | |||
self.stop_condition = stop_condition | |||
self.is_static_wait = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I agree with you, I chose this name because it is how this type of wait is called within the codenbase.
I don't really like has_udapters, because it's not really what this variable is supposed to express here.
I thinl is_frozen_wait
is fine, what do you think ?
# Save a static image, to avoid rendering non moving objects. | ||
self.static_image = self.save_static_frame_data(scene, scene.static_mobjects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for the record, this code is not mine (I moved this piece from elsewhere to here), the term static_image
is braodly used to say that an image is static.
I believe this is not the right PR toc hange this, because it was not the main goal, but it's defintely an issue.
@pytest.mark.skipif( | ||
sys.version_info < (3, 8), | ||
reason="Mock object has a different implementation in python 3.7, which makes it broken with this logic.", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will see what I can do.
To be honest, I didn't want to spend too much on investigating on this uncompataibilty, givent that this test is logically independent from the python version.
I fear that this PR is made irrelevant with the new opengl_renderer, since the play logic is the same in open_gl renderer. and should be refactroed as well. As long as open_gl renderer does not have proper tests, I won't work on it. The tests are nevertheless addable, so I will make a separate PR. |
Not irrelevant at all, still very desirable to refactor the play logic in the cairo renderer I think. So, unless we are planning to stop all development on the cairo renderer, this should be merged. |
Thanks for the reply. I changed my mind, so I tried to put again the old stuff this refactor has deleted (the cairo renderer doesn't use it anymore), to keep the opengl renderer working. I tried a scene and it was working. Can someone confirm that it works ? PS : Why on earth the OpenGL renderer does not have tests ?? PPS: Happy to see that you're back, @leotrs |
We need ASAP before continuing development, I think.
|
@@ -60,7 +63,7 @@ def __init__(self, camera_class=None, skip_animations=False, **kwargs): | |||
self.file_writer = None | |||
camera_cls = camera_class if camera_class is not None else Camera | |||
self.camera = camera_cls() | |||
self.original_skipping_status = skip_animations | |||
self._original_skipping_status = skip_animations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either this needs to change or opengl_renderer.py needs to change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I'll modify it
Thanks to @jsonvillanueva it is now seemingly working for the openGL renderer! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying an OpenGL scene crashes for me:
manim-2 git:(refactored-play-logic) poetry run manim example_scenes/opengl.py SurfaceExample --use_opengl_renderer -p -ql
Manim Community v0.4.0
Traceback (most recent call last):
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ File "/home/devneal/github/manim-2/manim/__main__.py", line 84, in main │
│ 81 try: │
│ 82 renderer = OpenGLRenderer() │
│ 83 scene = SceneClass(renderer) │
│ ❱ 84 scene.render() │
│ 85 except Exception: │
│ 86 console.print_exception() │
│ 87 elif config["use_webgl_renderer"]: │
│ File "/home/devneal/github/manim-2/manim/scene/scene.py", line 177, in render │
│ 174 """ │
│ 175 self.setup() │
│ 176 try: │
│ ❱ 177 self.construct() │
│ 178 except EndSceneEarlyException: │
│ 179 pass │
│ 180 self.tear_down() │
│ File "/home/devneal/github/manim-2/example_scenes/opengl.py", line 99, in construct │
│ 96 │
│ 97 surface = surfaces[0] │
│ 98 │
│ ❱ 99 self.play( │
│ 100 FadeIn(surface), │
│ 101 ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3), │
│ 102 ) │
│ File "/home/devneal/github/manim-2/manim/scene/scene.py", line 789, in play │
│ 786 return np.max([animation.run_time for animation in animations]) │
│ 787 │
│ 788 def play(self, *args, **kwargs): │
│ ❱ 789 self.renderer.play(self, *args, **kwargs) │
│ 790 │
│ 791 def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None): │
│ 792 self.play(Wait(duration=duration, stop_condition=stop_condition)) │
│ File "/home/devneal/github/manim-2/manim/utils/caching.py", line 27, in wrapper │
│ 24 # method has to be deleted. │
│ 25 │
│ 26 def wrapper(self, scene, *args, **kwargs): │
│ ❱ 27 self.skip_animations = self.original_skipping_status │
│ 28 self.update_skipping_status() │
│ 29 animations = scene.compile_animations(*args, **kwargs) │
│ 30 scene.add_mobjects_from_animations(animations) │
╰──────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'OpenGLRenderer' object has no attribute 'original_skipping_status'
manim-2 git:(refactored-play-logic)
Co-authored-by: Devin Neal <devin@eulertour.com>
@eulertour the last commit I made was seemingly wrong. |
Motivation
Main motivation was to fix #874, and I ended but by refactoring everything.
The main goal was to refactor
scene.compile_animation_data
. This method did way too much, including starting the animations, determining the run time and the time progression, saving static frame, etc.Overview / Explanation for Changes
~.Scene.play
, in particular: caching logic and file writer logic are now included within :meth:~.Scene.play
(it wasn't possible before, becausescene.wait
andscene.play
were two different things).is_static_wait
attributes to Wait. (<=> if wait is a frozen frame).scene.add_static_frame
torenderer.freeze_current_frame
.ValueError
instead of just a warning.874
by modfyingrenderer.update_skipping_status
renderer
starts the animation withscene.begin_animations
(scene.compile_animation_data
used to do this)scene.play_internal
although it'd make more sense that renderer processes it later.scene.render
Oneline Summary of Changes
Testing Status
Tests run. Added new ones.
Further Comments
This has probably broken a little of the webgl renderer.
I let
scene.compile_animation_data
returns None or self in function of the play has some informations (if it's a static wait, it returns non, else it returns .. self). I would like to remove this logic, as it makes not really a sense that a such method returns any information.cc @eulertour
Acknowledgements
Reviewer Checklist