ManimCommunity / manim

A community-maintained Python framework for creating mathematical animations.
https://www.manim.community
MIT License
19.99k stars 1.48k forks source link

Edges of Graph incorrectly hidden behind Image when idle #2304

Open vvolhejn opened 2 years ago

vvolhejn commented 2 years ago

Description of bug / unexpected behavior

First add an image to the scene. Then add a Graph (in the graph-theoretical sense) over it. The graph should be in front since it was added later. However, only the vertices are in front - the edges are not. When we animate the graph (e.g. move it around), the edges are in front as expected. As soon as the graph is not involved in an animation, the edges disappear again.

A hacky workaround is to involve the graph in every animation, e.g. by doing g.scale(1).

Expected behavior

The graph's edges should be in front of the image at all times.

How to reproduce the issue

Code for reproducing the problem ```py from manim import * class GraphImageBug(Scene): def construct(self): n = 256 imageArray = np.uint8([[i * 256 / n for i in range(0, n)] for _ in range(0, n)]) image = ImageMobject(imageArray).scale(2) vertices = [1, 2, 3, 4] edges = [(1, 2), (2, 3), (3, 4), (1, 3), (1, 4)] g = Graph(vertices, edges) self.add(image) self.add(g) self.wait() self.play( g[1].animate.move_to([1, 1, 0]), g[2].animate.move_to([-1, 1, 0]), g[3].animate.move_to([1, -1, 0]), g[4].animate.move_to([-1, -1, 0]), ) self.wait() self.play(g.animate.scale(3)) self.wait() ```

Additional media files

bug video

Logs

Terminal output ``` [16:01:51] ~/p/rubiks-cube-video> manim -ql -p graph_image_bug.py -v DEBUG Manim Community v0.12.0 [11/17/21 16:01:55] DEBUG Animation with empty mobject animation.py:165 DEBUG Hashing ... hashing.py:344 DEBUG Hashing done in 0.071008 s. hashing.py:356 DEBUG Hash generated : 3163782288_3493041802_919544174 hashing.py:359 DEBUG List of the first few animation hashes of the scene: ['3163782288_3493041802_919544174'] cairo_renderer.py:83 [11/17/21 16:01:56] INFO Animation 0 : Partial movie file written in '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_m scene_file_writer.py:510 ovie_files/GraphImageBug/3163782288_3493041802_919544174.mp4' DEBUG Hashing ... hashing.py:344 DEBUG Hashing done in 0.047561 s. hashing.py:356 DEBUG Hash generated : 2201830969_3362228145_1822438392 hashing.py:359 DEBUG List of the first few animation hashes of the scene: ['3163782288_3493041802_919544174', '2201830969_3362228145_1822438392'] cairo_renderer.py:83 INFO Animation 1 : Partial movie file written in '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_m scene_file_writer.py:510 ovie_files/GraphImageBug/2201830969_3362228145_1822438392.mp4' DEBUG Animation with empty mobject animation.py:165 DEBUG Hashing ... hashing.py:344 DEBUG Hashing done in 0.056231 s. hashing.py:356 DEBUG Hash generated : 2201830969_398514950_3400543663 hashing.py:359 DEBUG List of the first few animation hashes of the scene: ['3163782288_3493041802_919544174', '2201830969_3362228145_1822438392', cairo_renderer.py:83 '2201830969_398514950_3400543663'] INFO Animation 2 : Partial movie file written in '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_m scene_file_writer.py:510 ovie_files/GraphImageBug/2201830969_398514950_3400543663.mp4' DEBUG Hashing ... hashing.py:344 DEBUG Hashing done in 0.064292 s. hashing.py:356 DEBUG Hash generated : 2201830969_2942809912_3680414427 hashing.py:359 DEBUG List of the first few animation hashes of the scene: ['3163782288_3493041802_919544174', '2201830969_3362228145_1822438392', cairo_renderer.py:83 '2201830969_398514950_3400543663', '2201830969_2942809912_3680414427'] [11/17/21 16:01:57] INFO Animation 3 : Partial movie file written in '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_m scene_file_writer.py:510 ovie_files/GraphImageBug/2201830969_2942809912_3680414427.mp4' DEBUG Animation with empty mobject animation.py:165 DEBUG Hashing ... hashing.py:344 DEBUG Hashing done in 0.092485 s. hashing.py:356 DEBUG Hash generated : 2201830969_398514950_109819156 hashing.py:359 DEBUG List of the first few animation hashes of the scene: ['3163782288_3493041802_919544174', '2201830969_3362228145_1822438392', cairo_renderer.py:83 '2201830969_398514950_3400543663', '2201830969_2942809912_3680414427', '2201830969_398514950_109819156'] INFO Animation 4 : Partial movie file written in '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_m scene_file_writer.py:510 ovie_files/GraphImageBug/2201830969_398514950_109819156.mp4' INFO Combining to Movie file. scene_file_writer.py:604 DEBUG Partial movie files to combine (5 files): ['/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_mo scene_file_writer.py:547 vie_files/GraphImageBug/3163782288_3493041802_919544174.mp4', '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bu g/480p15/partial_movie_files/GraphImageBug/2201830969_3362228145_1822438392.mp4', '/Users/vaclav/prog/rubiks-cube-video/media/v ideos/graph_image_bug/480p15/partial_movie_files/GraphImageBug/2201830969_398514950_3400543663.mp4', '/Users/vaclav/prog/rubiks -cube-video/media/videos/graph_image_bug/480p15/partial_movie_files/GraphImageBug/2201830969_2942809912_3680414427.mp4', '/User s/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/partial_movie_files/GraphImageBug/2201830969_398514950_1098 19156.mp4'] INFO scene_file_writer.py:718 File ready at '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/GraphImageBug.mp4' INFO Rendered GraphImageBug scene.py:234 Played 5 animations INFO Previewed File at: '/Users/vaclav/prog/rubiks-cube-video/media/videos/graph_image_bug/480p15/GraphImageBug.mp4' file_ops.py:202 ```

