简体   繁体   English

在初始 3D plot 和 matplotlib 上动画 3D 表面

[英]Animate 3D surface over an initial 3D plot with matplotlib

I am trying to animate a 3D surface over an initial 3D plot. But am struggling with keeping the initial 3D plot on the animation. I have to call the cla function to be able to plot the new surface and this will delete my initial state. I am trying to animate a 3D surface over an initial 3D plot. But am struggling with keeping the initial 3D plot on the animation. I have to call the cla function to be able to plot the new surface and this will delete my initial state.

As an example, I have the animation of a surface while keeping the x,y,z axis.例如,我有一个表面的 animation,同时保持 x、y、z 轴。 The end goal would be to plot the initial state once, and animate the surface.最终目标是 plot 初始 state 一次,并为表面设置动画。

Is it possible to create plot layers?是否可以创建 plot 层? Send the initial state to one layer and the animation to another?将初始 state 发送到一层,将 animation 发送到另一层?

import matplotlib.pyplot as plt import numpy as np from matplotlib.animation import FuncAnimation从 matplotlib.animation 导入 matplotlib.pyplot 作为 plt 导入 numpy 作为 np 导入 FuncAnimation

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

def initialState(ax):
    s=1.15 
    x_axis = np.array([s*5, 0, 0])
    y_axis = np.array([0, s*5, 0])
    z_axis = np.array([0, 0, s*5])
    
    ax.quiver(0,0,0,*x_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*x_axis),'x',fontsize=10) 
    ax.quiver(0,0,0,*y_axis, color = 'k', alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*y_axis),'y',fontsize=10) 
    ax.quiver(0,0,0,*z_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*z_axis),'z',fontsize=10)
    ax.set_xlim3d(-10, 10)
    ax.set_ylim3d(-5, 5)
    ax.set_zlim3d(-5, 5)

def plot(i):
    X = np.arange(-5+i, 5+i, 0.25)
    Y = np.arange(-5, 5, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    
    return [X,Y,Z]


def anime(i, ax, stepFactor):
    ax.cla()
    points = plot(i*stepFactor)
    ax.plot_surface(*points, alpha=0.5)
    ax.set_xlim3d(-10, 10)
    ax.set_ylim3d(-5, 5)
    ax.set_zlim3d(-5, 5)

        
animation = FuncAnimation(
                              fig,
                              anime,
                              init_func=initialState(ax),
                              frames=range(100),
                              fargs=(ax, 0.1)
                          )

Thanks for the help!谢谢您的帮助!

This is how I would do it: add two identical surfaces (at t=0) at the end of the init method.这就是我的做法:在 init 方法的末尾添加两个相同的表面(在 t=0 处)。 This will add two Poly3DCollection in the last two positions of ax.collections .这将在Poly3DCollection的最后两个位置添加两个ax.collections Then, ax.collections[-2] represents the surface at t=0, and ax.collections[-1] will be removed and readded in the animation function: it will represent the surface at t=i.然后, ax.collections[-2]表示 t=0 处的表面, ax.collections[-1]将被删除并重新添加到 animation function 中:它将表示 t=i 处的表面。

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

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

def initialState(ax):
    s=1.15 
    x_axis = np.array([s*5, 0, 0])
    y_axis = np.array([0, s*5, 0])
    z_axis = np.array([0, 0, s*5])
    
    ax.quiver(0,0,0,*x_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*x_axis),'x',fontsize=10) 
    ax.quiver(0,0,0,*y_axis, color = 'k', alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*y_axis),'y',fontsize=10) 
    ax.quiver(0,0,0,*z_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*z_axis),'z',fontsize=10)
    # add the initial surface
    surf0 = ax.plot_surface(*plot(0), color="tab:blue")
    # add another copy of the initial surface: this will be removed (and later
    # added again) by the anim function.
    surf1 = ax.plot_surface(*plot(0), color="tab:blue")
    ax.set_xlim3d(-10, 10)
    ax.set_ylim3d(-5, 5)
    ax.set_zlim3d(-5, 5)

def plot(i):
    X = np.arange(-5+i, 5+i, 0.25)
    Y = np.arange(-5, 5, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    return [X,Y,Z]


def anime(i, stepFactor):
    points = plot(i*stepFactor)
    # remove the last surface added in the previous iteration
    # (or in the init function)
    ax.collections[-1].remove()
    anim_surf = ax.plot_surface(*points, color="tab:blue", alpha=0.5)
  
animation = FuncAnimation(
    fig,
    anime,
    init_func=initialState(ax),
    frames=range(100),
    fargs=(0.1, )
)
plt.show()

EDIT: Second solution to accommodate comment: you can also add the surface in the global namespace, like i did below with surf_to_update .编辑:容纳评论的第二个解决方案:您还可以在全局命名空间中添加表面,就像我在下面使用surf_to_update所做的那样。 Then, inside anime you would have to use global surf_to_update .然后,在anime中你必须使用global surf_to_update It is not the nicest solution, but it is definitely easier than keeping track of all the things you are adding/updating.这不是最好的解决方案,但它绝对比跟踪您正在添加/更新的所有内容更容易。

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

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

surf_to_update = ax.plot_surface(*plot(0), color="tab:blue")

def initialState(ax):
    s=1.15 
    x_axis = np.array([s*5, 0, 0])
    y_axis = np.array([0, s*5, 0])
    z_axis = np.array([0, 0, s*5])
    
    ax.quiver(0,0,0,*x_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*x_axis),'x',fontsize=10) 
    ax.quiver(0,0,0,*y_axis, color = 'k', alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*y_axis),'y',fontsize=10) 
    ax.quiver(0,0,0,*z_axis,color = 'k',  alpha =1, arrow_length_ratio=0.05)
    ax.text(*(s*z_axis),'z',fontsize=10)
    # add the initial surface
    surf0 = ax.plot_surface(*plot(0), color="tab:blue")
    
    ax.set_xlim3d(-10, 10)
    ax.set_ylim3d(-5, 5)
    ax.set_zlim3d(-5, 5)

def plot(i):
    X = np.arange(-5+i, 5+i, 0.25)
    Y = np.arange(-5, 5, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    return [X,Y,Z]


def anime(i, stepFactor):
    global surf_to_update
    points = plot(i*stepFactor)
    # remove the last surface added in the previous iteration
    # (or in the init function)
    surf_to_update.remove()
    surf_to_update = ax.plot_surface(*points, color="tab:blue", alpha=0.5)
  
animation = FuncAnimation(
    fig,
    anime,
    init_func=initialState(ax),
    frames=range(100),
    fargs=(0.1, )
)
plt.show()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM