Home > database >  Animate points with matplotlib
Animate points with matplotlib

Time:02-23

I want to have 10 moving points. I used the code below. I'm experimenting with matplotlib which I don't know very well.

from matplotlib import pyplot as plt
import numpy as np
from matplotlib import animation

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

# second option - move the point position at every frame
def update_point(n, x, y, z, point):
    point.set_data(np.array([x[n], y[n]]))
    point.set_3d_properties(z[n], 'z')
    return point

def x(i):
    return np.cos(t*i)

for i in range(10):
    t=np.arange(0, 2*np.pi, 2*np.pi/100)
    y=np.sin(t)
    z=t/(2.*np.pi)
    point, = ax.plot([x(i)[0]], [y[0]], [z[0]], 'o')
    ani=animation.FuncAnimation(fig, update_point, 99, fargs=(x(i), y, z, point))

ax.legend()
ax.set_xlim([-1.5, 1.5])
ax.set_ylim([-1.5, 1.5])
ax.set_zlim([-1.5, 1.5])

plt.show()

I hoped that if I turn x to a function of i, then I will have 10 points in the for loop, but nothing happened. Only one point is moving. What am I doing wrong?

CodePudding user response:

For a start, you place your animation object anim into the loop, so not only the point data but also the animation object is repeatedly overwritten. For ease of use, let's put the data points into numpy arrays, where rows represent the time and columns the different points you want to animate. Then, we calculate the x, y, and z arrays based on the t array (for aesthetics, a seamless loop along the columns with length 2*pi, with each column shifted so that the points are equally distributed) and simply update the x, y, and z data row-wise in each animation step. Closely related to your script, this would look like:

from matplotlib import pyplot as plt
import numpy as np
from matplotlib import animation

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

num_of_points = 7
num_of_frames = 50

t=np.linspace(0, 2*np.pi, num_of_frames, endpoint=False)[:, None]   np.linspace(0, 2*np.pi, num_of_points, endpoint=False)[None, :]
x=np.cos(t)
y=np.sin(t)
z=np.sin(t)*np.cos(t)

points, = ax.plot([], [], [], 'o')


def update_points(n):
    points.set_data(np.array([x[n, :], y[n, :]]))
    points.set_3d_properties(z[n, :], 'z')
    return points,

ax.set_xlim([-1.5, 1.5])
ax.set_ylim([-1.5, 1.5])
ax.set_zlim([-1.5, 1.5])


ani=animation.FuncAnimation(fig, update_points, num_of_frames, interval=10, blit=True, repeat=True)
plt.show()

Sample output: enter image description here

As you chose to animate line plots (these are animated markers without visible lines, scatter plots are different in structure), you cannot use different colors unless you plot each point separately. On the plus side, you can use blitting to make the animation faster. And another point regarding your code - I suggest not using np.arange(), as this can lead to float problems at the endpoint. Use instead enter image description here As the time information is derived from the row number, you could also forget the t helper array and fill directly the x, y, and z arrays with the desired or random data as the following example shows. However, for an animation, you have to ensure smooth transitions between states, so incremental changes along axis 0 are essential.

...
num_of_points = 4
num_of_frames = 100

#random walk
x = np.random.random((num_of_frames, num_of_points))-0.4
y = np.random.random((num_of_frames, num_of_points))-0.3
z = np.random.random((num_of_frames, num_of_points))-0.5
x[:] = x.cumsum(axis=0)
y[:] = y.cumsum(axis=0)
z[:] = z.cumsum(axis=0)

points, = ax.plot([], [], [], 'o')
...
  • Related