ManimCommunity / manim

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

MovingCamera rotation squishes objects instead of rotating #3373

Open meawplex-plus opened 1 year ago

meawplex-plus commented 1 year ago

Description of bug / unexpected behavior

When I apply the animation self.camera.frame.animate.rotate(PI/2) to the scene and compile the animation with manim scene.py -pqh myScene, all mobjects (I used a single Square mobject with default properties) are squished instead of rotated.

Expected behavior

I expected the camera to rotate by PI/2 radians (90 degrees) without squishing.

How to reproduce the issue

Code for reproducing the problem Add to scene.py: ```py class CameraIssue(MovingCameraScene): def construct(self): self.add(Square()) self.play(self.camera.frame.animate.rotate(PI/2)) ``` Compile with `manim scene.py -pqh CameraIssue`.

Additional media files

Images/GIFs https://github.com/ManimCommunity/manim/assets/62037395/5ef2d2ef-53c8-4008-8e4f-76f8578f2e6a

Logs

Terminal output ``` Manim Community v0.17.3 [09/20/23 15:53:42] DEBUG Hashing ... hashing.py:350 DEBUG Hashing done in 0.003948 s. hashing.py:362 DEBUG Hash generated : 1433715038_3210132319_2428177325 hashing.py:365 DEBUG List of the first few animation hashes of the cairo_renderer.py:87 scene: ['1433715038_3210132319_2428177325'] [09/20/23 15:53:43] INFO Animation 0 : Partial movie file written in scene_file_writer.py:527 '/my/path/media/videos/scene/1080p60/partial_movie_fi les/CameraIssue/1433715038_3210132319_2428177 325.mp4' INFO Combining to Movie file. scene_file_writer.py:617 DEBUG Partial movie files to combine (1 files): scene_file_writer.py:561 ['/my/path/media/videos/scene/1080p60/partial_movie_f iles/CameraIssue/1433715038_3210132319_242817 7325.mp4'] INFO scene_file_writer.py:736 File ready at '/my/path/media/videos/scene/1080p60/CameraIssue.mp4' INFO Rendered CameraIssue scene.py:241 Played 1 animations INFO Previewed File at: file_ops.py:227 '/my/path/media/v ideos/scene/1080p60/CameraIssue.mp4' ```

System specifications

System Details - OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)): macOS 13.4 (Ventura) - RAM: 16GB - Python version (`python/py/python3 --version`): 3.11.4 - Installed modules (provide output from `pip list`): ``` Package Version ---------------------- --------- certifi 2022.9.24 chardet 3.0.4 charset-normalizer 3.2.0 click 8.1.7 click-default-group 1.2.4 cloup 0.13.1 colour 0.1.5 Cython 3.0.2 decorator 5.1.1 glcontext 2.4.0 googletrans 3.0.0 h11 0.9.0 h2 3.2.0 hpack 3.0.0 hstspreload 2022.12.1 httpcore 0.9.1 httpx 0.13.3 hyperframe 5.2.0 idna 2.10 isosurfaces 0.1.0 manim 0.17.3 ManimPango 0.4.3 mapbox-earcut 1.0.1 markdown-it-py 3.0.0 mdurl 0.1.2 moderngl 5.8.2 moderngl-window 2.4.4 multipledispatch 1.0.0 networkx 2.8.8 numpy 1.25.2 Pillow 9.5.0 pip 23.2.1 pycairo 1.24.0 pydub 0.25.1 pygame 2.5.1 pyglet 2.0.9 Pygments 2.16.1 pyobjc-core 9.2 pyobjc-framework-Cocoa 9.2 pyrr 0.10.3 requests 2.31.0 rfc3986 1.5.0 rich 13.5.2 scipy 1.11.2 screeninfo 0.8.1 setuptools 65.5.0 skia-pathops 0.7.4 sniffio 1.3.0 srt 3.5.3 svgelements 1.9.6 tqdm 4.66.1 urllib3 2.0.4 watchdog 2.3.1 ```
LaTeX details + LaTeX distribution (e.g. TeX Live 2020): MacTeX + Installed LaTeX packages: Irrelevant; did not use LaTeX
FFMPEG Output of `ffmpeg -version`: ``` ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers built with Apple clang version 14.0.3 (clang-1403.0.22.14.1) configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.0_1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --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-videotoolbox --enable-audiotoolbox --enable-neon libavutil 58. 2.100 / 58. 2.100 libavcodec 60. 3.100 / 60. 3.100 libavformat 60. 3.100 / 60. 3.100 libavdevice 60. 1.100 / 60. 1.100 libavfilter 9. 3.100 / 9. 3.100 libswscale 7. 1.100 / 7. 1.100 libswresample 4. 10.100 / 4. 10.100 libpostproc 57. 1.100 / 57. 1.100 ```

Additional comments

I have found that this issue persists without -qh in the command line arguments.

DDQXZcp commented 1 year ago

I also encountered this issue. If rotating two squares using self.camera.frame.animate.rotate, they are squished simultaneously.

Grouping all the objects and rotating works, but it is a bit tedious.

How to reproduce the issue

