【问题标题】:Correct implementation for animation in matplotlibmatplotlib 中动画的正确实现
【发布时间】:2020-12-13 10:13:24
【问题描述】:

我正在尝试使用 FuncAnimation 在 matplotlib 中旋转 3D 立方体。然而,对于某种 70 年代的织物艺术来说,它不仅仅是显示一个立方体的单个渲染,它只是在自身上绘制动画。

在许多其他错误开始之后,这是我得到的最接近的结果,但显然我没有正确使用 FuncAnimation。对于我哪里出错的任何提示和解释,我将不胜感激。 (这是在 Jupyter 笔记本中运行的)

%matplotlib notebook
import numpy as np
from numpy import sin, cos, pi
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation

rot_num = 1 # number of rotations
smoothness = 90 # how many steps per rotation
# Define corners of a cube
cube = np.array([[0,0,1],[1,0,1],[1,1,1],[0,1,1],[0,0,0],[1,0,0],[1,1,0],[0,1,0]])
angles = np.linspace(0, rot_num*2*pi, smoothness*rot_num)
points = np.zeros(shape=(len(cube), 3, len(angles)),  dtype=np.float16)
# Calculate all the points needed for rotation
for i in range(len(points)):
    newX = cube[i,0] * cos(angles) - cube[i,2] * sin(angles)
    newY = cube[i,1]
    newZ = cube[i,2] * cos(angles) + cube[i,0] * sin(angles)
    points[i,0] = newX
    points[i,1] = newY
    points[i,2] = newZ
# Define the vertices/lines of the cube using corners, with color
cube_v = [[points[0], points[1], "green"],
          [points[1], points[2], "green"],
          [points[2], points[3], "green"],
          [points[3], points[0], "green"],
          [points[0], points[4], "blue"],
          [points[1], points[5], "blue"],
          [points[2], points[6], "blue"],
          [points[3], points[7], "blue"],
          [points[4], points[5], "red"],
          [points[5], points[6], "red"],
          [points[6], points[7], "red"],
          [points[7], points[4], "red"]]
    
fig = plt.figure()
plt.rcParams["figure.figsize"] = 9,9
ax = fig.add_subplot(111, projection="3d", autoscale_on=True)
ax.grid()
ax.set_title('3D Animation')
ax.set_xlim3d([-2.0, 2.0])
ax.set_xlabel('X')
ax.set_ylim3d([-2.0, 2.0])
ax.set_ylabel('Y')
ax.set_zlim3d([-2.0, 2.0])
ax.set_zlabel('Z')

def update(i): 
    for vertex in cube_v:
        line = ax.plot([vertex[0][0][i], vertex[1][0][i]], 
                       [vertex[0][1][i], vertex[1][1][i]], 
                       [vertex[0][2][i], vertex[1][2][i]], 
                       vertex[2])
        
ani = animation.FuncAnimation(fig, update, frames=len(angles), interval=20, blit=False, repeat=False)
plt.show()

【问题讨论】:

  • 在创建新的plot() 之前,您必须先clear() plot/figure/ax。或者您必须只创建/绘制line 一次,以后只需替换现有line 中的data

标签: python matplotlib animation 3d


【解决方案1】:

您可以在绘制新立方体之前使用ax.clear()清除所有内容

def update(i):
    ax.clear()
    
    for vertex in cube_v:
        line = ax.plot([vertex[0][0][i], vertex[1][0][i]], 
                       [vertex[0][1][i], vertex[1][1][i]], 
                       [vertex[0][2][i], vertex[1][2][i]], 
                       vertex[2])

但这也会删除其他设置,因此它会重新缩放轴、删除标题等。这不是您的设置的好解决方案。


在绘制新立方体之前,您可以保留列表中的所有行并remove()它们

all_lines = []

def update(i):
    #global all_lines
    
    # remove previous lines
    for line in all_lines:
        line.remove()
    
    # clear list     
    #all_lines = []    # needs `global`
    all_lines.clear()  # doesn't need `global`
    
    # draw new lines
    for vertex in cube_v:
        line = ax.plot([vertex[0][0][i], vertex[1][0][i]], 
                       [vertex[0][1][i], vertex[1][1][i]], 
                       [vertex[0][2][i], vertex[1][2][i]], 
                       vertex[2])

        # plot may create many lines so it gives list (even for single line)
        all_lines.append(line[0])

第三种方法可以是在开始时创建空的lines,然后只替换行中的.data。我没有此方法的示例,因为它需要更多更改。


似乎这种方法不适用于Jupyter,而只能使用普通的python script.py

最流畅的动画给了我blit=True。我必须用新行返回列表,它会自动删除旧行并放置新行。大概是用优化的方法替换item,所以很流畅。

def update(i):
    all_lines = []
    
    for vertex in cube_v:
        line = ax.plot([vertex[0][0][i], vertex[1][0][i]], 
                       [vertex[0][1][i], vertex[1][1][i]], 
                       [vertex[0][2][i], vertex[1][2][i]], 
                       vertex[2])

        # plot may create many lines so it gives list (even for single line)
        all_lines.append(line[0])
        
    return all_lines

ani = animation.FuncAnimation(fig, update, 
                              frames=len(angles), 
                              interval=20, 
                              blit=True,  # <-- True
                              repeat=False)

【讨论】:

  • 非常感谢,这非常有用,因为它让我走上了正轨。第三种方法让我找到了似乎是最好的解决方案,因为它避免了大量的重绘。
【解决方案2】:

感谢 Furas,这是我想出的解决方案,它只是替换初始行中的数据,让 FuncAnimation 处理其余部分。 这个页面也有帮助https://brushingupscience.com/2016/06/21/matplotlib-animations-the-easy-way/

# initialize with first set of lines and color
lines = [ax.plot([vertex[0][0][0], vertex[1][0][0]], 
                 [vertex[0][1][0], vertex[1][1][0]], 
                 [vertex[0][2][0], vertex[1][2][0]], 
                 vertex[2]) for vertex in cube_v]

# only change the x,y,z coordinates and return updated object
def update(i):
    out = [lines[v][0].set_data_3d([vertex[0][0][i], vertex[1][0][i]], 
                                   [vertex[0][1][i], vertex[1][1][i]], 
                                   [vertex[0][2][i], vertex[1][2][i]]) for v, vertex in enumerate(cube_v)]
    return lines

ani = animation.FuncAnimation(fig, update, frames=len(angles), interval=10, blit=True, repeat=False)
plt.show() 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 2022-09-25
    • 2020-05-27
    • 2019-04-24
    • 1970-01-01
    • 2020-11-24
    • 2021-04-11
    相关资源
    最近更新 更多