ManimCommunity / manim

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

Slight offset in MoveAlongPath #3936

Open jeremysalwen opened 6 days ago

jeremysalwen commented 6 days ago

Description of bug / unexpected behavior

When using MoveAlongPath with a rotated object, a slight offset is introduced

Expected behavior

The object follows the line exactly, not with an offset.

How to reproduce the issue

Minimum Reproducible Code ```py %%manim -qm MowerAnimation class MowerAnimation(Scene): def construct(self): # Create the mower as a small triangle mower = Triangle(color=GREEN, fill_opacity=1).scale(0.2) start_pos = np.array([-4, -3, 0]) end_pos = np.array([-1, 4, 0]) mower.move_to(start_pos) delta = end_pos - start_pos angle = np.arctan2(delta[0], delta[1]) mower.rotate(-angle) path = Line(start_pos, end_pos) dashed_line = DashedLine(start_pos, end_pos) self.add(dashed_line) self.play( MoveAlongPath(mower, path, rate_func=linear), run_time=5 ) self.wait() ```

Additional media files

image

Logs

Terminal output ``` [09/19/24 21:12:51] DEBUG Hashing ... [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[352](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#352) DEBUG Hashing done in 0.132026 s. [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[364](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#364) DEBUG Hash generated : 2016333726_3528055951_4110290056 [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[367](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#367) INFO Animation 0 : Using cached data (hash : [cairo_renderer.py](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py):[88](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py#88) 2016333726_3528055951_4110290056) DEBUG List of the first few animation hashes of the scene: [cairo_renderer.py](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py):[97](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py#97) ['2016333726_3528055951_4110290056'] DEBUG Animation with empty mobject [animation.py](file:///usr/local/lib/python3.11/site-packages/manim/animation/animation.py):[175](file:///usr/local/lib/python3.11/site-packages/manim/animation/animation.py#175) DEBUG Hashing ... [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[352](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#352) DEBUG Hashing done in 0.163328 s. [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[364](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#364) DEBUG Hash generated : 543634251_1704852926_3284192126 [hashing.py](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py):[367](file:///usr/local/lib/python3.11/site-packages/manim/utils/hashing.py#367) INFO Animation 1 : Using cached data (hash : [cairo_renderer.py](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py):[88](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py#88) 543634251_1704852926_3284192126) DEBUG List of the first few animation hashes of the scene: [cairo_renderer.py](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py):[97](file:///usr/local/lib/python3.11/site-packages/manim/renderer/cairo_renderer.py#97) ['2016333726_3528055951_4110290056', '543634251_1704852926_3284192126'] INFO Combining to Movie file. [scene_file_writer.py](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py):[617](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py#617) DEBUG Partial movie files to combine (2 files): [scene_file_writer.py](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py):[561](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py#561) ['/manim/media/videos/manim/720p30/partial_movie_files/MowerA nimation/2016333726_3528055951_4110290056.mp4', '/manim/media/videos/manim/720p30/partial_movie_files/MowerAn imation/543634251_1704852926_3284192126.mp4'] INFO [scene_file_writer.py](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py):[737](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene_file_writer.py#737) File ready at '/manim/media/videos/manim/720p30/MowerAnimation.mp4' INFO Rendered MowerAnimation [scene.py](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene.py):[247](file:///usr/local/lib/python3.11/site-packages/manim/scene/scene.py#247) Played 2 animations ```

System specifications

System Details ``` https://notebooks.gesis.org/binder/jupyter/user/manimcommunity-jupyter_examples-xz4gvrcr/notebooks/First%20Steps%20with%20Manim.ipynb ```
uwezi commented 5 days ago

It's not a bug, it's a feature. In Manim an object's position is generally identified by the center point of a rectangular bounding box around the object. This center point does not coincide with the mid-point of your rotated triangle though.

class MowerAnimation(Scene):
    def construct(self):
        # Create the mower as a small triangle
        mower = Triangle(color=GREEN, fill_opacity=0.5).scale(0.2).scale(3)

        start_pos = np.array([-4, -3, 0])
        end_pos = np.array([-1, 4, 0])
        mower.move_to(start_pos)

        delta = end_pos - start_pos
        angle = np.arctan2(delta[0], delta[1])
        mower.rotate(-angle)

        bbox = always_redraw(lambda:
            VGroup(
                Polygon(*[mower.get_critical_point(p) for p in (UR,UL,DL,DR)],stroke_width=1,stroke_color=YELLOW),
                Line(mower.get_critical_point(DOWN),mower.get_critical_point(UP),stroke_width=1,color=YELLOW),
                Line(mower.get_critical_point(LEFT),mower.get_critical_point(RIGHT),stroke_width=1,color=YELLOW),
            )
        )
        self.add(bbox)

        path = Line(start_pos, end_pos)

        dashed_line = DashedLine(start_pos, end_pos)

        self.add(dashed_line)

        self.play(
            MoveAlongPath(mower, path, rate_func=linear),
            run_time=5
        )
        self.wait()

https://github.com/user-attachments/assets/c0641b87-c57a-4911-8bc4-2d1a1e49acf0

How to change this?

Somehow I was most curious to try out my third idea there:

class MowerAnimation(Scene):
    def construct(self):
        # Create the mower as a small triangle
        mower = VGroup(
            Square(stroke_opacity=0.2, fill_opacity=0)
        )
        tri = Triangle(color=GREEN, fill_opacity=0.5).scale(0.2)

        start_pos = np.array([-4, -3, 0])
        end_pos = np.array([-1, 4, 0])
        mower.move_to(start_pos)

        delta = end_pos - start_pos
        angle = np.arctan2(delta[0], delta[1])
        tri.rotate(-angle).shift(mower.get_center()-tri.get_center_of_mass())
        mower += tri

        path = Line(start_pos, end_pos)

        dashed_line = DashedLine(start_pos, end_pos)

        self.add(dashed_line)

        self.play(
            MoveAlongPath(mower, path, rate_func=linear),
            run_time=5
        )
        self.wait()

https://github.com/user-attachments/assets/bdacd608-a30f-4dd6-99a8-3cf8c38b338e

uwezi commented 5 days ago

In case you intend to rotate your triangle more often in this script, then you should use tri.rotate(angle, about_point=tri.get_center_of_mass()) This will keep the center of mass of the triangle fixed inside the square and thus the VGroup and only rotate the triangle. Don't rotate the mower in this script.

jeremysalwen commented 5 days ago

Thank you, this is very helpful!

uwezi commented 5 days ago

Thank you, this is very helpful!

you are welcome. Come over to Discord for more tips about Manim and the details about its workings: https://docs.manim.community/en/stable/faq/general.html?highlight=discord#where-can-i-find-more-resources-for-learning-manim