如何使用FuncAnimation和blit = True为Poly3DCollection设置动画? [英] How to animate Poly3DCollection using FuncAnimation with blit=True?
问题描述
我正在尝试为旋转的立方体设置动画.为此,我使用Poly3DCollection并使用FuncAnimation对其进行动画处理:
anim = animation.FuncAnimation(fig,visualize_rotation,fargs = [collection],init_func = partial(init_func,ax,collection),帧= 360,间隔= 1000/30)
但是它使每个帧的渲染速度都很慢,所以我每秒只能得到几帧.为了解决这个问题,我尝试添加参数 blit = True
,希望它可以提高渲染速度,但是这样我看不到立方体.
这是我在窗口中看到的:
保存图形时,足够奇怪的是可见该多维数据集.这是我得到的结果:
我确保 visualize_rotation
返回 blit = True
所需的艺术家列表 [collection]
,如
这是窗口很小时的图:
图的开始显示窗口的大小调整.在第二种情况下,仅丢弃了2帧(分别约为50和150),并且根据需要,总体帧速率约为30 fps.当窗口大小正常时,我正在寻找相同的行为.当我打开 blit
时,该图看起来不错,但问题是该多维数据集不可见.
我为您找到了一种单线修复程序:在更新顶点后添加do_3d_projection.
...#情节双方collection.set_verts(顶点)collection.do_3d_projection(collection.axes.get_figure().canvas.get_renderer())打印(帧)返回[收藏]
当blit = True时,可能没有在基础代码中调用它.这可能是一个错误.
另外,另一个错误弹出.当动画以blit = True模式重复时,最后一帧会以某种方式被拖延.要解决此问题,请在init_func中添加ax.clear()和ax.add_collection3d():
def init_func(ax,collection):ax.clear()ax.add_collection3d(集合)ax.set_xlim(-15,15)ax.set_ylim(-15,15)...
I'm trying to animate a rotating cube. For that I use Poly3DCollection and animate it using FuncAnimation:
anim = animation.FuncAnimation(fig, visualize_rotation, fargs=[collection],
init_func=partial(init_func, ax, collection),
frames=360, interval=1000 / 30)
But it renders each frame very slowly so that I get just a few frames per second. To fix it I tried to add parameter blit=True
in the hope that it will improve rendering speed, but this way I cannot see the cube.
This is what I see in the window:
Weirdly enough the cube is visible when saving the figure. This is the result I get:
I made sure that visualize_rotation
returns list of artists [collection]
that is required by blit=True
as noted in this question, but the cube is still not visible.
So, how can I use blit
flag in this case, while being able to see the cube during the animation?
Full code:
import math
from functools import partial
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
def visualize_rotation(frame, collection):
angle = math.radians(2) * frame
points = np.array([[-1, -1, -1],
[1, -1, -1],
[1, 1, -1],
[-1, 1, -1],
[-1, -1, 1],
[1, -1, 1],
[1, 1, 1],
[-1, 1, 1]])
Z = np.zeros((8, 3))
for i in range(8):
Z[i, :] = [
math.cos(angle) * points[i, 0] - math.sin(angle) * points[i, 1],
math.sin(angle) * points[i, 0] + math.cos(angle) * points[i, 1],
points[i, 2]
]
Z = 10.0 * Z
# list of sides' polygons of figure
vertices = [[Z[0], Z[1], Z[2], Z[3]],
[Z[4], Z[5], Z[6], Z[7]],
[Z[0], Z[1], Z[5], Z[4]],
[Z[2], Z[3], Z[7], Z[6]],
[Z[1], Z[2], Z[6], Z[5]],
[Z[4], Z[7], Z[3], Z[0]]]
# plot sides
collection.set_verts(vertices)
print(frame)
return [collection]
def init_func(ax, collection):
ax.set_xlim(-15, 15)
ax.set_ylim(-15, 15)
ax.set_zlim(-15, 15)
ax.set_box_aspect(np.ptp([ax.get_xlim(), ax.get_ylim(), ax.get_zlim()], axis=1))
return [collection]
def animate_rotation():
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d', proj_type='persp')
collection = Poly3DCollection([[np.zeros(3)]], facecolors='white',
linewidths=1, edgecolors='r', alpha=0.8)
ax.add_collection3d(collection)
# noinspection PyUnusedLocal
anim = animation.FuncAnimation(fig, visualize_rotation, fargs=[collection],
init_func=partial(init_func, ax, collection),
frames=360, interval=1000 / 30, blit=True)
plt.show()
Edit:
I've added computation of frames per second and plotted it:
timestamps = []
def visualize_rotation(frame, collection):
...
# plot sides
collection.set_verts(vertices)
global timestamps
timestamps.append(time.time())
print(round(1 / np.mean(np.diff(timestamps[-1000:])), 1))
return [collection]
def animate_rotation():
...
plt.plot(np.diff(timestamps))
plt.ylim([0, 0.1])
plt.show()
This is what happens when the window is in normal size and the drawing speed is slow (time in seconds vs frame number):
And this is the plot when the window is tiny:
The start of the plot shows resizing of the window. Only 2 frames were dropped (at about 50 and 150) in the second case, and the overall frame rate is about 30 fps as desired. I'm looking for the same kind of behavior when the window is normally sized. When I turn blit
on, the plot looks fine, but the problem is that the cube is not visible.
I found a one-liner fix for you: add do_3d_projection after you update the vertices.
...
# plot sides
collection.set_verts(vertices)
collection.do_3d_projection(collection.axes.get_figure().canvas.get_renderer())
print(frame)
return [collection]
It's probably a bug that it's not being called in the underlying code when blit=True.
Also, another bug pops up; the last frame is somehow getting carried over when the animation repeats in blit=True mode. To fix this, add ax.clear() and ax.add_collection3d() in your init_func:
def init_func(ax, collection):
ax.clear()
ax.add_collection3d(collection)
ax.set_xlim(-15, 15)
ax.set_ylim(-15, 15)
...
这篇关于如何使用FuncAnimation和blit = True为Poly3DCollection设置动画?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!