vox_geometry_rust/
point_particle_emitter2.rs

1/*
2 * // Copyright (c) 2021 Feng Yang
3 * //
4 * // I am making my contributions/submissions to this project solely in my
5 * // personal capacity and am not conveying any rights to any intellectual
6 * // property of any third parties.
7 */
8
9use crate::matrix2x2::*;
10use crate::vector2::Vector2D;
11use crate::particle_emitter2::*;
12use std::sync::{RwLock, Arc};
13use rand::prelude::*;
14use rand_pcg::{Pcg64, Lcg128Xsl64};
15
16///
17/// # Callback function type for update calls.
18///
19/// This type of callback function will take the emitter pointer, current
20/// time, and time interval in seconds.
21///
22pub type OnBeginUpdateCallback = fn(&mut PointParticleEmitter2, f64, f64);
23
24///
25/// # 2-D point particle emitter.
26///
27/// This class emits particles from a single point in given direction, speed,
28/// and spreading angle.
29///
30pub struct PointParticleEmitter2 {
31    _rng: Lcg128Xsl64,
32    _first_frame_time_in_seconds: f64,
33    _number_of_emitted_particles: usize,
34
35    _max_number_of_new_particles_per_second: usize,
36    _max_number_of_particles: usize,
37
38    _origin: Vector2D,
39    _direction: Vector2D,
40    _speed: f64,
41    _spread_angle_in_radians: f64,
42
43    _emitter_data: ParticleEmitter2Data,
44    _on_begin_update_callback: Option<OnBeginUpdateCallback>,
45}
46
47impl PointParticleEmitter2 {
48    ///
49    /// Constructs an emitter that spawns particles from given origin,
50    /// direction, speed, spread angle, max number of new particles per second,
51    /// max total number of particles to be emitted, and random seed.
52    ///
53    /// - parameter:   origin                            The origin.
54    /// - parameter:   direction                         The direction.
55    /// - parameter:   speed                             The speed.
56    /// - parameter:   spread_angle_in_degrees           The spread angle in degrees.
57    /// - parameter:   max_num_of_new_particles_per_sec  The max number of new particles per second.
58    /// - parameter:   max_num_of_particles              The max number of particles to be emitted.
59    /// - parameter:   seed                              The random seed.
60    ///
61    pub fn new(origin: &Vector2D,
62               direction: &Vector2D,
63               speed: f64,
64               spread_angle_in_degrees: f64,
65               max_num_of_new_particles_per_sec: Option<usize>,
66               max_num_of_particles: Option<usize>,
67               seed: Option<u64>) -> PointParticleEmitter2 {
68        return PointParticleEmitter2 {
69            _rng: Pcg64::seed_from_u64(seed.unwrap_or(0)),
70            _first_frame_time_in_seconds: 0.0,
71            _number_of_emitted_particles: 0,
72            _max_number_of_new_particles_per_second: max_num_of_new_particles_per_sec.unwrap_or(1),
73            _max_number_of_particles: max_num_of_particles.unwrap_or(usize::MAX),
74            _origin: *origin,
75            _direction: *direction,
76            _speed: speed,
77            _spread_angle_in_radians: crate::math_utils::degrees_to_radians(spread_angle_in_degrees),
78            _emitter_data: ParticleEmitter2Data::new(),
79            _on_begin_update_callback: None,
80        };
81    }
82
83    /// Returns builder fox PointParticleEmitter2.
84    pub fn builder() -> Builder {
85        return Builder::new();
86    }
87
88    /// Returns max number of new particles per second.
89    pub fn max_number_of_new_particles_per_second(&self) -> usize {
90        return self._max_number_of_new_particles_per_second;
91    }
92
93    /// Sets max number of new particles per second.
94    pub fn set_max_number_of_new_particles_per_second(&mut self, rate: usize) {
95        self._max_number_of_new_particles_per_second = rate;
96    }
97
98    /// Returns max number of particles to be emitted.
99    pub fn max_number_of_particles(&self) -> usize {
100        return self._max_number_of_particles;
101    }
102
103    /// Sets max number of particles to be emitted.
104    pub fn set_max_number_of_particles(&mut self, max_number_of_particles: usize) {
105        self._max_number_of_particles = max_number_of_particles;
106    }
107
108    fn emit(&mut self, new_positions: &mut Vec<Vector2D>,
109            new_velocities: &mut Vec<Vector2D>,
110            max_new_number_of_particles: usize) {
111        for _ in 0..max_new_number_of_particles {
112            let new_angle_in_radian = (self.random() - 0.5) * self._spread_angle_in_radians;
113            let rotation_matrix = Matrix2x2D::make_rotation_matrix(new_angle_in_radian);
114
115            new_positions.push(self._origin);
116            new_velocities.push((rotation_matrix * self._direction) * self._speed);
117        }
118    }
119
120    fn random(&mut self) -> f64 {
121        return self._rng.gen_range(0.0..1.0);
122    }
123
124    ///
125    /// \brief      Sets the callback function to be called when
126    ///             ParticleEmitter2::update function is invoked.
127    ///
128    /// The callback function takes current simulation time in seconds unit. Use
129    /// this callback to track any motion or state changes related to this
130    /// emitter.
131    ///
132    /// - parameter:   callback The callback function.
133    ///
134    pub fn set_on_begin_update_callback(&mut self, callback: OnBeginUpdateCallback) {
135        self._on_begin_update_callback = Some(callback);
136    }
137}
138
139impl ParticleEmitter2 for PointParticleEmitter2 {
140    fn update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) where Self: Sized {
141        match self._on_begin_update_callback {
142            None => {}
143            Some(callback) => {
144                callback(self, current_time_in_seconds,
145                         time_interval_in_seconds);
146            }
147        }
148
149        self.on_update(current_time_in_seconds, time_interval_in_seconds);
150    }
151
152    fn on_update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) {
153        let particles;
154        match self.target() {
155            None => {
156                return;
157            }
158            Some(target) => {
159                particles = target.clone();
160            }
161        }
162
163        if self._number_of_emitted_particles == 0 {
164            self._first_frame_time_in_seconds = current_time_in_seconds;
165        }
166
167        let elapsed_time_in_seconds = current_time_in_seconds - self._first_frame_time_in_seconds;
168
169        let mut new_max_total_number_of_emitted_particles = (
170            f64::ceil((elapsed_time_in_seconds + time_interval_in_seconds)
171                * self._max_number_of_new_particles_per_second as f64)) as usize;
172        new_max_total_number_of_emitted_particles = usize::min(new_max_total_number_of_emitted_particles,
173                                                               self._max_number_of_particles);
174        let max_number_of_new_particles
175            = new_max_total_number_of_emitted_particles - self._number_of_emitted_particles;
176
177        if max_number_of_new_particles > 0 {
178            let mut candidate_positions: Vec<Vector2D> = Vec::new();
179            let mut candidate_velocities: Vec<Vector2D> = Vec::new();
180            let mut new_positions: Vec<Vector2D> = Vec::new();
181            let mut new_velocities: Vec<Vector2D> = Vec::new();
182
183            self.emit(
184                &mut candidate_positions,
185                &mut candidate_velocities,
186                max_number_of_new_particles);
187
188            new_positions.append(&mut candidate_positions);
189            new_velocities.append(&mut candidate_velocities);
190
191            particles.write().unwrap().add_particles(&new_positions, Some(&new_velocities), None);
192
193            self._number_of_emitted_particles += new_positions.len();
194        }
195    }
196
197    fn view(&self) -> &ParticleEmitter2Data {
198        return &self._emitter_data;
199    }
200
201    fn view_mut(&mut self) -> &mut ParticleEmitter2Data {
202        return &mut self._emitter_data;
203    }
204}
205
206/// Shared pointer for the PointParticleEmitter2 type.
207pub type PointParticleEmitter2Ptr = Arc<RwLock<PointParticleEmitter2>>;
208
209///
210/// # Front-end to create PointParticleEmitter2 objects step by step.
211///
212pub struct Builder {
213    _max_number_of_new_particles_per_second: usize,
214    _max_number_of_particles: usize,
215    _origin: Vector2D,
216    _direction: Vector2D,
217    _speed: f64,
218    _spread_angle_in_degrees: f64,
219    _seed: u64,
220}
221
222impl Builder {
223    /// Returns builder with origin.
224    pub fn with_origin(&mut self, origin: &Vector2D) -> &mut Self {
225        self._origin = *origin;
226        return self;
227    }
228
229    /// Returns builder with direction.
230    pub fn with_direction(&mut self, direction: &Vector2D) -> &mut Self {
231        self._direction = *direction;
232        return self;
233    }
234
235    /// Returns builder with speed.
236    pub fn with_speed(&mut self, speed: f64) -> &mut Self {
237        self._speed = speed;
238        return self;
239    }
240
241    /// Returns builder with spread angle in degrees.
242    pub fn with_spread_angle_in_degrees(&mut self, spread_angle_in_degrees: f64) -> &mut Self {
243        self._spread_angle_in_degrees = spread_angle_in_degrees;
244        return self;
245    }
246
247    pub fn with_max_number_of_new_particles_per_second(&mut self,
248                                                       max_num_of_new_particles_per_sec: usize) -> &mut Self {
249        self._max_number_of_new_particles_per_second = max_num_of_new_particles_per_sec;
250        return self;
251    }
252
253    /// Returns builder with max number of particles.
254    pub fn with_max_number_of_particles(&mut self, max_number_of_particles: usize) -> &mut Self {
255        self._max_number_of_particles = max_number_of_particles;
256        return self;
257    }
258
259    /// Returns builder with random seed.
260    pub fn with_random_seed(&mut self, seed: u64) -> &mut Self {
261        self._seed = seed;
262        return self;
263    }
264
265    /// Builds PointParticleEmitter2.
266    pub fn build(&mut self) -> PointParticleEmitter2 {
267        return PointParticleEmitter2::new(&self._origin,
268                                          &self._direction,
269                                          self._speed,
270                                          self._spread_angle_in_degrees,
271                                          Some(self._max_number_of_new_particles_per_second),
272                                          Some(self._max_number_of_particles),
273                                          Some(self._seed));
274    }
275
276    /// Builds shared pointer of PointParticleEmitter2 instance.
277    pub fn make_shared(&mut self) -> PointParticleEmitter2Ptr {
278        return PointParticleEmitter2Ptr::new(RwLock::new(self.build()));
279    }
280
281    /// constructor
282    pub fn new() -> Builder {
283        return Builder {
284            _max_number_of_new_particles_per_second: 1,
285            _max_number_of_particles: usize::MAX,
286            _origin: Vector2D::new_default(),
287            _direction: Vector2D::new(0.0, 1.0),
288            _speed: 1.0,
289            _spread_angle_in_degrees: 90.0,
290            _seed: 0,
291        };
292    }
293}