matplotlib homogeneous coordinate system draws 2D flickering blobs

To draw a flickering blob group, several population attributes need to be considered: the generation position of the group (xylim), the number of blobs (n), the mean radius of the blob (r), and the mean life span of the blob (delta)

For each spot, individual properties of the spot need to be considered: birth time (start), birth position (xy), radius (radius), color (color), lifespan (deltas), to control the shape and flashing speed of the spot

In the Spot object, I defined the following functions:

  • __init__: Write the formal parameter into the population attribute of the flickering spots, and call produce to generate the specified number of spots
  • produce: filter out the spots whose lifespan is exhausted, and generate the individual attributes of new spots
  • scatter: Generate the center point of the spot in the specified box according to the group attribute xylim, which can be rewritten
  • __call__: According to the survival time of the flickering spots, calculate the opacity of the flickering spots, and use CoordSys_2d to perform affine transformation on the position of the spots, and then draw the spots one by one
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)

 For the rotation and translation of flickering spots, the class Coord_2d written in https://hebitzj.blog.csdn.net/article/details/129912954 is mainly used  . This class has the following main functions:

  • transform: Given the offset and rotation angle on the x and y axes, and perform relative transformation/absolute transformation
  • apply: Given a point set describing x, y, translate and rotate the point set according to the homogeneous coordinate system matrix

Using this class, you can achieve continuous rotation of blinking spots

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)

Guess you like

Origin blog.csdn.net/qq_55745968/article/details/130049672