ManimCommunity / ManimCommunity/manim
`MObjects` dependent on `OpenGLSurface` behave differently in Cairo and OpenGL
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 40.9k
- Forks
- 3.1k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 25
Description
## Description of bug / unexpected behavior
When doing 3D animations with `Dot3D` or other 3D mobjects based on `OpenGLSurface` there are issues as `Surface` is a `VMObject` (vectorized) and `OpenGLSurface` is an `OpenGLMObject` (not vectorized). This can cause errors or unexpected behavior when running the same code in OpenGL (see code and log below).
## Expected behavior
`Dot3D` and other mobjects based on `Surface` and `OpenGLSurface` should behave the same in both Cairo and OpenGL.
## How to reproduce the issue
Code for reproducing the problem
```py
from manim import *
from manim.opengl import *
class Test(ThreeDScene):
def construct(self):
vect = Vector().to_edge(LEFT)
self.add(vect)
self.wait()
dot = Dot3D().to_edge(RIGHT)
self.add(dot)
self.wait()
self.play(Transform(vect, dot))
self.wait()
```
## Additional media files
Example of what Cairo outputs:
Images/GIFs
https://user-images.githubusercontent.com/79891353/137239258-e2a4203c-4494-4ca9-b689-95c55d16a598.mp4
## Logs
Terminal output
```
Manim Community v0.11.0
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/cli/render/commands.py:108 in render │
│ │
│ 105 │ │ │ while keep_running: │
│ 106 │ │ │ │ for SceneClass in scene_classes_from_file(file): │
│ 107 │ │ │ │ │ scene = SceneClass(renderer) │
│ ❱ 108 │ │ │ │ │ rerun = scene.render() │
│ 109 │ │ │ │ │ if rerun or config["write_all"]: │
│ 110 │ │ │ │ │ │ renderer.num_plays = 0 │
│ 111 │ │ │ │ │ │ continue │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/scene/scene.py:213 in render │
│ │
│ 210 │ │ """ │
│ 211 │ │ self.setup() │
│ 212 │ │ try: │
│ ❱ 213 │ │ │ self.construct() │
│ 214 │ │ except EndSceneEarlyException: │
│ 215 │ │ │ pass │
│ 216 │ │ except RerunSceneException as e: │
│ │
│ /home/alexlembcke/ManimDev/vector_translation.py:13 in construct │
│ │
│ 10 │ │ dot = Dot3D().to_edge(RIGHT) │
│ 11 │ │ self.add(dot) │
│ 12 │ │ self.wait() │
│ ❱ 13 │ │ self.play(Transform(vect, dot)) │
│ 14 │ │ self.wait() │
│ 15 │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/scene/scene.py:888 in play │
│ │
│ 885 │ │ │ return np.max([animation.run_time for animation in animations]) │
│ 886 │ │
│ 887 │ def play(self, *args, **kwargs): │
│ ❱ 888 │ │ self.renderer.play(self, *args, **kwargs) │
│ 889 │ │
│ 890 │ def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None): │
│ 891 │ │ self.play(Wait(run_time=duration, stop_condition=stop_condition)) │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/utils/caching.py:61 in wrapper │
│ │
│ 58 │ │ │ "List of the first few animation hashes of the scene: %(h)s", │
│ 59 │ │ │ {"h": str(self.animations_hashes[:5])}, │
│ 60 │ │ ) │
│ ❱ 61 │ │ func(self, scene, *args, **kwargs) │
│ 62 │ │
│ 63 │ return wrapper │
│ 64 │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/renderer/cairo_renderer.py:46 in │
│ wrapper │
│ │
│ 43 │ def wrapper(self, scene, *args, **kwargs): │
│ 44 │ │ self.animation_start_time = time.time() │
│ 45 │ │ self.file_writer.begin_animation(not self.skip_animations) │
│ ❱ 46 │ │ func(self, scene, *args, **kwargs) │
│ 47 │ │ self.file_writer.end_animation(not self.skip_animations) │
│ 48 │ │ self.num_plays += 1 │
│ 49 │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/renderer/opengl_renderer.py:406 in │
│ play │
│ │
│ 403 │ def play(self, scene, *args, **kwargs): │
│ 404 │ │ # TODO: Handle data locking / unlocking. │
│ 405 │ │ if scene.compile_animation_data(*args, **kwargs): │
│ ❱ 406 │ │ │ scene.begin_animations() │
│ 407 │ │ │ scene.play_internal() │
│ 408 │ │
│ 409 │ def clear_screen(self): │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/scene/scene.py:961 in │
│ begin_animations │
│ │
│ 958 │ def begin_animations(self) -> None: │
│ 959 │ │ """Start the animations of the scene.""" │
│ 960 │ │ for animation in self.animations: │
│ ❱ 961 │ │ │ animation.begin() │
│ 962 │ │
│ 963 │ def is_current_animation_frozen_frame(self) -> bool: │
│ 964 │ │ """Returns whether the current animation produces a static frame (generally a Wa │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/animation/transform.py:111 in begin │
│ │
│ 108 │ │ # Note, this potentially changes the structure │
│ 109 │ │ # of both mobject and target_mobject │
│ 110 │ │ if config["renderer"] == "opengl": │
│ ❱ 111 │ │ │ self.mobject.align_data_and_family(self.target_copy) │
│ 112 │ │ else: │
│ 113 │ │ │ self.mobject.align_data(self.target_copy) │
│ 114 │ │ super().begin() │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/mobject/opengl_mobject.py:1611 in │
│ align_data_and_family │
│ │
│ 1608 │ │
│ 1609 │ def align_data_and_family(self, mobject): │
│ 1610 │ │ self.align_family(mobject) │
│ ❱ 1611 │ │ self.align_data(mobject) │
│ 1612 │ │
│ 1613 │ def align_data(self, mobject): │
│ 1614 │ │ # In case any data arrays get resized when aligned to shader data │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/mobject/opengl_mobject.py:1619 in │
│ align_data │
│ │
│ 1616 │ │ for mob1, mob2 in zip(self.get_family(), mobject.get_family()): │
│ 1617 │ │ │ # Separate out how points are treated so that subclasses │
│ 1618 │ │ │ # can handle that case differently if they choose │
│ ❱ 1619 │ │ │ mob1.align_points(mob2) │
│ 1620 │ │ │ for key in mob1.data.keys() & mob2.data.keys(): │
│ 1621 │ │ │ │ if key == "points": │
│ 1622 │ │ │ │ │ continue │
│ │
│ /home/alexlembcke/.local/lib/python3.9/site-packages/manim/mobject/types/opengl_vectorized_mobje │
│ ct.py:856 in align_points │
│ │
│ 853 │ │ │ │ mob.start_new_path(mob.get_center()) │
│ 854 │ │ │ # If there's only one point, turn it into │
│ 855 │ │ │ # a null curve │
│ ❱ 856 │ │ │ if mob.has_new_path_started(): │
│ 857 │ │ │ │ mob.add_line_to(mob.points[0]) │
│ 858 │ │ │
│ 859 │ │ # Figure out what the subpaths are, and align │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'Dot3D' object has no attribute 'has_new_path_started'
```
## System specifications
System Details
- OS: Ubuntu 21.04
- RAM: 32GB
- Python version (`python/py/python3 --version`): 3.9.5
- Installed modules (provide output from `pip list`):
```
Package Version
----------------------- ---------------------
manim 0.10.0
moderngl 5.6.4
moderngl-window 2.4.0
```
LaTeX details
+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
FFMPEG
Output of `ffmpeg -version`:
```
ffmpeg version 4.3.2-0+deb11u1ubuntu1 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 10 (Ubuntu 10.2.1-20ubuntu1)
configuration: --prefix=/usr --extra-version=0+deb11u1ubuntu1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-nvenc --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
```
## Additional comments
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the Vector-to-Dot3D Transform example, comparing Cairo and OpenGL. Start with animation/transform.py and mobject/opengl_mobject.py at align_data_and_family, then trace the scene/scene.py animation path. Done means Dot3D and other Surface/OpenGLSurface-based mobjects behave consistently in both renderers without the reported error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100