Skip to main content

particula_rs/
lib.rs

1use std::marker::PhantomData;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6/// A collection of particles and emitters
7pub trait ParticleSystem {
8    /// The type of particle that this system will contain
9    type ParticleType: Particle;
10
11    /// The type of emitter that this system will contain
12    type EmitterType: ParticleEmitter<ParticleType = Self::ParticleType>;
13
14    /// Returns an iterator over all currently alive particles in the system
15    fn iter_particles(&self) -> impl Iterator<Item = &Self::ParticleType>;
16
17    /// Returns a mutable iterator over all currently alive particles in the system
18    fn iter_particles_mut(&mut self) -> impl Iterator<Item = &mut Self::ParticleType>;
19
20    /// Returns an iterator over all currently alive particle emitters in the system
21    fn iter_emitters(&self) -> impl Iterator<Item = &Self::EmitterType>;
22
23    /// Returns a mutable iterator over all currently alive particle emitters in the system
24    fn iter_emitters_mut(&mut self) -> impl Iterator<Item = &mut Self::EmitterType>;
25
26    /// Adds a particle to the system
27    fn add_particle(&mut self, particle: Self::ParticleType);
28
29    /// Adds an emitter to the system
30    fn add_emitter(&mut self, emitter: Self::EmitterType);
31
32    /// Iterates over all currently alive particles in the system and calls their update method
33    fn update_particles(&mut self, dt: f64) {
34        for particle in self.iter_particles_mut() {
35            particle.update(dt);
36        }
37    }
38
39    /// Iterates over all currently alive particle emitters in the system and calls their update method, returning the vector of new particles to add to the system
40    fn update_emitters(&mut self, dt: f64) -> Vec<Self::ParticleType> {
41        self.iter_emitters_mut()
42            .flat_map(|emitter| emitter.update(dt))
43            .collect()
44    }
45
46    /// Removes dead particles from the system
47    fn clean_particles(&mut self);
48
49    /// Removes dead emitters from the system
50    fn clean_emitters(&mut self);
51
52    /// Updates the particle system
53    ///
54    /// This method is comprised of 3 steps:
55    /// 1. Update emitters and add the new particles to the system
56    /// 2. Update all particles in the system
57    /// 3. Remove dead particles and emitters from the system
58    fn update(&mut self, dt: f64) {
59        let new_particles = self.update_emitters(dt);
60
61        for new_particle in new_particles {
62            self.add_particle(new_particle);
63        }
64
65        self.update_particles(dt);
66
67        self.clean_particles();
68        self.clean_emitters();
69    }
70}
71
72/// A base particle system using vectors to store the particles and emitters
73///
74/// This should suffice for most particle system needs
75#[derive(Debug, Clone)]
76#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
77pub struct VecParticleSystem<P, E> {
78    particles: Vec<P>,
79    emitters: Vec<E>,
80}
81
82impl<P, E> Default for VecParticleSystem<P, E>
83{
84    fn default() -> Self {
85        Self {
86            particles: Vec::default(),
87            emitters: Vec::default(),
88        }
89    }
90}
91
92impl<P: Particle, E: ParticleEmitter<ParticleType = P>> ParticleSystem for VecParticleSystem<P, E> {
93    /// This system can hold any particle that implements `Particle` with the same `Coordinate` type
94    type ParticleType = P;
95
96    /// This system can hold any emitter that emits any particle that implements `Particle` with the same `Coordinate` type
97    type EmitterType = E;
98
99    fn iter_particles(&self) -> impl Iterator<Item = &Self::ParticleType> {
100        self.particles.iter()
101    }
102
103    fn iter_particles_mut(&mut self) -> impl Iterator<Item = &mut Self::ParticleType> {
104        self.particles.iter_mut()
105    }
106
107    fn iter_emitters(&self) -> impl Iterator<Item = &Self::EmitterType> {
108        self.emitters.iter()
109    }
110
111    fn iter_emitters_mut(&mut self) -> impl Iterator<Item = &mut Self::EmitterType> {
112        self.emitters.iter_mut()
113    }
114
115    fn add_particle(&mut self, particle: Self::ParticleType) {
116        self.particles.push(particle);
117    }
118
119    fn add_emitter(&mut self, emitter: Self::EmitterType) {
120        self.emitters.push(emitter);
121    }
122
123    fn clean_particles(&mut self) {
124        self.particles.retain(|particle| particle.is_alive());
125    }
126
127    fn clean_emitters(&mut self) {
128        self.emitters.retain(|emitter| emitter.is_alive());
129    }
130}
131
132pub type BaseParticleSystem<C> = VecParticleSystem<
133    Box<dyn Particle<Coordinate = C>>,
134    Box<dyn ParticleEmitter<ParticleType = Box<dyn Particle<Coordinate = C>>>>,
135>;
136
137/// Creates new particles
138pub trait ParticleEmitter {
139    /// The type of the particles to be emitted
140    type ParticleType: Particle;
141
142    /// Update the state of the emitter and return a vector of particles to add to the system
143    fn update(&mut self, dt: f64) -> Vec<Self::ParticleType>;
144
145    /// Returns false if the emitter should be removed from the system
146    fn is_alive(&self) -> bool;
147}
148
149impl<E: ParticleEmitter + ?Sized> ParticleEmitter for Box<E> {
150    type ParticleType = E::ParticleType;
151
152    fn update(&mut self, dt: f64) -> Vec<Self::ParticleType> {
153        E::update(self, dt)
154    }
155
156    fn is_alive(&self) -> bool {
157        E::is_alive(self)
158    }
159}
160
161/// A particle emitter that never emits particles and is never alive.
162/// Useful for when you don't really need emitters in your particle system.
163#[derive(Debug, Clone, Copy)]
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165pub struct NullParticleEmitter<P> {
166    phantom: PhantomData<P>,
167}
168
169impl<P: Particle> ParticleEmitter for NullParticleEmitter<P> {
170    type ParticleType = P;
171
172    fn update(&mut self, _dt: f64) -> Vec<Self::ParticleType> {
173        vec![]
174    }
175
176    fn is_alive(&self) -> bool {
177        false
178    }
179}
180
181/// A representation of some particle space
182pub trait Particle {
183    /// The position type of the particle
184    type Coordinate;
185
186    /// The position of the particle in space
187    fn get_position(&self) -> Self::Coordinate;
188
189    /// Updates the state of the particle
190    fn update(&mut self, dt: f64);
191
192    /// Returns false if the particle should be removed from the system
193    fn is_alive(&self) -> bool;
194}
195
196impl<P: Particle + ?Sized> Particle for Box<P> {
197    type Coordinate = P::Coordinate;
198
199    fn get_position(&self) -> Self::Coordinate {
200        P::get_position(self)
201    }
202
203    fn update(&mut self, dt: f64) {
204        P::update(self, dt);
205    }
206
207    fn is_alive(&self) -> bool {
208        P::is_alive(self)
209    }
210}
211
212/// Tracks age in a particle
213pub trait Aging {
214    /// Gets the current age of the particle
215    fn get_age(&self) -> f64;
216
217    /// Sets the current age of the particle
218    fn set_age(&mut self, age: f64);
219}
220
221/// Represents a particle that dies after a set amount of time
222pub trait MaxAging: Aging {
223    /// Gets the max age of the particle
224    fn get_max_age(&self) -> f64;
225
226    /// Gets the age of the particle from 0.0 to 1.0
227    fn get_age_percent(&self) -> f64 {
228        self.get_age() / self.get_max_age()
229    }
230
231    /// Returns false if the particle's age percent is greater than or equal to 1.0
232    fn is_alive(&self) -> bool {
233        self.get_age_percent() < 1.0
234    }
235}