System specifications

System Details - macOS 10.15.7 (Catalina) - RAM: 8GB - Python version: 3.9.7 - Installed modules (provide output from `pip list`): ``` Package Version Location ------------------------- --------- ------------------------ absl-py 0.13.0 appdirs 1.4.4 appnope 0.1.2 backcall 0.2.0 black 20.8b1 cachetools 4.2.2 certifi 2021.5.30 chardet 4.0.0 click 7.1.2 click-default-group 1.2.2 cloup 0.7.1 colorama 0.4.4 colour 0.1.5 commonmark 0.9.1 Cython 0.29.24 decorator 5.1.0 distlib 0.3.2 filelock 3.0.12 glcontext 2.3.4 google-auth 1.31.0 google-auth-oauthlib 0.4.4 grpcio 1.38.0 idna 2.10 ipython 7.22.0 ipython-genutils 0.2.0 isosurfaces 0.1.0 jedi 0.18.0 joblib 1.1.0 kociemba-manim-rubikscube 0.0.1 manim 0.12.0 manim-rubikscube 0.1.0 ManimPango 0.3.0 mapbox-earcut 0.12.10 Markdown 3.3.4 moderngl 5.6.4 moderngl-window 2.4.0 multipledispatch 0.6.0 mypy 0.812 mypy-extensions 0.4.3 networkx 2.6.3 numpy 1.20.2 oauthlib 3.1.1 packaging 20.9 pandocfilters 1.5.0 parso 0.8.2 pathspec 0.8.1 pexpect 4.8.0 pickleshare 0.7.5 Pillow 8.3.2 pip 21.2.4 prompt-toolkit 3.0.18 protobuf 3.17.3 ptyprocess 0.7.0 pyasn1 0.4.8 pyasn1-modules 0.2.8 pycairo 1.20.1 pydub 0.25.1 pygame 2.0.1 pyglet 1.5.21 Pygments 2.8.1 pyobjc-core 7.3 pyobjc-framework-Cocoa 7.3 pyparsing 2.4.7 PyQt6 6.1.0 PyQt6-3D 6.1.0 PyQt6-Charts 6.1.0 PyQt6-DataVisualization 6.1.0 PyQt6-NetworkAuth 6.1.0 PyQt6-sip 13.1.0 pyrr 0.10.3 regex 2021.3.17 requests 2.25.1 requests-oauthlib 1.3.0 rich 10.11.0 rsa 4.7.2 scikit-learn 1.0 scipy 1.7.1 screeninfo 0.6.7 setuptools 57.4.0 sip 6.1.1 six 1.16.0 skia-pathops 0.7.1 TBB 0.2 tensorboard 2.5.0 tensorboard-data-server 0.6.1 tensorboard-plugin-wit 1.8.0 termcolor 1.1.0 threadpoolctl 3.0.0 toml 0.10.2 tqdm 4.59.0 traitlets 5.0.5 typed-ast 1.4.2 typing-extensions 3.7.4.3 urllib3 1.26.5 virtualenv 20.4.7 watchdog 2.1.6 wcwidth 0.2.5 Werkzeug 2.0.1 wheel 0.37.0 ```
FFMPEG Output of `ffmpeg -version`: ``` ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers built with Apple clang version 12.0.0 (clang-1200.0.32.29) configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox libavutil 56. 70.100 / 56. 70.100 libavcodec 58.134.100 / 58.134.100 libavformat 58. 76.100 / 58. 76.100 libavdevice 58. 13.100 / 58. 13.100 libavfilter 7.110.100 / 7.110.100 libavresample 4. 0. 0 / 4. 0. 0 libswscale 5. 9.100 / 5. 9.100 libswresample 3. 9.100 / 3. 9.100 libpostproc 55. 9.100 / 55. 9.100 ```
behackl commented 2 years ago

This behavior is expected: the implementation sets the z_index of the edges to be -1 so that edges are behind the vertices. If you would like them to be before the image, then you can try setting the z_index of the image to something smaller than -1; e.g., image = ImageMobject(imageArray).scale(2).set_z_index(-5).

vvolhejn commented 2 years ago

Thank you for the explanation. I understand now the technical reason why this happens, but I think for the end user the behavior is certainly not expected. The graph is a single object, both semantically and in the sense of being one MObject. I can't think of a scenario where I would want the graph to behave in the way it does now, especially since the in-animation and out-of-animation behavior differs. For this reason, it makes sense to call this a bug.

Also, out of curiosity, could you please explain the technical reason why the edges jump to the front during the animation?

behackl commented 2 years ago

Oh, sure, I should have mentioned that I agree; this is not intuitive (and I see how me saying this is expected might suggest otherwise; I meant this is expected from the implementation's point of view).

I can remember thinking about this, but I can't recall the reason I decided for the current behavior. It might be worth trying to change it to see what happens.

As for your other question: the Cairo renderer has some peculiarities; this is one of them. I am not sure whether this is because the edges have update functions on them and the image has not, or because ImageMobjects have a sort of special role in the render loop, or because mobjects not involved in the currently playing animation are generally rendered in the background of the scene -- but I am pretty sure it is because of one of these three. :-)