Search code examples
manim

How to continually update a set of lines in manim


I'm relatively new to manim and have run into a strange issue I don't understand. I'm trying to continually update a set of lines and everything seems to work fine, but then all of the lines disappear except one. Here is a short (non) working example to demonstrate what's happening. First I create a set of arrows and also a set of lines (using always_redraw), all of which begin at a dot. Then I create a list of GrowArrow animations, which allows me to grow the entire set of arrows at the same time. This seems to work fine. Next, I create a list of ReplacementTransform animations to turn the arrows into the lines. This also seems to work fine, but after the arrows are transformed into lines, all of the lines end up disappearing except the last one. Lastly, I move the dot to show that the lines are continually updated. This seems to work fine for the single line that is left, but I don't understand why all but one of the lines are disappearing. I am using manimCE v0.7.0 on a mac. Here is the code and the final image from the movie. Any help would be greatly appreciated, as this is driving me nuts and I am completely stymied.

class RedrawTest(Scene):
    def construct(self):
        # Create x and y coordinates for a set of arrows
        n_arrows = 8
        length = 3
        angles = np.linspace(0,PI,n_arrows)
        x, y = length*np.cos(angles), length*np.sin(angles)
        
        dot = Dot()
        arrows = [Arrow(dot.get_center(), [x[i],y[i],0], buff=0) for i in range(n_arrows)] # List of arrows from dot to (x,y) coords
        lines = [always_redraw(lambda: Arrow(dot.get_center(), [x[i],y[i],0], tip_length=0.0, buff=0)) for i in range(n_arrows)] # List of (always redrawn) lines from dot to (x,y) coords
        
        grow_arrows = [GrowArrow(arrows[i],run_time=2) for i in range(n_arrows)] # List of GrowArrow animations
        transform = [ReplacementTransform(arrows[i],lines[i],run_time=4) for i in range(n_arrows)] # List of ReplacementTransform animations
        
        self.play(FadeIn(dot))
        self.play(*grow_arrows) # Grow an entire set of arrows simultaneously
        self.wait()
        self.play(*transform) # Transform entire set of arrows into (always redrawn) lines
        self.wait()
        self.play(dot.animate.shift(2*DOWN),run_time=3) # Move dot
        self.wait()

Final image from movie


Solution

  • I also encountered this problem. It was GitHub Copilot that eventually solved it for me!

    Here is a code that works - I replaced the line:

    lines = [always_redraw(lambda: Arrow(dot.get_center()...
    

    With

    lines = [always_redraw(lambda i=i: Arrow(dot.get_center()...
    

    And in total:

    class RedrawTest(Scene):
    def construct(self):
        # Create x and y coordinates for a set of arrows
        n_arrows = 8
        length = 3
        angles = np.linspace(0,PI,n_arrows)
        x, y = length*np.cos(angles), length*np.sin(angles)
        
        dot = Dot()
        arrows = [Arrow(dot.get_center(), [x[i],y[i],0], buff=0) for i in range(n_arrows)] # List of arrows from dot to (x,y) coords
        lines = [always_redraw(lambda i=i: Arrow(dot.get_center(), [x[i],y[i],0], tip_length=0.0, buff=0)) for i in range(n_arrows)] # List of (always redrawn) lines from dot to (x,y) coords
        
        grow_arrows = [GrowArrow(arrows[i],run_time=2) for i in range(n_arrows)] # List of GrowArrow animations
        transform = [ReplacementTransform(arrows[i],lines[i],run_time=4) for i in range(n_arrows)] # List of ReplacementTransform animations
        
        self.play(FadeIn(dot))
        self.play(*grow_arrows) # Grow an entire set of arrows simultaneously
        self.wait()
        self.play(*transform) # Transform entire set of arrows into (always redrawn) lines
        self.wait()
        self.play(dot.animate.shift(2*DOWN),run_time=3) # Move dot
        self. Wait()
    

    I am not sure why it works, but it seems that if you don't add this "i=i" after the lambda they all get the same last or the first value of i in the for loop.