Search code examples
pythonmatplotlibanimationgeometrymatplotlib-3d

Moving circle animation 3d plot


I am trying to make an animation with a circle moving in a 3D plot using Matplotlib and FuncAnimation(). Everything works except that I do not find a way to remove the circle already plotted in the previous frame. This leads to the formation of a cylinder as all circles plotted overlap. I only want to plot and see the circle in the current frame. This is the code that I wrote:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import mpl_toolkits.mplot3d.art3d as art3d
from matplotlib.patches import Circle

fig = plt.figure(figsize = (8, 8))
ax = fig.add_subplot(projection="3d")


ax.set_xlabel('X(t)')
ax.set_ylabel('Y(t)')
ax.set_zlabel('Z(t)')


x_min = -100.
x_max = 100.
y_min = -100.
y_max = 100. 
z_min = -100.
z_max = 100.


ax.set_xlim3d([x_min, x_max])
ax.set_ylim3d([y_min, y_max])
ax.set_zlim3d([z_min, z_max])


x = 0.
y = 0.
# Center of the circle
center = (x, y)

# Values of z along which the circle will move
z = np.arange(0, 100, 1.)


# Initialize the circle in the 3D plot
def init():
    circle = Circle(xy = center, radius = 20., color = "coral")
    ax.add_patch(circle)
    art3d.pathpatch_2d_to_3d(circle, z = z[0], zdir="z")
    return circle


# Animation that changes the z value
def animate(iframe):
    circle = Circle(xy = center, radius = 20., color = "coral")
    ax.add_patch(circle)
    art3d.pathpatch_2d_to_3d(circle, z = z[iframe], zdir="z")
    return circle


anim = animation.FuncAnimation(fig, animate, init_func = init, frames=len(z), interval=100, blit=False, repeat = False)

enter image description here

Any idea how to solve this? Thanks.

Update: It works if at the end of animate() one adds:

plt.pause(0.1)
circle.remove()

However, it does not work if plt.pause() is not used. It just does nothing, it does not plot any circle.


Solution

  • You could use ax.patches.pop() at the beginning of your animate function to remove the previous circle (see here).

    Adapted to your code, this would give:

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import mpl_toolkits.mplot3d.art3d as art3d
    from matplotlib.patches import Circle
    
    fig = plt.figure(figsize = (8, 8))
    ax = fig.add_subplot(projection="3d")
    
    ax.set_xlabel('X(t)')
    ax.set_ylabel('Y(t)')
    ax.set_zlabel('Z(t)')
    
    x_min = -100.
    x_max = 100.
    y_min = -100.
    y_max = 100. 
    z_min = -100.
    z_max = 100.
    
    ax.set_xlim3d([x_min, x_max])
    ax.set_ylim3d([y_min, y_max])
    ax.set_zlim3d([z_min, z_max])
    
    x = 0.
    y = 0.
    # Center of the circle
    center = (x, y)
    
    # Values of z along which the circle will move
    z = np.arange(0, 100, 1.)
    circle = Circle(xy = center, radius = 0., color = "coral") #Create empty circle that acts as a placeholder
    ax.add_patch(circle)
    art3d.pathpatch_2d_to_3d(circle, z = z[0], zdir="z")
    
    # Animation that changes the z value
    def animate(iframe):
        ax.patches.pop() #remove previous circle
        circle = Circle(xy = center, radius = 20., color = "coral")
        ax.add_patch(circle)
        art3d.pathpatch_2d_to_3d(circle, z = z[iframe], zdir="z")
        return circle
    
    anim = animation.FuncAnimation(fig, animate,frames=len(z), interval=100, blit=False, repeat = False)
    

    enter image description here