use std::marker::PhantomData;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait ParticleSystem {
type ParticleType: Particle;
type EmitterType: ParticleEmitter<ParticleType = Self::ParticleType>;
fn iter_particles(&self) -> impl Iterator<Item = &Self::ParticleType>;
fn iter_particles_mut(&mut self) -> impl Iterator<Item = &mut Self::ParticleType>;
fn iter_emitters(&self) -> impl Iterator<Item = &Self::EmitterType>;
fn iter_emitters_mut(&mut self) -> impl Iterator<Item = &mut Self::EmitterType>;
fn add_particle(&mut self, particle: Self::ParticleType);
fn add_emitter(&mut self, emitter: Self::EmitterType);
fn update_particles(&mut self, dt: f64) {
for particle in self.iter_particles_mut() {
particle.update(dt);
}
}
fn update_emitters(&mut self, dt: f64) -> Vec<Self::ParticleType> {
self.iter_emitters_mut()
.flat_map(|emitter| emitter.update(dt))
.collect()
}
fn clean_particles(&mut self);
fn clean_emitters(&mut self);
fn update(&mut self, dt: f64) {
let new_particles = self.update_emitters(dt);
for new_particle in new_particles {
self.add_particle(new_particle);
}
self.update_particles(dt);
self.clean_particles();
self.clean_emitters();
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct VecParticleSystem<P, E> {
particles: Vec<P>,
emitters: Vec<E>,
}
impl<P, E> Default for VecParticleSystem<P, E>
{
fn default() -> Self {
Self {
particles: Vec::default(),
emitters: Vec::default(),
}
}
}
impl<P: Particle, E: ParticleEmitter<ParticleType = P>> ParticleSystem for VecParticleSystem<P, E> {
type ParticleType = P;
type EmitterType = E;
fn iter_particles(&self) -> impl Iterator<Item = &Self::ParticleType> {
self.particles.iter()
}
fn iter_particles_mut(&mut self) -> impl Iterator<Item = &mut Self::ParticleType> {
self.particles.iter_mut()
}
fn iter_emitters(&self) -> impl Iterator<Item = &Self::EmitterType> {
self.emitters.iter()
}
fn iter_emitters_mut(&mut self) -> impl Iterator<Item = &mut Self::EmitterType> {
self.emitters.iter_mut()
}
fn add_particle(&mut self, particle: Self::ParticleType) {
self.particles.push(particle);
}
fn add_emitter(&mut self, emitter: Self::EmitterType) {
self.emitters.push(emitter);
}
fn clean_particles(&mut self) {
self.particles.retain(|particle| particle.is_alive());
}
fn clean_emitters(&mut self) {
self.emitters.retain(|emitter| emitter.is_alive());
}
}
pub type BaseParticleSystem<C> = VecParticleSystem<
Box<dyn Particle<Coordinate = C>>,
Box<dyn ParticleEmitter<ParticleType = Box<dyn Particle<Coordinate = C>>>>,
>;
pub trait ParticleEmitter {
type ParticleType: Particle;
fn update(&mut self, dt: f64) -> Vec<Self::ParticleType>;
fn is_alive(&self) -> bool;
}
impl<E: ParticleEmitter + ?Sized> ParticleEmitter for Box<E> {
type ParticleType = E::ParticleType;
fn update(&mut self, dt: f64) -> Vec<Self::ParticleType> {
E::update(self, dt)
}
fn is_alive(&self) -> bool {
E::is_alive(self)
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NullParticleEmitter<P> {
phantom: PhantomData<P>,
}
impl<P: Particle> ParticleEmitter for NullParticleEmitter<P> {
type ParticleType = P;
fn update(&mut self, _dt: f64) -> Vec<Self::ParticleType> {
vec![]
}
fn is_alive(&self) -> bool {
false
}
}
pub trait Particle {
type Coordinate;
fn get_position(&self) -> Self::Coordinate;
fn update(&mut self, dt: f64);
fn is_alive(&self) -> bool;
}
impl<P: Particle + ?Sized> Particle for Box<P> {
type Coordinate = P::Coordinate;
fn get_position(&self) -> Self::Coordinate {
P::get_position(self)
}
fn update(&mut self, dt: f64) {
P::update(self, dt);
}
fn is_alive(&self) -> bool {
P::is_alive(self)
}
}
pub trait Aging {
fn get_age(&self) -> f64;
fn set_age(&mut self, age: f64);
}
pub trait MaxAging: Aging {
fn get_max_age(&self) -> f64;
fn get_age_percent(&self) -> f64 {
self.get_age() / self.get_max_age()
}
fn is_alive(&self) -> bool {
self.get_age_percent() < 1.0
}
}