表面动画和使用 matplotlib 保存

Surface animation and saving with matplotlib

我尝试做一个很简单的动画,用matplotlib保存,但是没有成功。例如,我想看到一些振荡:这是我能做的最好的

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)

我阅读了大量在线文档,包括官方文档,但我找不到表面随时间演变的明确示例。我还发现很难掌握使用 matplotlib 构建绘图背后的逻辑(我阅读了有关图形、关联的对象、轴、其他对象……我感到困惑),但我也在自学它以备考试和我不是一个真正的技术人员,所以也许这就是我遇到这么多麻烦的原因;从根本上说,如果您回答并能多花两分钟更详细地描述您在做什么,您会让我非常高兴。非常感谢您的帮助。

首先,你要设置表面的数学域,它对于每个动画帧都是不变的,所以你可以在开始的时候做:

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')

这里是动画的东西,你必须定义一个函数来描述一个动画帧和下一帧之间的变化。首先你必须从图中清除你在前一帧中已经绘制的内容,所以你可以使用 ax.cla().
然后是数学部分:您必须定义您的函数以及它如何随时间变化。注意 animate 函数有一个参数,在你的例子中是 n,它在每一帧中增加 1,你可以用它来描述连续帧之间的变化。例如,如果我写 u = np.sin((X + Y) + n) 那么正交曲面将在每次迭代中增加其相位,因此它将“向前移动”;您可以重新定义此等式,以便通过正确使用 n 参数来实现您的目标。
然后是用 ax.plot_surface(X, Y, u) 绘制表面的时候了。这不是强制性的,但我建议修复轴限制,以改进动画,ax.set_zlim(-2, 2)
这样 animate 函数定义为:

def animate(n):
    ax.cla()

    u = np.sin((X + Y) + n)

    ax.plot_surface(X, Y, u)
    ax.set_zlim(-2, 2)

    return fig,

最后,您必须使用您想要的参数设置 FuncAnimation 对象:

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

完整代码

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)


另一个改变曲面公式的例子,如果你使用:

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

animate函数中,表面相位保持不变,但正弦表面振幅会发生变化,遵循正弦函数,所以你会得到:

这是另一个例子。我从JavaScript库vis3d的图库借来的,感谢@Zephyr的回答,我把它变成了Python。

from math import pi, cos, sin
import numpy as np
import matplotlib.colors as mcolors
from matplotlib import cm
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

x_ = np.linspace(0, 314, 50)
y_ = np.linspace(0, 314, 50)
X, Y = np.meshgrid(x_, y_)

t_ = np.linspace(0, 2*pi, 90)

def f(x, y):
  return np.sin(x/50) * np.cos(y/50) * 50 + 50

def g(x, y, t):
  return f(x*cos(t) - y*sin(t), x*sin(t) + y*cos(t))

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

def animate(n):
    ax.cla()
    Z = g(X, Y, t_[n])
    colorfunction = (X**2+Y**2+Z**2)
    norm = mcolors.Normalize(colorfunction.min(), colorfunction.max())
    ax.plot_surface(
      X, Y, Z, rstride = 1, cstride = 1, facecolors=cm.jet(norm(colorfunction))
    )
    ax.set_zlim(0, 100)
    return fig


anim = FuncAnimation(
  fig = fig, func = animate, frames = len(t_), interval = 1, repeat = False
)
anim.save("Anim.mp4", fps = 10)

(感谢网站 ezgif.com,我将 mp4 转换为 gif,但是 gif 对于 SO 来说太大了,所以我用 压缩了它gifsicle 质量下降。)


编辑

更好的 gif: