Search code examples
pythonmatplotlibmatplotlib-animation

FuncAnimation: Cannot animate a point and a line at the same time


I am trying to make an animated plot to show the behavior of a simple harmonic oscillator. I would like for this animation to be a combination of the following two plots. That is, I would like the red square to go up and down, and on the same plot I would like the blue line to progressively be displayed.

Red square Line

that is what I am having issues with. I cannot get my code to animate them both at the same time

This is the code I used to get the square to move up and down

%matplotlib notebook
import itertools
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

plt.rcParams["figure.figsize"] = 9,6

x_0, v_0, k, m = 1,2,1,2
omega = np.sqrt(k/m)

def HA(t):
    xt = x_0*np.cos(omega*t) + (v_0/omega)*np.sin(omega*t)
    return np.array([t, xt])

# create a figure with an axes
fig, ax = plt.subplots()
# set the axes limits
ax.axis([0.0,50,-4,4])

# create a point in the axes
point, = ax.plot(0,x_0, marker="s", color = 'red', markersize=20)

t_vector = np.linspace(0,50, 100)
xt_vector = x_0*np.cos(omega*t_vector) + (v_0/omega)*np.sin(omega*t_vector)

# Updating function, to be repeatedly called by the animation
def update(t):
    #Point coordinates 
    t,xt = HA(t)
    point.set_data([0],[xt])
    return point

ani = animation.FuncAnimation(fig, update, interval=500, blit=True, repeat=True,frames=t_vector.size)
                    
plt.axhline(y=0.0, color='black', linestyle='--', label = "Equilibrium")
plt.xlabel(r'$t$', fontsize=18)
plt.ylabel(r"$x(t)$", fontsize=16)
plt.legend()

#ani.save('Square.mp4', writer = 'ffmpeg', fps = 10)
plt.show()

And this is the code I used for the line

plt.rcParams["figure.figsize"] = 9,6

x_0, v_0, k, m = 1,2,1,2
omega = np.sqrt(k/m)

def HA(t):
    xt = x_0*np.cos(omega*t) + (v_0/omega)*np.sin(omega*t)
    return np.array([t, xt])

# create a figure with an axes
fig, ax = plt.subplots()
# set the axes limits
ax.axis([0.0,50,-4,4])

line, = ax.plot([], [], color="blue")

t_vector = np.linspace(0,50, 100)
xt_vector = x_0*np.cos(omega*t_vector) + (v_0/omega)*np.sin(omega*t_vector)

# Updating function, to be repeatedly called by the animation
def update(t):
    line.set_data(t_vector[:t], xt_vector[:t])
    return line

ani = animation.FuncAnimation(fig, update, interval=500, blit=True, repeat=True,frames=t_vector.size)
                    
plt.axhline(y=0.0, color='black', linestyle='--', label = "Equilibrium")
plt.xlabel(r'$t$', fontsize=18)
plt.ylabel(r"$x(t)$", fontsize=16)
plt.legend()

#ani.save('Line.mp4', writer = 'ffmpeg', fps = 10)

plt.show()

I tried to just combine them both, but although this does not give any error, the resulting plot does not move. I am a rookie when it comes to animations with matplotlib, I checked the similar questions I could find, but none helped me. I could not extend their answers to get something useful for me.

plt.rcParams["figure.figsize"] = 9,6

x_0, v_0, k, m = 1,2,1,2
omega = np.sqrt(k/m)

def HA(t):
    xt = x_0*np.cos(omega*t) + (v_0/omega)*np.sin(omega*t)
    return np.array([t, xt])

# create a figure with an axes
fig, ax = plt.subplots()
ax.axis([0.0,50,-4,4])

# create a point in the axes
point, = ax.plot(0,x_0, marker="s", color = 'red', markersize=20)
line, = ax.plot([], [], color="blue")


t_vector = np.linspace(0,50, 100)
xt_vector = x_0*np.cos(omega*t_vector) + (v_0/omega)*np.sin(omega*t_vector)

# Updating function, to be repeatedly called by the animation
def update(t):
    t,xt = HA(t)
    line.set_data(t_vector[:t], xt_vector[:t])
    point.set_data([0],[xt])
    return line, point


ani = animation.FuncAnimation(fig, update, interval=500, blit=True, repeat=True,frames=t_vector.size)
                    
plt.axhline(y=0.0, color='black', linestyle='--', label = "Equilibrium")
plt.xlabel(r'$t$', fontsize=18)
plt.ylabel(r"$x(t)$", fontsize=16)
plt.legend()

plt.show()

Could you help me, please?


Solution

  • I tried to just combine them both, but although this does not give any error, the resulting plot does not move.

    In the update function, after t, xt = HA(t), t is a float. You'll have to cast it back to an integer value for t_vector[:t] and xt_vector[:t] to work.

    def update(t):
        t,xt = HA(t)
        t = int(t)
        line.set_data(t_vector[:t], xt_vector[:t])
        point.set_data([0],[xt])
        return line, point
    

    Note that it would probably make more sense for HA() function to only return the xt value rather than np.array([t, xt]).

    Doing this small edit, I obtain a working animation:

    enter image description here

    However, assuming that the red rectangle should follow the tip of the blue line along the Y-axis, this does not look good.

    This is because the line is plotted from xt_vector and the rectangle from the xt value (returned by the HA() function). Both formulas are the same but the t differs: xt_vector is computed for t in np.linspace(0, 50, 100) while xt is computed for t in np.range(0, nb_frame) (nb_frame = 100 here).

    Bottom line: changing the update function as follow should work

    def update(t):
        line.set_data(t_vector[:t], xt_vector[:t])
        point.set_data([0], [xt_vector[t]])
        return line, point
    

    enter image description here