绘制闪烁的斑点群,需要考虑几个群体属性:群体的生成位置 (xylim),斑点数量 (n),斑点的半径均值 (r),斑点的寿命均值 (delta)
而对于每一个斑点,又需要考虑斑点个体属性:出生时间 (start)、出生位置 (xy)、半径 (radius)、颜色 (color)、寿命 (deltas),以控制斑点的外形和闪烁速度
在 Spot 这个对象中,我定义了以下几个函数:
import time
import matplotlib.patches as pch
import matplotlib.pyplot as plt
import numpy as np
# coord.py 详见: https://blog.csdn.net/qq_55745968/article/details/129912954
from coord import CoordSys_2d
red = 'orangered'
orange = 'orange'
yellow = 'yellow'
green = 'greenyellow'
cyan = 'aqua'
blue = 'deepskyblue'
purple = 'mediumpurple'
pink = 'violet'
class Spot:
''' 闪烁斑点对象
xylim: xy 坐标区间, [[xmin, ymin], [xmax, ymax]]
n: 闪烁斑点的数量
r: 斑点的半径均值, 标准差为 r/2, 最小值为 r/4
delta: 斑点生存时间的均值, 标准差为 delta, 最小值为 delta/10'''
colors = [red, orange, yellow, green, cyan, blue, purple, pink]
def __init__(self, xylim: np.ndarray, n: int,
r: float = .2, delta: float = 1., alpha: float = .7):
# <群体属性>
self.xylim = xylim
self.n, self.r = n, r
self.delta = delta
self.alpha = alpha
# <个体属性>
# 出生时间, 生存时间
self.start = np.array([])
self.deltas = np.array([])
# 出生位置, 半径, 颜色
self.xy = np.ones([0, 2])
self.radius = np.array([])
self.color = np.array([])
# 生产斑点
self.produce()
def scatter(self, n):
return self.xylim[0] + np.random.rand(n, 2) * (self.xylim[1] - self.xylim[0])
def produce(self, filt=None):
# 筛除生存时间耗尽的斑点
if isinstance(filt, np.ndarray):
for key in ('start', 'color', 'xy', 'radius', 'deltas'):
setattr(self, key, getattr(self, key)[filt])
# 补全缺失的斑点
lack = self.n - self.xy.shape[0]
if lack > 0:
# 记录出生时间, 生存时间
self.start = np.concatenate((self.start, np.full(lack, fill_value=time.time())))
self.deltas = np.concatenate((self.deltas, np.maximum(np.random.normal(
loc=self.delta, scale=self.delta, size=lack), self.delta / 10)))
# 随机位置, 随机半径, 随机颜色
self.xy = np.concatenate((self.xy, self.scatter(lack)), axis=0)
self.radius = np.concatenate((self.radius, np.maximum(np.random.normal(
loc=self.r, scale=self.r / 2, size=lack), self.r / 4)))
self.color = np.concatenate((self.color, np.random.choice(self.colors, size=lack)))
def __call__(self, fig, state: CoordSys_2d = None):
''' 刷新斑点的透明度
state: CoordSys_2d 对象'''
x = time.time() - self.start
# y = 4/d^2 x (d - x)
alpha = self.alpha * np.maximum(4 / self.deltas ** 2 * x * (self.deltas - x), 0)
# 向图像添加斑点
for i, xy in enumerate(np.stack(state.apply(*self.xy.T), axis=-1) if state else self.xy):
patch = pch.Circle(xy, self.radius[i], alpha=alpha[i], edgecolor=None, facecolor=self.color[i])
fig.add_patch(patch)
self.produce(alpha > 0)
而对于闪烁斑点的旋转、平移,主要使用了 https://hebitzj.blog.csdn.net/article/details/129912954 中所编写的类 CoordSys_2d,这个类有以下几个主要函数:
利用这个类,可以实现闪烁斑点的持续旋转
if __name__ == '__main__':
plt.rcParams['figure.figsize'] = [6.4, 6.4]
fig = plt.subplot()
# 初始化齐次坐标系
state = CoordSys_2d()
# 初始化闪烁斑点
spot = Spot(xylim=np.array([[-2, 2], [-1, 1]]).T, n=100, r=.2, delta=1)
while True:
fig.cla()
plt.xlim([-2, 2])
plt.ylim([-2, 2])
# 使当前的齐次坐标系旋转 2°
state = state.transform(theta=2)
# 调用闪烁斑点的 __call__ 函数绘制
spot(fig, state=state)
plt.pause(0.01)