How to remove a list of patches?

Viewed 32

I want to make a very simple animation of 3 frames:

  1. In the first frame there are only tree black circle,
  2. In the second the same black circles and an arrow above the last one,
  3. In the last there are 3 black circles and a green one to the right of the last one.

I am making the animation with the following code:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib

def make_circles(s):
    circles=[]
    radius=0.45
    pad=0.5-radius
    for i,x in enumerate(s):
        if x==0:
            circles.append(plt.Circle((i+radius+pad, 0), radius, facecolor='white', edgecolor="black"))
        elif x==1:
            circles.append(plt.Circle((i+radius+pad, 0), radius, color='black'))
        else:
            circles.append(plt.Circle((i+radius+pad, 0), radius, color='green'))
        
    return circles

if __name__=="__main__":
data=[[0,1,1,1,0],
      [0,1,1,1,0],
      [0,1,1,1,2]]
N=5
to_animate=[]
for i,x in enumerate(data):
    c=make_circles(x)
    if(i==1):
        a=plt.Arrow(3.5,2,0,-1)
        c.append(a)
    to_animate.append(c)

            
fig, ax = plt.subplots()
ax.set_xlim(-0.5,N+0.5)
ax.set_ylim(-2,2)

def animate(i):
    for cir in to_animate[i]:
        ax.add_patch(cir)

ani=FuncAnimation(fig, animate, frames=3, repeat=False)
#ani.save('./animation.gif', writer='imagemagick', fps=1)
plt.show()

However at the moment at the second iteration I am drawing above the elements on the first iteration, and at the third above the second. This is made clear by the fact that the arrow does not disappear when the last circle is green. GIF representing it

How can I erase the patches belonging to the previous iteration before drawing the new one?

2 Answers

At each new iteration, you could first remove the patches added in the previous iteration:

def animate(i):
    if i > 0:
        for cir in to_animate[i-1]:
            cir.remove()
    for cir in to_animate[i]:
        ax.add_patch(cir)

enter image description here

At each iteration you are adding new patches. Instead, you should add all the elements of the plot at the beginning, and then control their visibility during the animation sequence. During the call to animate you will can set the visibility of particular objects, and then return list of patches.

Here is an example that keeps the code relatively similarto its previous current form

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib


def make_circles(s):
    circles = []
    radius = 0.45
    pad = 0.5 - radius
    for i, x in enumerate(s):
        circles.append(plt.Circle((i + radius + pad, 0), radius, facecolor='white', edgecolor="black"))
        if x == 1:
            circles.append(plt.Circle((i + radius + pad, 0), radius, color='black'))
        elif x == 2:
            circles.append(plt.Circle((i + radius + pad, 0), radius, color='green'))

    return circles


to_animate = make_circles([0, 1, 1, 1, 2])
to_animate.append(plt.Arrow(3.5, 2, 0, -1))

hidden_at_start = [8, 9]
for ix in hidden_at_start:
    to_animate[ix].set_visible(False)

fig, ax = plt.subplots()

for patch in to_animate:
    ax.add_patch(patch)

ax.set_xlim(-0.5, N + 0.5)
ax.set_ylim(-2, 2)


def animate(i):
    if i == 0:
        pass
    elif i == 1:
        to_animate[-1].set_visible(True)
    else:
        to_animate[-2].set_visible(True)
        to_animate[-1].set_visible(False)
        
    return to_animate,


ani = FuncAnimation(fig, animate, frames=3, repeat=False)
# ani.save('./animation.gif', writer='imagemagick', fps=1)
plt.show()
Related