Code for reproducing the problem ```py %%manim -qm CircleToSquare class CircleToSquare(MovingCameraScene): def construct(self): green_square = Square(color=GREEN, fill_opacity=0.8).move_to(2 * LEFT) red_square = Square(color=RED, fill_opacity=0.8).move_to(2 * RIGHT) self.play(Create(green_square)) self.play(Create(red_square)) self.wait() # Working all_objects = VGroup(green_square, red_square) self.play(all_objects.animate.rotate(PI/2)) # Rotate all objects by 90 degrees self.wait() # Not Working self.play(self.camera.frame.animate.rotate(PI*1/2)) self.wait() self.play(self.camera.frame.animate.rotate(PI*1/2)) self.wait() # Not Working self.play(Rotate(self.camera.frame, angle=2*PI, rate_func=linear),) self.wait() ```

Additional media files

Images/GIFs https://github.com/ManimCommunity/manim/assets/28733722/70e574ac-5745-4bed-a860-c928b7815c80
wengkeii commented 1 year ago

Hi, could I please be assigned to work on this issue?

I think that the unexpected behavior is caused by the default perspective projection of the camera, which distort objects when the camera is rotated.

I have been able to work around this issue using the following code as an example:

from manim import *

class CameraIssue(MovingCameraScene):
    def construct(self):
        square = Square()
        self.play(Create(square))
        self.wait(2)

        all_objects = VGroup(square)
        self.play(all_objects.animate.rotate(PI/2))
        self.wait(2)

In this code, I create a VGroup to group the objects and then apply the rotation to that group. This works as expected and rotates the objects without distortion.

However, I do acknowledge that this does not solve the issue from its root and this issue could possibly be caused by the frame rotation of the camera not being fully implemented yet as mentioned.

DDQXZcp commented 1 year ago

I am also trying to fix it. I think the frame rotation is not yet implemented I think? I found a TODO tag here moving_camera.py line 57. This is what the structure looks like in my mind. I think the frame rotation should be independent of object rotation. COMP6120 Manim drawio

wizard-com commented 1 year ago

class CameraSolution(MovingCameraScene):

def construct(self):
    s1 = Square()
    s2 = Square()
    s1.set_fill(RED, opacity=0.5).shift(2 * LEFT)
    s2.set_fill(GREEN, opacity=0.5).shift(2 * RIGHT)
    self.add(s1)
    self.add(s2)
    self.rotate_frame(PI / 2)

def rotate_frame(self, angle):
    all_objs = [
        obj for obj in self.mobjects if not isinstance(obj, ScreenRectangle)
    ]
    group = Group(*all_objs).set_x(0).arrange(buff=1.0)
    self.add(group)
    self.play(group.animate.rotate(angle))

This is what I have tried so far. It works for n objects but the code only works in Scene

meawplex-plus commented 1 year ago

Thank you, all. I had no idea that frame rotation was unfinished. I will look into the VGrouping/Custom self.camera.frame.animate.rotate methods.

DDQXZcp commented 1 year ago

I have some idea of what is going wrong. The video is a standard 16:9 rectangle, as well as the frame. If we rotate the frame, the frame height and width change (this is not expected, rotating the view should not affect height and width). Then the frame is auto-zoom to fit the video.

Here I rotate the frame by 1/4*pi, and the frame is expanded from (8.0, 14.222222222222221) to (15.713484026367722, 15.713484026367723). That is 16:9 -> 1:1. Then imagine a square frame is compressed back into a flat frame. That explains why the objects are squished vertically.

How to reproduce the issue

Code for reproducing the problem ```py %%manim -qm CircleToSquare class CircleToSquare(MovingCameraScene): def construct(self): green_square = Square(color=GREEN, fill_opacity=0.8).move_to(2 * LEFT) red_square = Square(color=RED, fill_opacity=0.8).move_to(2 * RIGHT) self.play(Create(green_square)) self.play(Create(red_square)) self.wait() # Check frame shape print(self.camera.frame.height) print(self.camera.frame.width) # Rotate frame self.play(self.camera.frame.animate.rotate(PI*1/4)) self.wait() # Check frame shape print(self.camera.frame.height) print(self.camera.frame.width) ```

It prints out

8.0
14.222222222222221
15.713484026367722
15.713484026367723
DDQXZcp commented 1 year ago

The camera frame rotation is working perfectly in 3D Scene. No manual grouping is needed. The animation is exactly the same as 2D.

Code for Alternative Solution

Code for Alternative Solution ```py %%manim -qm My3DScene class My3DScene(ThreeDScene): def construct(self): # Create a rectangle green_square = Square(color=GREEN, fill_opacity=0.8).move_to(2 * LEFT+2 * DOWN) red_square = Square(color=RED, fill_opacity=0.8).move_to(2 * RIGHT + 2 * DOWN) blue_square = Square(color=BLUE, fill_opacity=0.8).move_to(2 * UP) self.play(Create(green_square), Create(red_square), Create(blue_square)) self.wait() # Rotate the camera along theta self.move_camera(phi=0, theta=0) self.wait() self.move_camera(phi=0, theta=PI/2) self.wait() self.move_camera(phi=0, theta=PI) self.wait() self.move_camera(phi=0, theta=1.5*PI) self.wait() self.move_camera(phi=0, theta=-PI/2) self.wait() # Rotate the camera alogn phi self.move_camera(phi=PI/2, theta=-PI/2) self.wait() self.move_camera(phi=PI, theta=-PI/2) self.wait() self.move_camera(phi=1.5*PI, theta=-PI/2) self.wait() self.move_camera(phi=2*PI, theta=-PI/2) self.wait() self.move_camera(phi=0, theta=-PI/2) self.wait() ```

Additional media files

Images/GIFs https://github.com/ManimCommunity/manim/assets/28733722/f0fe2b20-57b1-4458-b4eb-fcd29327234d