Source code for gale.particle_system

"""
This file contains the classes Particle (a single point with
position/acceleration/velocity/lifetime, using numpy for the physics
integration) and ParticleSystem, spawning and managing bursts of them
— explosions, sparks, trails, and similar effects.

Author: Alejandro Mujica (aledrums@gmail.com)
"""

from typing import Callable, List, Optional

import numpy as np

import pygame


[docs] class Particle: def __init__( self, x: float, y: float, ax: float, ay: float, life_time: float, color: pygame.Color, ) -> None: """ Set the initial value for a particle :param x: X position. :param y: Y position. :param ax: X acceleration. :param ay: Y acceleration. :param life_time: duration in seconds of the particle. :param color: render color. """ # Position self.x: float = x self.y: float = y # Velocity self.vx: float = 0 self.vy: float = 0 # Acceleration self.ax: float = ax self.ay: float = ay self.life_time: float = life_time self.color: pygame.Color = color
[docs] def update(self, dt: float) -> None: self.vx += self.ax * dt self.vy += self.ay * dt self.x += self.vx self.y += self.vy
[docs] def render(self, surface: pygame.Surface) -> None: s = pygame.Surface((4, 4)) s.set_alpha(self.color[3]) pygame.draw.circle(s, self.color, (2, 2), 2) surface.blit(s, (int(self.x), int(self.y)))
[docs] class ParticleSystem: def __init__( self, x: float, y: float, n: int, on_finish: Optional[Callable[[], None]] = None ) -> None: """ Builds a particle system. :param x: Center x of the system. :param y: Center y of the system. :param n: Number of particles. """ self.x_mean: float = x self.y_mean: float = y self.size: int = n self.min_life_time: float = 0 self.max_life_time: float = 0 self.timer: float = 0 self.ax1: float = 0 self.ax2: float = 0 self.ay1: float = 0 self.ay2: float = 0 self.x_desv: float = 1 self.y_desv: float = 1 self.colors: List[pygame.Color] = [] self.particles: List[Particle] = [] self.on_finish = on_finish or (lambda: None)
[docs] def set_life_time(self, minimum: float, maximum: float) -> None: self.min_life_time = minimum self.max_life_time = maximum
[docs] def set_linear_acceleration( self, x1: float, y1: float, x2: float, y2: float ) -> None: self.ax1 = x1 self.ay1 = y1 self.ax2 = x2 self.ay2 = y2
[docs] def set_colors(self, colors: List[pygame.Color]) -> None: self.colors = colors
[docs] def set_area_spread(self, rx: float, ry: float) -> None: self.x_desv = rx self.y_desv = ry
[docs] def generate(self) -> None: for _ in range(self.size): ax: float = np.random.uniform(self.ax1, self.ax2) ay: float = np.random.uniform(self.ay1, self.ay2) px: float = np.random.normal(self.x_mean, self.x_desv) py: float = np.random.normal(self.y_mean, self.y_desv) color: pygame.Color = self.colors[np.random.choice(len(self.colors))] life_time: float = np.random.uniform(self.min_life_time, self.max_life_time) self.particles.append(Particle(px, py, ax, ay, life_time, color))
[docs] def update(self, dt: float) -> None: if len(self.particles) == 0: return self.timer += dt if self.timer >= self.max_life_time: self.timer = 0 self.particles = [] self.on_finish() for particle in self.particles: if self.timer < particle.life_time: particle.update(dt)
[docs] def render(self, surface: pygame.Surface) -> None: for particle in self.particles: if self.timer < particle.life_time: particle.render(surface)