首页 > 解决方案 > 如何制作多个移动点的动画?

问题描述

我正在尝试制作一个显示 100 个移动点的动画的脚本。每个点将走 100 步,之后,每个点都被不同的点替换,这将再走 100 步。我想像 1k 次(1000 代点)那样做这个过程。在每一代中——100 个点能够完成 100 个步骤。我从泡菜中读取坐标,但我不知道应该如何为这些动作设置动画。我写了一些代码,但我有点知道这是错误的,我不知道下一步该怎么做。我正在等待一些帮助;) PS:坐标保存为张量

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

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')


def init():
    ax.set_xlim(0, 20) 
    ax.set_ylim(0, 20)
    return ln,


def update(i):
    with open("output.txt", "rb") as file:
        try:
            while True:
                for point, coordinate in pickle.load(file).items():
                    for i in range(100):
                        if point == i:
                            xdata.append(coordinate.cpu().numpy()[0]) 
                            ydata.append(coordinate.cpu().numpy()[1])
                            ln.set_data(xdata, ydata)
                            return ln,
        except EOFError:
            pass

ani = FuncAnimation(fig, update, np.arange(1, 1000), init_func=init, blit=True)
plt.show()

output.txt是一个巨大的文件,其内容是这样生成的:

output = open("output.txt", "wb")
for i in range(number_of_points):
    self.points[i].point_step()
    points = { i: self.points[i].point_location }
    pickle.dump(points, output)
    output.close()

标签: pythonmatplotlib

解决方案


如果我理解正确,文件 ,output.txt包含一系列腌制的字典。每个字典的形式

{ i: self.points[i].point_location }

每次调用update都应从此文件中读取 100 个(新)字典。我们可以通过创建一个生成器函数来做到这一点,get_data它一次从文件中生成一个项目。然后定义

data = get_data(path)

在外部update并将其update作为参数传递给(使用FuncAnimation'sfargs参数。) 在内部update,循环

for point, coordinate in itertools.islice(data, 100):

遍历 100 个项目data。由于data是一个迭代器,每次调用它itertools.islice都会产生 100 个项目。data


import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pickle
import itertools as IT


def init():
    ax.set_xlim(0, 20)
    ax.set_ylim(0, 20)
    return (pathcol,)


def pickleLoader(f):
    """
    Return generator which yields unpickled objects from file object f
    # Based on https://stackoverflow.com/a/18675864/190597 (Darko Veberic)
    """
    try:
        while True:
            yield pickle.load(f)
    except EOFError:
        pass


def get_data(path):
    with open(path, "rb") as f:
        for dct in pickleLoader(f):
            yield from dct.items()


def update(i, data, pathcol, texts, title, num_points):
    title.set_text("Generation {}".format(i))
    xdata, ydata, color = [], [], []
    for point, coordinate in IT.islice(data, num_points):
        texti = texts[point]
        x, y = coordinate.cpu().numpy()
        xdata.append(x)
        ydata.append(y)
        color.append(point)
        texti.set_position((x, y))
    color = np.array(color, dtype="float64")
    color /= num_points
    pathcol.set_color = color
    pathcol.set_offsets(np.column_stack([xdata, ydata]))

    return [pathcol, title] + texts


class Coord:
    # just to make the code runnable
    def __init__(self, coord):
        self.coord = coord

    def cpu(self):
        return Cpu(self.coord)


class Cpu:
    # just to make the code runnable
    def __init__(self, coord):
        self.coord = coord

    def numpy(self):
        return self.coord


def make_data(path, num_frames=1000, num_points=100):
    # just to make the code runnable
    with open(path, "wb") as output:
        for frame in range(num_frames):
            for i in range(num_points):
                points = {i: Coord(20 * np.random.random((2,)))}
                pickle.dump(points, output)


def _blit_draw(self, artists, bg_cache):
    # https://stackoverflow.com/a/17562747/190597 (tacaswell)
    # Handles blitted drawing, which renders only the artists given instead
    # of the entire figure.
    updated_ax = []
    for a in artists:
        # If we haven't cached the background for this axes object, do
        # so now. This might not always be reliable, but it's an attempt
        # to automate the process.
        if a.axes not in bg_cache:
            # bg_cache[a.axes] = a.figure.canvas.copy_from_bbox(a.axes.bbox)
            # change here
            bg_cache[a.axes] = a.figure.canvas.copy_from_bbox(a.axes.figure.bbox)
        a.axes.draw_artist(a)
        updated_ax.append(a.axes)

    # After rendering all the needed artists, blit each axes individually.
    for ax in set(updated_ax):
        # and here
        # ax.figure.canvas.blit(ax.bbox)
        ax.figure.canvas.blit(ax.figure.bbox)


# MONKEY PATCH!!
matplotlib.animation.Animation._blit_draw = _blit_draw

num_points = 100
num_frames = 1000
fig, ax = plt.subplots()
pathcol = ax.scatter(
    [0] * num_points, [0] * num_points, c=np.linspace(0, 1, num_points), s=100
)
title = ax.text(
    0.5,
    1.05,
    "",
    transform=ax.transAxes,
    horizontalalignment="center",
    fontsize=15,
    animated=True,
)

texts = []
for i in range(num_points):
    t = ax.text(0, 0, str(i), fontsize=10, animated=True)
    texts.append(t)

path = "/tmp/output.txt"
make_data(path, num_frames, num_points)  # just to make the code runnable
data = get_data(path)

ani = FuncAnimation(
    fig,
    update,
    range(1, num_frames + 1),
    init_func=init,
    blit=True,
    fargs=(data, pathcol, texts, title, num_points),
    interval=1000,  # decrease to speed up the animation
    repeat=False,
)
plt.show()

推荐阅读