在2D动画中绘制可变大小和位置的圆圈
我正在使用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 个回答
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还建议,与其在绘制一组新的圆形之前先移除每一个圆形,不如直接移动和调整现有圆形的大小。这确实是可行的:只需更改radius
和xy
这两个属性就可以了。我没有测试过这个方法,但可以想象这样做会比移除所有现有圆形再添加新的圆形要快。
绘制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()
这是动画的一帧: