如何使用FuncAnimation和blit = True为Poly3DCollection设置动画? [英] How to animate Poly3DCollection using FuncAnimation with blit=True?

查看:120
本文介绍了如何使用FuncAnimation和blit = True为Poly3DCollection设置动画?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为旋转的立方体设置动画.为此,我使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