Search code examples
pythonmatplotlibanimationmathmatplotlib-animation

Surface animation and saving with matplotlib


I tried to do a very simple animation and save it with matplotlib, but without success. I want for example to see something oscillating: here the best I could do

import numpy as np
import matplotlib.pyplot as plt    
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation

#Define x,y vectors and meshgrid with function u on it

x = np.arange(0,10,0.1)
y = np.arange(0,10,0.1)
X,Y = np.meshgrid(x,y)
u = np.sin(X + Y)

#Create a figure and an axis object for the surface
#(which by the way is not initialized, because I don't know were to)

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

#Define a kind-of animation function, imitating what I saw many times

def animate(n):
    global u
    for n in (1,20):
        u = np.sin((X + Y)*n)
    return fig,     

#I'm missing many commands, I'm just putting what I know

anim = animation.FuncAnimation(fig,animate)
anim.save('A.mp4',fps=10)

I read tons of online documentation, including the official one, but I cannot find a clear example in which a surface evolve with time. I also find very difficult to grasp the logic behind the construction of plots with matplotlib (I read about figures, objects associated, axes, other objects... I get confused), but I'm also self-learning it for an exam and I'm not really a tech guy, so maybe that's why I'm having so much troubles; fundamentally, if you answer and can spend two more minutes describing in some more detail what you're doing, you'll make me very happy. Many thanks for any help.


Solution

  • First of all, you have to set up the mathematical domain of the surface, which is constant for each animation frame, therefore you can do it at the beginning:

    x = np.arange(0,10,0.1)
    y = np.arange(0,10,0.1)
    X,Y = np.meshgrid(x,y)
    

    Then you can initialize the figure:

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

    Here come the animation stuff, you have to define a function which describes what changes between one animation frame and the following one. First of all you have to clear from the plot what you have already drawn in the previous frame, so you can use ax.cla().
    Then it comes the math part: you have to define your function and how it changes with time. Pay attention that the animate function takes a parameter, n in you case, which increase by 1 in each frame, you can use it to describe what changes between consecutive frames. For example if I write u = np.sin((X + Y) + n) then the synusoidal surface will increase its phase in each iteration, so it will "move forward"; you can re-define this equation in order to achieve your target by properly use the n parameter.
    Then it's time to draw the surface with ax.plot_surface(X, Y, u). It is not mandatory, but I suggest to fix the axis limits, in order to improve the animation, with ax.set_zlim(-2, 2).
    In this way the animate function is defined as:

    def animate(n):
        ax.cla()
    
        u = np.sin((X + Y) + n)
    
        ax.plot_surface(X, Y, u)
        ax.set_zlim(-2, 2)
    
        return fig,
    

    Finally you have to set up the FuncAnimation object with the parameter you want:

    anim = FuncAnimation(fig = fig, func = animate, frames = 10, interval = 1, repeat = False)
    

    Complete code

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    
    
    x = np.arange(0,10,0.1)
    y = np.arange(0,10,0.1)
    X,Y = np.meshgrid(x,y)
    
    
    fig = plt.figure()
    ax = fig.add_subplot(111,projection='3d')
    
    
    def animate(n):
        ax.cla()
    
        u = np.sin((X + Y) + n)
    
        ax.plot_surface(X, Y, u)
        ax.set_zlim(-2, 2)
    
        return fig,
    
    
    anim = FuncAnimation(fig = fig, func = animate, frames = 10, interval = 1, repeat = False)
    anim.save('A.mp4',fps=10)
    

    enter image description here


    Another example changing the surface formula, if you use:

    u = np.sin(5/2/np.pi*n)*np.sin((X + Y))
    

    within animate function, then the surface phase will be kept constant, but the synusoidal surface amplitude will change, following itself a synusoidal function, so you will get:

    enter image description here