ManimCommunity / manim

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

Graph labels not working as expected when passing "vertex_config" #3662

Closed seanpden closed 3 months ago

seanpden commented 3 months ago

Description of bug / unexpected behavior

When creating a Graph object and setting the "labels" argument as "True", the labels render as expected. However, when you add any value to the "vertex_config" argument, the resulting render only uses the final value of the "vertices" argument and is not formatted correctly.

Expected behavior

The graph should render as normal, only making changes to the specified keys in the "vertex_config" argument.

How to reproduce the issue

Code for reproducing the problem ```py import manim as mn class KonigsbergBridgeWorking(mn.Scene): def construct(self): self.camera.background_color = mn.DARKER_GREY title_text = mn.Text(text="KonigsbergBridgeWorking", color=mn.WHITE).shift( mn.UP * 3.5 ) vertices: list = [0, 1, 2, 3] edges: list = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (2, 3), (3, 0)] graph = mn.Graph( vertices=vertices, edges=edges, labels=True, edge_type=mn.Line, edge_config=dict.fromkeys( edges, {"path_arc": mn.PI / 4, "color": mn.WHITE} ), ) self.play(mn.AddTextLetterByLetter(title_text)) self.play(mn.Create(graph)) self.wait(3) class KonigsbergBridgeNotWorking(mn.Scene): def construct(self): self.camera.background_color = mn.DARKER_GREY title_text = mn.Text(text="KonigsbergBridgeNotWorking", color=mn.WHITE).shift( mn.UP * 3.5 ) vertices: list = [0, 1, 2, 3] edges: list = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (2, 3), (3, 0)] graph = mn.Graph( vertices=vertices, edges=edges, labels=True, vertex_config=dict.fromkeys(vertices, {"color": mn.PINK, "radius": 0.1}), edge_type=mn.Line, edge_config=dict.fromkeys( edges, {"path_arc": mn.PI / 4, "color": mn.WHITE} ), ) self.play(mn.AddTextLetterByLetter(title_text)) self.play(mn.Create(graph)) self.wait(3) class KonigsbergBridgeNotWorkingAlt(mn.Scene): def construct(self): self.camera.background_color = mn.DARKER_GREY title_text = mn.Text( text="KonigsbergBridegNotWorkingAlt", color=mn.WHITE, font="JetBrainsMono NFM Medium", ).shift(mn.UP * 3.5) vertices: list = [0, 1, 2, 3] edges: list = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (2, 3), (3, 0)] labels = "0123" label_dict = {i: label for i, label in zip(vertices, labels)} print(label_dict) graph = mn.Graph( vertices=vertices, edges=edges, layout="circular", labels=label_dict, vertex_type=mn.LabeledDot, vertex_config=dict.fromkeys( vertices, {"color": mn.PINK}, # , "radius": 0.1} ), edge_type=mn.Line, edge_config=dict.fromkeys( filter(lambda edge: edge != (0, 2), edges), {"path_arc": mn.PI / 4, "color": mn.WHITE}, ) | {(0, 2): {"color": mn.WHITE}}, ) self.play(mn.AddTextLetterByLetter(title_text)) self.play(mn.Create(graph)) self.wait(3) class KonigsbergBridge(mn.Scene): def construct(self): self.camera.background_color = COLORS.base.hex title_text = mn.Text( text="Konigsberg Bridge", color=COLORS.text.hex, font="JetBrainsMono NFM Medium", ).shift(mn.UP * 3.5) vertices: list = [0, 1, 2, 3] edges: list = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (2, 3), (3, 0)] labels = "0123" label_dict = {i: label for i, label in zip(vertices, labels)} print(label_dict) graph = mn.Graph( vertices=vertices, edges=edges, layout="circular", labels=label_dict, vertex_type=mn.LabeledDot, vertex_config=dict.fromkeys( vertices, {"color": COLORS.pink.hex}, # , "radius": 0.1} ), edge_type=mn.Line, edge_config=dict.fromkeys( filter(lambda edge: edge != (0, 2), edges), {"path_arc": mn.PI / 4, "color": COLORS.text.hex}, ) | {(0, 2): {"color": COLORS.text.hex}}, ) self.play(mn.AddTextLetterByLetter(title_text)) self.play(mn.Create(graph)) self.wait(3) if __name__ == "__main__": scene = KonigsbergBridgeWorking() scene.render(True) scene = KonigsbergBridgeNotWorking() scene.render(True) scene = KonigsbergBridgeNotWorkingAlt() scene.render(True) ```

