use crate::vector3::Vector3D;
use crate::particle_system_data3::*;
use crate::collider3::Collider3Ptr;
use crate::particle_emitter3::ParticleEmitter3Ptr;
use crate::vector_field3::VectorField3Ptr;
use crate::constant_vector_field3::ConstantVectorField3;
use crate::animation::{Animation, Frame};
use crate::physics_animation::{PhysicsAnimation, PhysicsAnimationData};
use log::info;
use std::time::SystemTime;
use std::sync::{RwLock, Arc};
use rayon::prelude::*;
pub struct ParticleSystemSolver3 {
pub _drag_coefficient: f64,
pub _restitution_coefficient: f64,
pub _gravity: Vector3D,
pub _particle_system_data: ParticleSystemData3Ptr,
pub _new_positions: Vec<Vector3D>,
pub _new_velocities: Vec<Vector3D>,
pub _collider: Option<Collider3Ptr>,
pub _emitter: Option<ParticleEmitter3Ptr>,
pub _wind: VectorField3Ptr,
pub _animation_data: PhysicsAnimationData,
}
impl ParticleSystemSolver3 {
pub fn new_default() -> ParticleSystemSolver3 {
return ParticleSystemSolver3::new(1.0e-3, 1.0e-3);
}
pub fn new(radius: f64,
mass: f64) -> ParticleSystemSolver3 {
let data = Arc::new(RwLock::new(ParticleSystemData3::new_default()));
data.write().unwrap().set_radius(radius);
data.write().unwrap().set_mass(mass);
let wind = Arc::new(RwLock::new(ConstantVectorField3::new(Some(Vector3D::new_default()))));
return ParticleSystemSolver3 {
_drag_coefficient: 1.0e-4,
_restitution_coefficient: 0.0,
_gravity: Vector3D::new(0.0, crate::constants::K_GRAVITY, 0.0),
_particle_system_data: data,
_new_positions: vec![],
_new_velocities: vec![],
_collider: None,
_emitter: None,
_wind: wind,
_animation_data: PhysicsAnimationData::new(),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
}
impl ParticleSystemSolver3 {
pub fn drag_coefficient(&self) -> f64 {
return self._drag_coefficient;
}
pub fn set_drag_coefficient(&mut self, new_drag_coefficient: f64) {
self._drag_coefficient = f64::max(new_drag_coefficient, 0.0);
}
pub fn restitution_coefficient(&self) -> f64 {
return self._restitution_coefficient;
}
pub fn set_restitution_coefficient(&mut self, new_restitution_coefficient: f64) {
self._restitution_coefficient = crate::math_utils::clamp(new_restitution_coefficient, 0.0, 1.0);
}
pub fn gravity(&self) -> &Vector3D {
return &self._gravity;
}
pub fn set_gravity(&mut self, new_gravity: &Vector3D) {
self._gravity = *new_gravity;
}
pub fn particle_system_data(&self) -> &ParticleSystemData3Ptr {
return &self._particle_system_data;
}
pub fn collider(&self) -> &Option<Collider3Ptr> {
return &self._collider;
}
pub fn set_collider(&mut self, new_collider: &Collider3Ptr) {
self._collider = Some(new_collider.clone());
}
pub fn emitter(&self) -> &Option<ParticleEmitter3Ptr> {
return &self._emitter;
}
pub fn set_emitter(&mut self, new_emitter: &ParticleEmitter3Ptr) {
self._emitter = Some(new_emitter.clone());
new_emitter.write().unwrap().set_target(self._particle_system_data.clone());
}
pub fn wind(&self) -> &VectorField3Ptr {
return &self._wind;
}
pub fn set_wind(&mut self, new_wind: &VectorField3Ptr) {
self._wind = new_wind.clone();
}
}
impl Animation for ParticleSystemSolver3 {
fn on_update(&mut self, frame: &Frame) {
PhysicsAnimation::on_update(self, frame);
}
}
impl PhysicsAnimation for ParticleSystemSolver3 {
fn on_advance_time_step(&mut self, time_step_in_seconds: f64) {
self.begin_advance_time_step(time_step_in_seconds);
let mut timer = SystemTime::now();
self.accumulate_forces(time_step_in_seconds);
info!("Accumulating forces took {} seconds", timer.elapsed().unwrap().as_secs_f64());
timer = SystemTime::now();
self.time_integration(time_step_in_seconds);
info!("Time integration took {} seconds", timer.elapsed().unwrap().as_secs_f64());
timer = SystemTime::now();
self.resolve_collision();
info!("Resolving collision took {} seconds", timer.elapsed().unwrap().as_secs_f64());
self.end_advance_time_step(time_step_in_seconds);
}
fn on_initialize(&mut self) {
let mut timer = SystemTime::now();
self.update_collider(0.0);
info!("Update collider took {} seconds", timer.elapsed().unwrap().as_secs_f64());
timer = SystemTime::now();
self.update_emitter(0.0);
info!("Update emitter took {} seconds", timer.elapsed().unwrap().as_secs_f64());
}
fn view(&self) -> &PhysicsAnimationData {
return &self._animation_data;
}
fn view_mut(&mut self) -> &mut PhysicsAnimationData {
return &mut self._animation_data;
}
}
impl ParticleSystemSolver3 {
fn accumulate_forces(&mut self, _: f64) {
self.accumulate_external_forces();
}
fn on_begin_advance_time_step(&mut self, _: f64) {}
fn on_end_advance_time_step(&mut self, _: f64) {}
fn resolve_collision(&mut self) {
if let Some(collider) = &self._collider {
let radius = self._particle_system_data.read().unwrap().radius();
let restitution = self._restitution_coefficient;
(&mut self._new_positions, &mut self._new_velocities).into_par_iter().for_each(|(pos, vel)| {
collider.read().unwrap().resolve_collision(
radius,
restitution,
pos,
vel);
});
}
}
fn begin_advance_time_step(&mut self, time_step_in_seconds: f64) {
self._particle_system_data.write().unwrap().forces_mut().fill(Vector3D::new_default());
let mut timer = SystemTime::now();
self.update_collider(time_step_in_seconds);
info!("Update collider took {} seconds", timer.elapsed().unwrap().as_secs_f64());
timer = SystemTime::now();
self.update_emitter(time_step_in_seconds);
info!("Update emitter took {} seconds", timer.elapsed().unwrap().as_secs_f64());
let n = self._particle_system_data.read().unwrap().number_of_particles();
self._new_positions.resize(n, Vector3D::new_default());
self._new_velocities.resize(n, Vector3D::new_default());
self.on_begin_advance_time_step(time_step_in_seconds);
}
fn end_advance_time_step(&mut self, time_step_in_seconds: f64) {
(self._particle_system_data.write().unwrap().positions_mut(),
self._particle_system_data.write().unwrap().velocities_mut(),
&self._new_positions, &self._new_velocities).into_par_iter().for_each(|(pos, vel, pos_new, vel_new)| {
*pos = *pos_new;
*vel = *vel_new;
});
self.on_end_advance_time_step(time_step_in_seconds);
}
fn accumulate_external_forces(&mut self) {
let mass = self._particle_system_data.read().unwrap().mass();
(self._particle_system_data.write().unwrap().forces_mut(),
self._particle_system_data.read().unwrap().positions(),
self._particle_system_data.read().unwrap().velocities()).into_par_iter().for_each(|(force, pos, vel)| {
let mut f = self._gravity * mass;
let relative_vel = *vel - self._wind.read().unwrap().sample(pos);
f += relative_vel * -self._drag_coefficient;
*force += f;
});
}
fn time_integration(&mut self, time_step_in_seconds: f64) {
let mass = self._particle_system_data.read().unwrap().mass();
(&mut self._new_positions, &mut self._new_velocities,
self._particle_system_data.read().unwrap().forces(),
self._particle_system_data.read().unwrap().positions(),
self._particle_system_data.read().unwrap().velocities()).into_par_iter().for_each(|(new_pos, new_vel, force, pos, vel)| {
*new_vel = *vel + *force * time_step_in_seconds / mass;
*new_pos = *pos + *new_vel * time_step_in_seconds;
});
}
fn update_collider(&mut self, time_step_in_seconds: f64) {
if let Some(collider) = &self._collider {
collider.write().unwrap().update(self.current_time_in_seconds(),
time_step_in_seconds);
}
}
fn update_emitter(&mut self, time_step_in_seconds: f64) {
if let Some(emitter) = &self._emitter {
emitter.write().unwrap().update(self.current_time_in_seconds(),
time_step_in_seconds);
}
}
}
pub type ParticleSystemSolver3Ptr = Arc<RwLock<ParticleSystemSolver3>>;
pub trait ParticleSystemSolverBuilderBase3 {
fn with_radius(&mut self, radius: f64) -> &mut Self;
fn with_mass(&mut self, mass: f64) -> &mut Self;
}
pub struct Builder {
_radius: f64,
_mass: f64,
}
impl Builder {
pub fn build(&mut self) -> ParticleSystemSolver3 {
return ParticleSystemSolver3::new(self._radius, self._mass);
}
pub fn make_shared(&mut self) -> ParticleSystemSolver3Ptr {
return ParticleSystemSolver3Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_radius: 1.0e-3,
_mass: 1.0e-3,
};
}
}
impl ParticleSystemSolverBuilderBase3 for Builder {
fn with_radius(&mut self, radius: f64) -> &mut Self {
self._radius = radius;
return self;
}
fn with_mass(&mut self, mass: f64) -> &mut Self {
self._mass = mass;
return self;
}
}