use crate::implicit_surface2::ImplicitSurface2Ptr;
use crate::bounding_box2::BoundingBox2D;
use crate::vector2::Vector2D;
use crate::particle_emitter2::*;
use crate::particle_system_data2::ParticleSystemData2Ptr;
use crate::triangle_point_generator::*;
use crate::surface2::Surface2Ptr;
use crate::surface_to_implicit2::SurfaceToImplicit2;
use crate::point_hash_grid_searcher2::PointHashGridSearcher2;
use crate::matrix2x2::Matrix2x2D;
use crate::point_generator2::PointGenerator2;
use crate::point_neighbor_searcher2::PointNeighborSearcher2;
use crate::usize2::USize2;
use std::sync::{RwLock, Arc};
use rand::prelude::*;
use rand_pcg::{Pcg64, Lcg128Xsl64};
use rayon::prelude::*;
use log::info;
pub type OnBeginUpdateCallback = fn(&mut VolumeParticleEmitter2, f64, f64);
pub struct VolumeParticleEmitter2 {
_rng: Lcg128Xsl64,
_implicit_surface: ImplicitSurface2Ptr,
_bounds: BoundingBox2D,
_spacing: f64,
_initial_vel: Vector2D,
_linear_vel: Vector2D,
_angular_vel: f64,
_points_gen: TrianglePointGeneratorPtr,
_max_number_of_particles: usize,
_number_of_emitted_particles: usize,
_jitter: f64,
_is_one_shot: bool,
_allow_overlapping: bool,
_emitter_data: ParticleEmitter2Data,
_on_begin_update_callback: Option<OnBeginUpdateCallback>,
}
impl VolumeParticleEmitter2 {
pub fn new(implicit_surface: ImplicitSurface2Ptr,
max_region: BoundingBox2D,
spacing: f64,
initial_vel: Option<Vector2D>,
linear_vel: Option<Vector2D>,
angular_vel: Option<f64>,
max_number_of_particles: Option<usize>,
jitter: Option<f64>,
is_one_shot: Option<bool>,
allow_overlapping: Option<bool>,
seed: Option<u64>) -> VolumeParticleEmitter2 {
return VolumeParticleEmitter2 {
_rng: Pcg64::seed_from_u64(seed.unwrap_or(0)),
_implicit_surface: implicit_surface,
_bounds: max_region,
_spacing: spacing,
_initial_vel: initial_vel.unwrap_or(Vector2D::new_default()),
_linear_vel: linear_vel.unwrap_or(Vector2D::new_default()),
_angular_vel: angular_vel.unwrap_or(0.0),
_points_gen: Arc::new(RwLock::new(TrianglePointGenerator {})),
_max_number_of_particles: max_number_of_particles.unwrap_or(usize::MAX),
_number_of_emitted_particles: 0,
_jitter: jitter.unwrap_or(0.0),
_is_one_shot: is_one_shot.unwrap_or(true),
_allow_overlapping: allow_overlapping.unwrap_or(false),
_emitter_data: ParticleEmitter2Data::new(),
_on_begin_update_callback: None,
};
}
pub fn surface(&self) -> ImplicitSurface2Ptr {
return self._implicit_surface.clone();
}
pub fn set_surface(&mut self, new_surface: ImplicitSurface2Ptr) {
self._implicit_surface = new_surface;
}
pub fn max_region(&self) -> BoundingBox2D {
return self._bounds.clone();
}
pub fn set_max_region(&mut self, new_max_region: BoundingBox2D) {
self._bounds = new_max_region;
}
pub fn jitter(&self) -> f64 {
return self._jitter;
}
pub fn set_jitter(&mut self, new_jitter: f64) {
self._jitter = crate::math_utils::clamp(new_jitter, 0.0, 1.0);
}
pub fn is_one_shot(&self) -> bool {
return self._is_one_shot;
}
pub fn set_is_one_shot(&mut self, new_value: bool) {
self._is_one_shot = new_value;
}
pub fn allow_overlapping(&self) -> bool {
return self._allow_overlapping;
}
pub fn set_allow_overlapping(&mut self, new_value: bool) {
self._allow_overlapping = new_value;
}
pub fn max_number_of_particles(&self) -> usize {
return self._max_number_of_particles;
}
pub fn set_max_number_of_particles(&mut self, new_max_number_of_particles: usize) {
self._max_number_of_particles = new_max_number_of_particles;
}
pub fn spacing(&self) -> f64 {
return self._spacing;
}
pub fn set_spacing(&mut self, new_spacing: f64) {
self._spacing = new_spacing;
}
pub fn initial_velocity(&self) -> Vector2D {
return self._initial_vel;
}
pub fn set_initial_velocity(&mut self, new_initial_vel: Vector2D) {
self._initial_vel = new_initial_vel;
}
pub fn linear_velocity(&self) -> Vector2D {
return self._linear_vel;
}
pub fn set_linear_velocity(&mut self, new_linear_vel: Vector2D) {
self._linear_vel = new_linear_vel;
}
pub fn angular_velocity(&self) -> f64 {
return self._angular_vel;
}
pub fn set_angular_velocity(&mut self, new_angular_vel: f64) {
self._angular_vel = new_angular_vel;
}
fn emit(&mut self, particles: ParticleSystemData2Ptr, new_positions: &mut Vec<Vector2D>,
new_velocities: &mut Vec<Vector2D>) {
self._implicit_surface.read().unwrap().update_query_engine();
let mut region = self._bounds.clone();
if self._implicit_surface.read().unwrap().is_bounded() {
let surface_bbox = self._implicit_surface.read().unwrap().bounding_box();
region.lower_corner = crate::vector2::max(®ion.lower_corner, &surface_bbox.lower_corner);
region.upper_corner = crate::vector2::min(®ion.upper_corner, &surface_bbox.upper_corner);
}
let j = self.jitter();
let max_jitter_dist = 0.5 * j * self._spacing;
let mut num_new_particles = 0;
if self._allow_overlapping || self._is_one_shot {
self._points_gen.clone().read().unwrap().for_each_point(®ion, self._spacing, &mut |point: &Vector2D| {
let new_angle_in_radian = (self.random() - 0.5) * crate::constants::K_TWO_PI_D;
let rotation_matrix = Matrix2x2D::make_rotation_matrix(new_angle_in_radian);
let random_dir = rotation_matrix * Vector2D::new_default();
let offset = random_dir * max_jitter_dist;
let candidate = *point + offset;
if self._implicit_surface.read().unwrap().signed_distance(&candidate) <= 0.0 {
if self._number_of_emitted_particles < self._max_number_of_particles {
new_positions.push(candidate);
self._number_of_emitted_particles += 1;
num_new_particles += 1;
} else {
return false;
}
}
return true;
});
} else {
let mut neighbor_searcher = PointHashGridSearcher2::new_vec(
USize2::new(64, 64),
2.0 * self._spacing);
if !self._allow_overlapping {
neighbor_searcher.build(particles.read().unwrap().positions());
}
self._points_gen.clone().read().unwrap().for_each_point(®ion, self._spacing, &mut |point: &Vector2D| {
let new_angle_in_radian = (self.random() - 0.5) * crate::constants::K_TWO_PI_D;
let rotation_matrix = Matrix2x2D::make_rotation_matrix(new_angle_in_radian);
let random_dir = rotation_matrix * Vector2D::new_default();
let offset = random_dir * max_jitter_dist;
let candidate = *point + offset;
if self._implicit_surface.read().unwrap().is_inside(&candidate) &&
(!self._allow_overlapping &&
!neighbor_searcher.has_nearby_point(&candidate, self._spacing)) {
if self._number_of_emitted_particles < self._max_number_of_particles {
new_positions.push(candidate);
neighbor_searcher.add(candidate);
self._number_of_emitted_particles += 1;
num_new_particles += 1;
} else {
return false;
}
}
return true;
});
}
info!("Number of newly generated particles: {}", num_new_particles);
info!("Number of total generated particles: {}", self._number_of_emitted_particles);
new_velocities.resize(new_positions.len(), Vector2D::new_default());
{
let translate = self._implicit_surface.read().unwrap().view().transform.translation();
let linear_vel = self._linear_vel;
let angular_vel = self._angular_vel;
let initial_vel = self._initial_vel;
(new_velocities, new_positions).into_par_iter().for_each(|(vel, pos)| {
let r = *pos - translate;
*vel = linear_vel + Vector2D::new(-r.y, r.x) * angular_vel + initial_vel;
});
}
}
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 VolumeParticleEmitter2 {
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, _: f64, _: f64) {
let particles;
match self.target() {
None => {
return;
}
Some(target) => {
particles = target.clone();
}
}
if !self.is_enabled() {
return;
}
let mut new_positions: Vec<Vector2D> = Vec::new();
let mut new_velocities: Vec<Vector2D> = Vec::new();
self.emit(particles.clone(), &mut new_positions, &mut new_velocities);
particles.write().unwrap().add_particles(&new_positions, Some(&new_velocities), None);
if self._is_one_shot {
self.set_is_enabled(false);
}
}
fn view(&self) -> &ParticleEmitter2Data {
return &self._emitter_data;
}
fn view_mut(&mut self) -> &mut ParticleEmitter2Data {
return &mut self._emitter_data;
}
}
pub type VolumeParticleEmitter2Ptr = Arc<RwLock<VolumeParticleEmitter2>>;
pub struct Builder {
_implicit_surface: Option<ImplicitSurface2Ptr>,
_is_bound_set: bool,
_bounds: BoundingBox2D,
_spacing: f64,
_initial_vel: Vector2D,
_linear_vel: Vector2D,
_angular_vel: f64,
_max_number_of_particles: usize,
_jitter: f64,
_is_one_shot: bool,
_allow_overlapping: bool,
_seed: u64,
}
impl Builder {
pub fn with_implicit_surface(&mut self, implicit_surface: ImplicitSurface2Ptr) -> &mut Self {
self._implicit_surface = Some(implicit_surface);
if !self._is_bound_set {
self._bounds = self._implicit_surface.as_ref().unwrap().read().unwrap().bounding_box();
}
return self;
}
pub fn with_surface(&mut self, surface: Surface2Ptr) -> &mut Self {
self._implicit_surface = Some(Arc::new(RwLock::new(SurfaceToImplicit2::new(surface.clone(),
None, None))));
if !self._is_bound_set {
self._bounds = surface.read().unwrap().bounding_box();
}
return self;
}
pub fn with_max_region(&mut self, bounds: BoundingBox2D) -> &mut Self {
self._bounds = bounds;
self._is_bound_set = true;
return self;
}
pub fn with_spacing(&mut self, spacing: f64) -> &mut Self {
self._spacing = spacing;
return self;
}
pub fn with_initial_velocity(&mut self, initial_vel: Vector2D) -> &mut Self {
self._initial_vel = initial_vel;
return self;
}
pub fn with_linear_velocity(&mut self, linear_vel: Vector2D) -> &mut Self {
self._linear_vel = linear_vel;
return self;
}
pub fn with_angular_velocity(&mut self, angular_vel: f64) -> &mut Self {
self._angular_vel = angular_vel;
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_jitter(&mut self, jitter: f64) -> &mut Self {
self._jitter = jitter;
return self;
}
pub fn with_is_one_shot(&mut self, is_one_shot: bool) -> &mut Self {
self._is_one_shot = is_one_shot;
return self;
}
pub fn with_allow_overlapping(&mut self, allow_overlapping: bool) -> &mut Self {
self._allow_overlapping = allow_overlapping;
return self;
}
pub fn with_random_seed(&mut self, seed: u64) -> &mut Self {
self._seed = seed;
return self;
}
pub fn build(&mut self) -> VolumeParticleEmitter2 {
return VolumeParticleEmitter2::new(self._implicit_surface.as_ref().unwrap().clone(),
self._bounds.clone(),
self._spacing,
Some(self._initial_vel),
Some(self._linear_vel),
Some(self._angular_vel),
Some(self._max_number_of_particles),
Some(self._jitter),
Some(self._is_one_shot),
Some(self._allow_overlapping),
Some(self._seed));
}
pub fn make_shared(&mut self) -> VolumeParticleEmitter2Ptr {
return VolumeParticleEmitter2Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_implicit_surface: None,
_is_bound_set: false,
_bounds: BoundingBox2D::new_default(),
_spacing: 0.1,
_initial_vel: Vector2D::new_default(),
_linear_vel: Vector2D::new_default(),
_angular_vel: 0.0,
_max_number_of_particles: usize::MAX,
_jitter: 0.0,
_is_one_shot: true,
_allow_overlapping: false,
_seed: 0,
};
}
}