Additional media files

Images/GIFs Working: ![image](https://github.com/ManimCommunity/manim/assets/61769940/cfb4169d-54b0-453c-ad14-9497d126902b) Not Working: ![image](https://github.com/ManimCommunity/manim/assets/61769940/daed4bd3-3bb6-4ffd-bf67-42f66defd4de) Not Working Alternative: ![image](https://github.com/ManimCommunity/manim/assets/61769940/cfbdb585-2453-4dd7-b502-ca3926f0f591)

Logs

Terminal output ``` https://pastebin.com/8dDRJMNJ ```

System specifications

System Details - OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)): Windows 11 Enterprise 23H2 - RAM: 32GB - Python version (`python/py/python3 --version`): 3.12.2 - Installed modules (provide output from `pip list`): ``` catppuccin==2.2.0 certifi==2024.2.2 charset-normalizer==3.3.2 click==8.1.7 click-default-group==1.2.4 cloup==2.1.2 colorama==0.4.6 decorator==5.1.1 glcontext==2.5.0 idna==3.6 isosurfaces==0.1.0 manim==0.18.0 ManimPango==0.5.0 mapbox-earcut==1.0.1 markdown-it-py==3.0.0 mdurl==0.1.2 moderngl==5.10.0 moderngl-window==2.4.4 multipledispatch==1.0.0 networkx==3.2.1 numpy==1.26.4 Pillow==9.5.0 pycairo==1.26.0 pydub==0.25.1 pyglet==2.0.14 Pygments==2.17.2 pyrr==0.10.3 requests==2.31.0 rich==13.7.1 scipy==1.12.0 screeninfo==0.8.1 setuptools==69.2.0 skia-pathops==0.8.0.post1 srt==3.5.3 svgelements==1.9.6 tqdm==4.66.2 typing_extensions==4.10.0 urllib3==2.2.1 watchdog==3.0.0 ```
LaTeX details + LaTeX distribution (e.g. TeX Live 2020):MiKTeX Console 4.12 + Installed LaTeX packages: Mode Size Date Modified Name -a--- 32k 6 Jun 2021  amsfonts.tpm -a--- 1.8k 16 Jun 2023  amsmath.tpm -a--- 9.0k 20 Jan 06:44  arabi.tpm -a--- 1.0k 6 Jun 2021  atbegshi.tpm -a--- 1.1k 6 Jun 2021  atveryend.tpm -a--- 1.3k 6 Jun 2021  babel-english.tpm -a--- 51k 10 Feb 14:17  babel.tpm -a--- 1.0k 17 Mar 2023  biber-windows-x64.tpm -a--- 8.9k 24 Jan 2014  cm.tpm -a--- 3.4k 5 Feb 2020  dvips.tpm -a--- 1.2k 16 Feb 11:19  fontspec.tpm -a--- 1.2k 7 Feb 2022  iftex.tpm -a--- 1.9k 20 Feb 2021  knuth-lib.tpm -a--- 1.4k 17 Mar 05:01  l3backend.tpm -a--- 2.1k 17 Mar 05:02  l3kernel.tpm -a--- 1.6k 17 Mar 05:02  l3packages.tpm -a--- 1.3k 8 Mar 10:58  latex-firstaid.tpm -a--- 3.4k 30 Jun 2009  latex-fonts.tpm -a--- 2.7k 23 Nov 2023  latex-tools.tpm -a--- 6.4k 20 Jan 06:21  ltxbase.tpm -a--- 818 6 Jun 2021  luatex85.tpm -a--- 813 10 Mar 02:57  miktex-arctrl-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-asymptote-bin-x64-2.9.tpm -a--- 1.7k 10 Mar 02:57  miktex-autosp-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-axohelp-bin-x64-2.9.tpm -a--- 958 10 Mar 02:57  miktex-bibarts-bin-x64-2.9.tpm -a--- 845 10 Mar 02:57  miktex-bibtex-bin-x64-2.9.tpm -a--- 926 10 Mar 02:57  miktex-bibtex8bit-bin-x64-2.9.tpm -a--- 860 10 Mar 02:57  miktex-bzip2-bin-x64-2.9.tpm -a--- 1.8k 10 Mar 02:57  miktex-cairo-bin-x64-2.9.tpm -a--- 857 10 Mar 02:57  miktex-chktex-bin-x64-2.9.tpm -a--- 1.2k 10 Mar 02:57  miktex-cjkutils-bin-x64-2.9.tpm -a--- 2.9k 15 Apr 2023  miktex-config-2.9.tpm -a--- 908 10 Mar 02:57  miktex-console-bin-x64-2.9.tpm -a--- 1.8k 10 Mar 02:57  miktex-curl-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-cweb-bin-x64-2.9.tpm -a--- 875 10 Mar 02:57  miktex-devnag-bin-x64-2.9.tpm -a--- 857 10 Mar 02:57  miktex-dvicopy-bin-x64-2.9.tpm -a--- 900 10 Mar 02:57  miktex-dvipdfmx-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-dvipng-bin-x64-2.9.tpm -a--- 884 10 Mar 02:57  miktex-dvips-bin-x64-2.9.tpm -a--- 760 9 Apr 2017  miktex-dvips.tpm -a--- 1.4k 10 Mar 02:57  miktex-dvisvgm-bin-x64-2.9.tpm -a--- 860 10 Mar 02:57  miktex-epstopdf-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-expat-bin-x64-2.9.tpm -a--- 856 10 Mar 02:57  miktex-findtexmf-bin-x64-2.9.tpm -a--- 873 10 Mar 02:57  miktex-fmt-bin-x64-2.9.tpm -a--- 1.3k 10 Mar 02:57  miktex-fontconfig-bin-x64-2.9.tpm -a--- 749 4 Nov 2016  miktex-fontconfig.tpm -a--- 842 10 Mar 02:57  miktex-fonts-bin-x64-2.9.tpm -a--- 942 10 Mar 02:57  miktex-freeglut-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-freetype2-bin-x64-2.9.tpm -a--- 880 10 Mar 02:57  miktex-fribidixetex-bin-x64-2.9.tpm -a--- 1.3k 10 Mar 02:57  miktex-gd-bin-x64-2.9.tpm -a--- 1.4k 25 Sep 2018  miktex-ghostscript-bin-x64.tpm -a--- 1.3k 10 Mar 02:57  miktex-graphite2-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-gregorio-bin-x64-2.9.tpm -a--- 837 10 Mar 02:57  miktex-gsf2pk-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-harfbuzz-bin-x64-2.9.tpm -a--- 984 10 Mar 02:57  miktex-hitex-bin-x64.tpm -a--- 1.1k 10 Mar 02:57  miktex-hunspell-bin-x64-2.9.tpm -a--- 1.1k 28 Dec 2023  miktex-icu-bin-x64.tpm -a--- 890 10 Mar 02:57  miktex-jpeg-bin-x64-2.9.tpm -a--- 877 10 Mar 02:57  miktex-kpathsea-bin-x64-2.9.tpm -a--- 869 10 Mar 02:57  miktex-lacheck-bin-x64-2.9.tpm -a--- 1.4k 10 Mar 02:57  miktex-lcdf-typetools-bin-x64-2.9.tpm -a--- 1.0k 10 Mar 02:57  miktex-libressl-bin-x64-2.9.tpm -a--- 1.0k 10 Mar 02:57  miktex-log4cxx-bin-x64-2.9.tpm -a--- 1.5k 10 Mar 02:57  miktex-lua53-bin-x64-2.9.tpm -a--- 1.3k 10 Mar 02:57  miktex-luatex-bin-x64-2.9.tpm -a--- 833 10 Mar 02:57  miktex-lzma-bin-x64-2.9.tpm -a--- 1.0k 10 Mar 02:57  miktex-m-tx-bin-x64-2.9.tpm -a--- 896 10 Mar 02:57  miktex-makeindex-bin-x64-2.9.tpm -a--- 870 10 Mar 02:57  miktex-md5-bin-x64-2.9.tpm -a--- 854 10 Mar 02:57  miktex-metafont-bin-x64-2.9.tpm -a--- 847 10 Mar 02:57  miktex-metapost-bin-x64-2.9.tpm -a--- 932 10 Mar 02:57  miktex-mfware-bin-x64-2.9.tpm -a--- 1.6k 8 Mar 10:58  miktex-misc.tpm -a--- 1.1k 10 Mar 02:57  miktex-mktex-bin-x64-2.9.tpm -a--- 914 10 Mar 02:57  miktex-mo-bin-x64-2.9.tpm -a--- 891 10 Mar 02:57  miktex-mpmcli-bin-x64-2.9.tpm -a--- 928 10 Mar 02:57  miktex-mspack-bin-x64-2.9.tpm -a--- 838 10 Mar 02:57  miktex-mthelp-bin-x64-2.9.tpm -a--- 846 10 Mar 02:57  miktex-mtprint-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-omegaware-bin-x64-2.9.tpm -a--- 781 10 Mar 02:57  miktex-patgen-bin-x64.tpm -a--- 935 10 Mar 02:57  miktex-pdftex-bin-x64-2.9.tpm -a--- 837 10 Nov 2016  miktex-pdftex.tpm -a--- 1.0k 10 Mar 02:57  miktex-pixman-bin-x64-2.9.tpm -a--- 1.3k 10 Mar 02:57  miktex-pmx-bin-x64-2.9.tpm -a--- 978 10 Mar 02:57  miktex-png-bin-x64-2.9.tpm -a--- 1.5k 10 Mar 02:57  miktex-poppler-bin-x64-2.9.tpm -a--- 1.0k 10 Mar 02:57  miktex-popt-bin-x64-2.9.tpm -a--- 961 10 Mar 02:57  miktex-posix-bin-x64-2.9.tpm -a--- 829 10 Mar 02:57  miktex-ps2pk-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-psutils-bin-x64-2.9.tpm -a--- 1.1k 10 Mar 02:57  miktex-ptex-bin-x64.tpm -a--- 1.2k 27 Jan 08:47  miktex-qt6-bin-x64.tpm -a--- 1.9k 10 Mar 02:57  miktex-runtime-bin-x64-2.9.tpm -a--- 852 10 Mar 02:57  miktex-synctex-bin-x64-2.9.tpm -a--- 867 10 Mar 02:57  miktex-tdsutil-bin-x64.tpm -a--- 877 10 Mar 02:57  miktex-teckit-bin-x64-2.9.tpm -a--- 831 10 Mar 02:57  miktex-tex-bin-x64-2.9.tpm -a--- 864 10 Mar 02:57  miktex-tex2xindy-bin-x64-2.9.tpm -a--- 900 10 Mar 02:57  miktex-tex4ht-bin-x64-2.9.tpm -a--- 834 10 Mar 02:57  miktex-texify-bin-x64-2.9.tpm -a--- 947 10 Mar 02:57  miktex-texware-bin-x64-2.9.tpm -a--- 893 10 Mar 02:57  miktex-texworks-bin-x64-2.9.tpm -a--- 946 10 Mar 02:57  miktex-ttf2pk2-bin-x64-2.9.tpm -a--- 3.1k 5 Dec 2021  miktex-ucrt-bin-x64.tpm -a--- 837 10 Mar 02:57  miktex-upmendex-bin-x64.tpm -a--- 1.0k 10 Mar 02:57  miktex-uriparser-bin-x64-2.9.tpm -a--- 1.6k 5 Dec 2021  miktex-vc140-bin-x64.tpm -a--- 816 10 Mar 02:57  miktex-web-bin-x64-2.9.tpm -a--- 856 10 Mar 02:57  miktex-xetex-bin-x64-2.9.tpm -a--- 676 19 Jun 2015  miktex-xindy-bin-x64.tpm -a--- 871 10 Mar 02:57  miktex-xml2pmx-bin-x64.tpm -a--- 930 10 Mar 02:57  miktex-yap-bin-x64-2.9.tpm -a--- 1.1k 12 Jan 2018  miktex-zip-bin-x64.tpm -a--- 1.1k 10 Mar 02:57  miktex-zlib-bin-x64-2.9.tpm -a--- 876 10 Mar 02:57  miktex-zzip-bin-x64-2.9.tpm -a--- 1.4k 28 Jul 2023  pdftex.tpm -a--- 1.4k 20 Jan 06:22  preview.tpm -a--- 1.5k 17 Oct 2022  standalone.tpm -a--- 2.5k 3 Nov 2016  tetex.tpm -a--- 1.7k 23 Nov 2023  tex-ini-files.tpm -a--- 2.5k 22 Sep 2023  unicode-data.tpm -a--- 1.6k 26 Jun 2022  xkeyval.tpm
FFMPEG Output of `ffmpeg -version`: ``` ffmpeg version 6.1.1-full_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developers built with gcc 12.2.0 (Rev10, Built by MSYS2 project) configuration: --enable-gpl --enable-version3 --enable-static --pkg-config=pkgconf --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-dxva2 --enable-d3d11va --enable-libvpl --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint libavutil 58. 29.100 / 58. 29.100 libavcodec 60. 31.102 / 60. 31.102 libavformat 60. 16.100 / 60. 16.100 libavdevice 60. 3.100 / 60. 3.100 libavfilter 9. 12.100 / 9. 12.100 libswscale 7. 5.100 / 7. 5.100 libswresample 4. 12.100 / 4. 12.100 libpostproc 57. 3.100 / 57. 3.100 ```

