use crate::matrix2x2::*;
use crate::vector2::Vector2D;
use crate::particle_emitter2::*;
use std::sync::{RwLock, Arc};
use rand::prelude::*;
use rand_pcg::{Pcg64, Lcg128Xsl64};
pub type OnBeginUpdateCallback = fn(&mut PointParticleEmitter2, f64, f64);
pub struct PointParticleEmitter2 {
_rng: Lcg128Xsl64,
_first_frame_time_in_seconds: f64,
_number_of_emitted_particles: usize,
_max_number_of_new_particles_per_second: usize,
_max_number_of_particles: usize,
_origin: Vector2D,
_direction: Vector2D,
_speed: f64,
_spread_angle_in_radians: f64,
_emitter_data: ParticleEmitter2Data,
_on_begin_update_callback: Option<OnBeginUpdateCallback>,
}
impl PointParticleEmitter2 {
pub fn new(origin: &Vector2D,
direction: &Vector2D,
speed: f64,
spread_angle_in_degrees: f64,
max_num_of_new_particles_per_sec: Option<usize>,
max_num_of_particles: Option<usize>,
seed: Option<u64>) -> PointParticleEmitter2 {
return PointParticleEmitter2 {
_rng: Pcg64::seed_from_u64(seed.unwrap_or(0)),
_first_frame_time_in_seconds: 0.0,
_number_of_emitted_particles: 0,
_max_number_of_new_particles_per_second: max_num_of_new_particles_per_sec.unwrap_or(1),
_max_number_of_particles: max_num_of_particles.unwrap_or(usize::MAX),
_origin: *origin,
_direction: *direction,
_speed: speed,
_spread_angle_in_radians: crate::math_utils::degrees_to_radians(spread_angle_in_degrees),
_emitter_data: ParticleEmitter2Data::new(),
_on_begin_update_callback: None,
};
}
pub fn builder() -> Builder {
return Builder::new();
}
pub fn max_number_of_new_particles_per_second(&self) -> usize {
return self._max_number_of_new_particles_per_second;
}
pub fn set_max_number_of_new_particles_per_second(&mut self, rate: usize) {
self._max_number_of_new_particles_per_second = rate;
}
pub fn max_number_of_particles(&self) -> usize {
return self._max_number_of_particles;
}
pub fn set_max_number_of_particles(&mut self, max_number_of_particles: usize) {
self._max_number_of_particles = max_number_of_particles;
}
fn emit(&mut self, new_positions: &mut Vec<Vector2D>,
new_velocities: &mut Vec<Vector2D>,
max_new_number_of_particles: usize) {
for _ in 0..max_new_number_of_particles {
let new_angle_in_radian = (self.random() - 0.5) * self._spread_angle_in_radians;
let rotation_matrix = Matrix2x2D::make_rotation_matrix(new_angle_in_radian);
new_positions.push(self._origin);
new_velocities.push((rotation_matrix * self._direction) * self._speed);
}
}
fn random(&mut self) -> f64 {
return self._rng.gen_range(0.0..1.0);
}
pub fn set_on_begin_update_callback(&mut self, callback: OnBeginUpdateCallback) {
self._on_begin_update_callback = Some(callback);
}
}
impl ParticleEmitter2 for PointParticleEmitter2 {
fn update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) where Self: Sized {
match self._on_begin_update_callback {
None => {}
Some(callback) => {
callback(self, current_time_in_seconds,
time_interval_in_seconds);
}
}
self.on_update(current_time_in_seconds, time_interval_in_seconds);
}
fn on_update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) {
let particles;
match self.target() {
None => {
return;
}
Some(target) => {
particles = target.clone();
}
}
if self._number_of_emitted_particles == 0 {
self._first_frame_time_in_seconds = current_time_in_seconds;
}
let elapsed_time_in_seconds = current_time_in_seconds - self._first_frame_time_in_seconds;
let mut new_max_total_number_of_emitted_particles = (
f64::ceil((elapsed_time_in_seconds + time_interval_in_seconds)
* self._max_number_of_new_particles_per_second as f64)) as usize;
new_max_total_number_of_emitted_particles = usize::min(new_max_total_number_of_emitted_particles,
self._max_number_of_particles);
let max_number_of_new_particles
= new_max_total_number_of_emitted_particles - self._number_of_emitted_particles;
if max_number_of_new_particles > 0 {
let mut candidate_positions: Vec<Vector2D> = Vec::new();
let mut candidate_velocities: Vec<Vector2D> = Vec::new();
let mut new_positions: Vec<Vector2D> = Vec::new();
let mut new_velocities: Vec<Vector2D> = Vec::new();
self.emit(
&mut candidate_positions,
&mut candidate_velocities,
max_number_of_new_particles);
new_positions.append(&mut candidate_positions);
new_velocities.append(&mut candidate_velocities);
particles.write().unwrap().add_particles(&new_positions, Some(&new_velocities), None);
self._number_of_emitted_particles += new_positions.len();
}
}
fn view(&self) -> &ParticleEmitter2Data {
return &self._emitter_data;
}
fn view_mut(&mut self) -> &mut ParticleEmitter2Data {
return &mut self._emitter_data;
}
}
pub type PointParticleEmitter2Ptr = Arc<RwLock<PointParticleEmitter2>>;
pub struct Builder {
_max_number_of_new_particles_per_second: usize,
_max_number_of_particles: usize,
_origin: Vector2D,
_direction: Vector2D,
_speed: f64,
_spread_angle_in_degrees: f64,
_seed: u64,
}
impl Builder {
pub fn with_origin(&mut self, origin: &Vector2D) -> &mut Self {
self._origin = *origin;
return self;
}
pub fn with_direction(&mut self, direction: &Vector2D) -> &mut Self {
self._direction = *direction;
return self;
}
pub fn with_speed(&mut self, speed: f64) -> &mut Self {
self._speed = speed;
return self;
}
pub fn with_spread_angle_in_degrees(&mut self, spread_angle_in_degrees: f64) -> &mut Self {
self._spread_angle_in_degrees = spread_angle_in_degrees;
return self;
}
pub fn with_max_number_of_new_particles_per_second(&mut self,
max_num_of_new_particles_per_sec: usize) -> &mut Self {
self._max_number_of_new_particles_per_second = max_num_of_new_particles_per_sec;
return self;
}
pub fn with_max_number_of_particles(&mut self, max_number_of_particles: usize) -> &mut Self {
self._max_number_of_particles = max_number_of_particles;
return self;
}
pub fn with_random_seed(&mut self, seed: u64) -> &mut Self {
self._seed = seed;
return self;
}
pub fn build(&mut self) -> PointParticleEmitter2 {
return PointParticleEmitter2::new(&self._origin,
&self._direction,
self._speed,
self._spread_angle_in_degrees,
Some(self._max_number_of_new_particles_per_second),
Some(self._max_number_of_particles),
Some(self._seed));
}
pub fn make_shared(&mut self) -> PointParticleEmitter2Ptr {
return PointParticleEmitter2Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_max_number_of_new_particles_per_second: 1,
_max_number_of_particles: usize::MAX,
_origin: Vector2D::new_default(),
_direction: Vector2D::new(0.0, 1.0),
_speed: 1.0,
_spread_angle_in_degrees: 90.0,
_seed: 0,
};
}
}