Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slight offset in MoveAlongPath #3936

Open
jeremysalwen opened this issue Sep 19, 2024 · 4 comments
Open

Slight offset in MoveAlongPath #3936

jeremysalwen opened this issue Sep 19, 2024 · 4 comments

Comments

@jeremysalwen
Copy link

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
%%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
Copy link
Contributor

uwezi commented Sep 19, 2024

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()
MowerAnimation.mp4

How to change this?

  • use always_redraw or updater functions to run along the line rather than MoveAlongPath
  • or calculate the actual offset between the two centers, add an invisible, shifted path which you use for the MoveAlongPath
  • or create a VGroup which contains a larger object (e.g. a square or circle) than your triangle, place your triangle in the VGroup so that the mid-point of the triangle coincides with the bounding box of the complete VGroup

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()
MowerAnimation.mp4

@uwezi
Copy link
Contributor

uwezi commented Sep 19, 2024

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
Copy link
Author

Thank you, this is very helpful!

@uwezi
Copy link
Contributor

uwezi commented Sep 20, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🆕 New
Development

No branches or pull requests

2 participants