Additional comments

behackl commented 3 months ago

That's a fun one -- and actually not really the fault of Graph; it's a consequence of the behavior of dict.fromkeys. You can check that this version (after slightly increasing the vertex radius) renders correctly:

import manim as mn
class KonigsbergBridgeNotWorking(mn.Scene):
    def construct(self):
        self.camera.background_color = mn.DARKER_GREY

        title_text = mn.Text(text="KonigsbergBridgeNotWorking", color=mn.WHITE).shift(
            mn.UP * 3.5
        )

        vertices: list = [0, 1, 2, 3]
        edges: list = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (2, 3), (3, 0)]

        graph = mn.Graph(
            vertices=vertices,
            edges=edges,
            labels=True,
            vertex_config={v: {"color": mn.PINK, "radius": 0.3} for v in vertices},
            edge_type=mn.Line,
            edge_config={e: {"path_arc": mn.PI / 4, "color": mn.WHITE} for e in edges},
        )

        self.play(mn.AddTextLetterByLetter(title_text))

        self.play(mn.Create(graph))

        self.wait(3)

The issue with dict.fromkeys is that if you use a dictionary (as a complex data type) as a value, the separate values will actually just be shallow copies of each other; they reference the same dict under the hood. Here is a plain example illustrating this behavior:

>>> conf = {'x': 1, 'y': 2}
>>> dct = dict.fromkeys([7, 8], conf); dct
{7: {'x': 1, 'y': 2}, 8: {'x': 1, 'y': 2}}
>>> dct[7]['x'] = 42
>>> dct
{7: {'x': 42, 'y': 2}, 8: {'x': 42, 'y': 2}}

The Graph mobject doesn't have any safeguards to fix this internally, the identical config dicts appear to mess things up in a weird way. You can check that by printing the value of graph._vertex_config; its entries will all be the exact same dict.

seanpden commented 3 months ago

Wow, just looked at the docs and your example - you're totally right. You learn something new every day. With the fault being dict.fromkeys passing a reference to the same instance, I'd wager that it's not worth it to put a check in the Graph constuctor.

Thanks for the help! You think this one can be closed out?