Home > Software design >  Video of geometrical shapes in matplotlib
Video of geometrical shapes in matplotlib

Time:11-10

I have position data of three circles and I want to make an animation of these moving circles. I have seen a lot of animations of functions, but I can't get to work animations of geometric shapes. Here is some code I wrote to create plots of the three circles in time varying positions.

import matplotlib.pyplot as plt
import numpy as np

# Just a minimal example mock data
positions=np.zeros((3,2,100))
positions[0,0,:]=np.linspace(-10,10,100)
positions[0,1,:]=np.linspace(-10,10,100)
positions[1,0,:]=7*np.cos(np.linspace(0,30,100))
positions[1,1,:]=7*np.sin(np.linspace(0,30,100))
positions[2,0,:]=5*np.sin(np.linspace(0,30,100))
positions[2,1,:]=np.linspace(-10,10,100)

# My code
for i in range(100):
    fig, ax = plt.subplots()
    ax.set_xlim(-10, 10)
    ax.set_ylim(-10, 10)
    circle1=plt.Circle((positions[0,0,i], positions[0,1,i]),1)
    circle2=plt.Circle((positions[1,0,i], positions[1,1,i]),1)
    circle3=plt.Circle((positions[2,0,i], positions[2,1,i]),1)
    ax.add_patch(circle1)
    ax.add_patch(circle2)
    ax.add_patch(circle3)
    ax.axis("equal")

positions is an array: the first index labels the different circles, the second the x and y coordinate and the third the ith time step. I am able to create plots of these shapes as their position varies. But now I want to put these images together to make an animation.

CodePudding user response:

You need to call animate function.

(There are other ways. Depending on how you render the plot, you can loop yourself, and update the data. But for this kind of animation, animate is better).

# Let start with import. Btw, you should include those in your question.
# The "minimal reproducible example" is an example that is minimal, in the sense
# that it contains only what is strictly needed for your question, 
# with nothing from your app that is not needed.
# But it contains also ALL that is needed to reproduce problem.
# You wouldn't want someone to reply to you "if your code doesn't work
# it is because you did not import matplotlib". But if you don't include
# it...
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as anim

# Likewise, a minimal reproducible example should include mock data
# Again, no need for the real one. I am sure they are complicated to
# create, and this complication if off topic to your question
# But anything, just to illustrate the question.
positions=np.zeros((3,2,100))
positions[0,0,:]=np.linspace(-10,10,100)
positions[0,1,:]=np.linspace(-10,10,100)
positions[1,0,:]=7*np.cos(np.linspace(0,30,100))
positions[1,1,:]=7*np.sin(np.linspace(0,30,100))
positions[2,0,:]=5*np.sin(np.linspace(0,30,100))
positions[2,1,:]=np.linspace(-10,10,100)


# That preamble done, we start with  something very similar to one loop
# of your code, just to create the plot
i=0 # That is because I am lazy, and it's easier than replacing i by 0 in the following lines
fig, ax = plt.subplots()
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
circle1=plt.Circle((positions[0,0,i], positions[0,1,i]),1)
circle2=plt.Circle((positions[1,0,i], positions[1,1,i]),1)
circle3=plt.Circle((positions[2,0,i], positions[2,1,i]),1)
ax.add_patch(circle1)
ax.add_patch(circle2)
ax.add_patch(circle3)
#ax.axis("equal") # I comment this one because it breaks xlim/ylim

# So now, we have 1 plot, with the circles
# We DONT just redo it to with other animations. That wouldn't work (it creates
# a new figure) But even if it were working, that would be hugely slow
# Creating a figure is way more expansive than just updating some 
# parameters of it

# What we do, is to regularly update the plot data, here, the centers
# of the 3 circles
# In this case (again, it could be done other way), I do so using a
# animation function that will be called back iteratively by the
# rendering loop

# animate has 1 arg. That is `i`, the frame number (the exact same i of your code)
# And returns a list of `artist` that is of "things" whose you want to
# update the data.
# and before returning it does the job of updating the data
def animate(i):
   # We update the center of each of the 3 circles
   circle1.set_center(positions[0,:,i])
   circle2.set_center(positions[1,:,i])
   circle3.set_center(positions[2,:,i])
   # And return a list of those 3 circles so that the main loop knows
   # that they were changed
   return [circle1, circle2, circle3]

# This is how we ensure `animate` is called 100 times. With 20ms between each call. 
# In fact, because of the last parameter, it is called infinitely, because
# we loop back to the beginning 
theAnimation = anim.FuncAnimation(fig, animate, frames=100, interval=20, blit=True )
#theAnimation.save("result.gif")
plt.show()

So, roughly, outside the heavy commenting, this is your code 3 lines for the set_center to do in each loop, your own loop being done only once. The loop being here done by a FuncAnimation calling iteratively an animate callback.

And, I'll edit your question to include the "minimal reproducible example" (but you are free to edit it back if you think that is contradictory to your intent). And I encourage you to try to use the same kind of examples for your future questions.

enter image description here

  • Related