在2D动画中绘制可变大小和位置的圆圈

4 投票
2 回答
2747 浏览
提问于 2025-04-18 06:17

我正在使用Python 3.3中的matplotlib库。我有一个可以做动画的2D和3D窗口,在这个窗口上我会画一些点。这些点代表一些物体,但并不一定意味着它们真的存在。所以我想在这些点周围画一个圈,以显示不确定性。这种不确定性是会变化的,所以总的来说,我想在2D动画中画多个小圆圈,圆圈的中心和半径都是可变的,可能有0个,也可能有100个。

我试过使用scatter(散点图)。scatter看起来很有前途,但当我调整屏幕大小时,scatter的大小是以像素为单位的,而不是和坐标轴的大小有关。有没有什么办法可以做到这一点?

我尝试了以下的scatter代码:

#In the init
self.circles = self.ax.scatter([],[],[],c = 'b',alpha=0.6)
#In the animate function
self.circles.set_offsets([[10],[15]])    
self.circles._sizes = [2000]

中心点是正确的,x = 10,y = 15。但是大小是以像素为单位的,和坐标轴没有关系。在这种情况下,我本来希望圆圈能在x = 10和y = 20015的位置,比如说。但实际上并没有。

当我调整窗口大小时,这就成了一个问题。

2 个回答

0

Tcaswell在上面的评论中给出了答案,所以如果你想点赞这个回答,也请给他点赞。

解决办法是使用一个圆形:http://matplotlib.org/api/artist_api.html#matplotlib.patches.Circle。这些圆形的工作方式和axis.plot以及axis.scatter很不一样,添加它们时需要这样做:

i = pyplot.Circle((item["x"],item["y"]), radius=item["inaccuracy"], fc='None'))
self.ax.add_patch(i)

而要移除它们则需要:

i.remove()

tcaswell还建议,与其在绘制一组新的圆形之前先移除每一个圆形,不如直接移动和调整现有圆形的大小。这确实是可行的:只需更改radiusxy这两个属性就可以了。我没有测试过这个方法,但可以想象这样做会比移除所有现有圆形再添加新的圆形要快。

2

绘制Collections会更快,所以我对scatter()返回的PathCollection对象的draw()函数进行了修改。新的draw()函数使用transData来计算每个圆的大小,它把每个圆的大小当作直径来用。

import pylab as pl
import numpy as np
from matplotlib.collections import Collection
from matplotlib import transforms
import matplotlib.animation as animation

fig = pl.figure()

N = 40
x, y = np.random.rand(2, N)
color = np.random.rand(N)
r = np.random.rand(N) * 0.05 + 0.05

c = pl.scatter(x, y, s=r, c=color, alpha=0.5, animated=True)
pl.gca().set_aspect("equal")

def draw(self, renderer):
    if self._sizes is not None:
        m = self.axes.transData.get_matrix()
        self._transforms = [
            transforms.Affine2D().scale(m[0, 0]*x, m[1, 1]*x)
            for x in self._sizes]
    return Collection.draw(self, renderer)

c.draw = lambda renderer:draw(c, renderer)

def update_loc(n):
    global x2, y2, x, y
    n = n % 50
    if n == 0:
        x2, y2 = np.random.rand(2, N)
    x += (x2 - x) * 0.1
    y += (y2 - y) * 0.1
    c.set_offsets(np.c_[x, y])
    return c,

ani = animation.FuncAnimation(fig, update_loc, 2500, interval=50, blit=True)

pl.show()

这是动画的一帧:

在这里输入图片描述

撰写回答