Search code examples
pythonmanim

How to move line to original position with manim?


I have 4 lines making up a rectangle. In the scene I want to show, that the lines are not attached to each other and so I move them a little bit away from each other, and then I want to move them back to their original position (making up the rectangle). Is there a way to move them to their original position without creating a new target position?

This is my code so far:

from manim import *

bufferCoordinates = [
    [5, -1.65180767, 0.0],
    [3.67578217, -3.77055619, 0.0],
    [-5.0, 1.65180767, 0.0],
    [-3.67578217, 3.77055619, 0.0],
]

config.frame_width = 18
config.frame_height = 12

class getLSWgeoms(ZoomedScene):
    def construct(self):
        
        # Polygon
        buffer = Polygon(*bufferCoordinates)
        buffer.set_fill(PINK, opacity=0.5)

        # Single Lines
        bufferline1 = Line(*[[5, -1.65180767, 0.0], [3.67578217, -3.77055619, 0.0]])
        bufferline1.generate_target()
        bufferline1.target.move_to(5.0 * RIGHT + 3 * DOWN)

        bufferline2 = Line(*[[3.67578217, -3.77055619, 0.0], [-5.0, 1.65180767, 0.0]])
        bufferline2.generate_target()
        bufferline2.target.move_to(1.5 * DOWN + 1 * LEFT)

        bufferline3 = Line(*[[-5.0, 1.65180767, 0.0], [-3.67578217, 3.77055619, 0.0]])
        bufferline3.generate_target()
        bufferline3.target.move_to(5 * LEFT + 3 * UP)

        bufferline4 = Line(*[[-3.67578217, 3.77055619, 0.0],[5, -1.65180767, 0.0]])
        bufferline4.generate_target()
        bufferline4.target.move_to(1.5 * UP + 1 * RIGHT)
        
        # Play Scenes
        self.play(FadeIn(buffer), run_time=1)
        self.play(
            MoveToTarget(bufferline1),
            MoveToTarget(bufferline2),
            MoveToTarget(bufferline3),
            MoveToTarget(bufferline4),
            run_time=3
        )

enter image description here


Solution

  • Simple solution using save_state() and Restore():

    from manim import *
    
    bufferCoordinates = [
        [5, -1.65180767, 0.0],
        [3.67578217, -3.77055619, 0.0],
        [-5.0, 1.65180767, 0.0],
        [-3.67578217, 3.77055619, 0.0],
    ]
    
    config.frame_width = 18
    config.frame_height = 12
    
    class getLSWgeoms(ZoomedScene):
        def construct(self):
            
            # Polygon
            buffer = Polygon(*bufferCoordinates)
            buffer.set_fill(PINK, opacity=0.5)
    
            # Single Lines
            bufferline1 = Line(*[[5, -1.65180767, 0.0], [3.67578217, -3.77055619, 0.0]])
            bufferline1.save_state()
            bufferline1.generate_target()
            bufferline1.target.move_to(5.0 * RIGHT + 3 * DOWN)
    
            bufferline2 = Line(*[[3.67578217, -3.77055619, 0.0], [-5.0, 1.65180767, 0.0]])
            bufferline2.save_state()
            bufferline2.generate_target()
            bufferline2.target.move_to(1.5 * DOWN + 1 * LEFT)
    
            bufferline3 = Line(*[[-5.0, 1.65180767, 0.0], [-3.67578217, 3.77055619, 0.0]])  
            bufferline3.save_state()
            bufferline3.generate_target()
            bufferline3.target.move_to(5 * LEFT + 3 * UP)
    
            bufferline4 = Line(*[[-3.67578217, 3.77055619, 0.0],[5, -1.65180767, 0.0]])   
            bufferline4.save_state()
            bufferline4.generate_target()
            bufferline4.target.move_to(1.5 * UP + 1 * RIGHT)
            
            # Play Scenes
            self.play(FadeIn(buffer), run_time=1)
            self.play(
                MoveToTarget(bufferline1),
                MoveToTarget(bufferline2),
                MoveToTarget(bufferline3),
                MoveToTarget(bufferline4),
                run_time=3
            )
            self.wait()
            self.play(Restore(bufferline1),Restore(bufferline2),Restore(bufferline3),Restore(bufferline4), )
            self.wait()
    
    

    Btw, using the target functions here is a bit more complicated than necessary imo, but maybe this is just because this is mwe. Also, your bufferlines* will appear directly in the scene when they will be moved, they are not created or sth. similar before.


    UPDATE: Here is an answer which is more how I would have done it.

    from manim import *
    
    bufferCoordinates = [
        [5, -1.65180767, 0.0],
        [3.67578217, -3.77055619, 0.0],
        [-5.0, 1.65180767, 0.0],
        [-3.67578217, 3.77055619, 0.0],
        [5, -1.65180767, 0.0],
    ]
    
    config.frame_width = 18
    config.frame_height = 12
    
    
    class getLSWgeoms(ZoomedScene):
        def construct(self):
            # Polygon
            buffer = Polygon(*bufferCoordinates[:4])
            buffer.set_fill(PINK, opacity=0.5)
    
            # Single Lines
            bufferlines = VGroup(
                *[
                    Line(start, end)
                    for start, end in zip(bufferCoordinates[:4], bufferCoordinates[1:])
                ]
            )
            bufferlines.save_state()
    
            # Play Scenes
            self.play(FadeIn(buffer), run_time=1)
            self.wait()
            self.play(Create(bufferlines))
            self.wait()
            self.play(
                bufferlines[0].animate.move_to(5.0 * RIGHT + 3 * DOWN),
                bufferlines[1].animate.move_to(1.5 * DOWN + 1 * LEFT),
                bufferlines[2].animate.move_to(5 * LEFT + 3 * UP),
                bufferlines[3].animate.move_to(1.5 * UP + 1 * RIGHT),
                run_time=3,
            )
            self.wait()
            self.play(Restore(bufferlines))
            self.wait()
    

    I'm not a python expert, so the list operations could maybe even more optimized. Instead of animating each bufferline you could also use an AnimationGroup, but I don't use it that often. I think like this it is more readable. Also, you could use shift() instead of move_to() but I was too lazy to do the recalculation :D.