use crate::math::{Isometry, Point, Real, Vector};
use crate::object::{ContiguousArena, ContiguousArenaIndex};
use crate::solver::NonPressureForce;
use num::Zero;
pub struct Fluid {
pub nonpressure_forces: Vec<Box<dyn NonPressureForce>>,
pub positions: Vec<Point<Real>>,
pub velocities: Vec<Vector<Real>>,
pub accelerations: Vec<Vector<Real>>,
pub volumes: Vec<Real>,
pub density0: Real,
deleted_particles: Vec<bool>,
num_deleted_particles: usize,
particle_radius: Real,
}
impl Fluid {
pub fn new(
particle_positions: Vec<Point<Real>>,
particle_radius: Real, density0: Real,
) -> Self {
let num_particles = particle_positions.len();
let velocities: Vec<_> = std::iter::repeat(Vector::zeros())
.take(num_particles)
.collect();
let accelerations: Vec<_> = velocities.clone();
let particle_volume = Self::particle_volume(particle_radius);
Self {
nonpressure_forces: Vec::new(),
positions: particle_positions,
velocities,
accelerations,
volumes: std::iter::repeat(particle_volume)
.take(num_particles)
.collect(),
deleted_particles: std::iter::repeat(false).take(num_particles).collect(),
num_deleted_particles: 0,
density0,
particle_radius,
}
}
pub fn delete_particle_at_next_timestep(&mut self, particle: usize) {
if !self.deleted_particles[particle] {
self.deleted_particles[particle] = true;
self.num_deleted_particles += 1;
}
}
pub fn num_deleted_particles(&self) -> usize {
self.num_deleted_particles
}
pub fn deleted_particles_mask(&self) -> &[bool] {
&self.deleted_particles
}
pub(crate) fn apply_particles_removal(&mut self) {
if self.num_deleted_particles != 0 {
crate::helper::filter_from_mask(&self.deleted_particles, &mut self.positions);
crate::helper::filter_from_mask(&self.deleted_particles, &mut self.velocities);
crate::helper::filter_from_mask(&self.deleted_particles, &mut self.accelerations);
crate::helper::filter_from_mask(&self.deleted_particles, &mut self.volumes);
self.deleted_particles.truncate(self.positions.len());
self.deleted_particles.iter_mut().for_each(|i| *i = false);
self.num_deleted_particles = 0;
}
}
pub fn particle_radius(&self) -> Real {
self.particle_radius
}
pub fn default_particle_volume(&self) -> Real {
Self::particle_volume(self.particle_radius)
}
fn particle_volume(particle_radius: Real) -> Real {
#[cfg(feature = "dim2")]
let particle_volume = particle_radius * particle_radius * na::convert::<_, Real>(4.0 * 0.8);
#[cfg(feature = "dim3")]
let particle_volume =
particle_radius * particle_radius * particle_radius * na::convert::<_, Real>(8.0 * 0.8);
particle_volume
}
pub fn add_particles(
&mut self,
positions: &[Point<Real>],
velocities: Option<&[Vector<Real>]>,
) {
let nparticles = self.positions.len() + positions.len();
let particle_volume = self.default_particle_volume();
self.positions.extend_from_slice(positions);
if let Some(vels) = velocities {
assert_eq!(
positions.len(),
vels.len(),
"The provided positions and velocities arrays must have the same length."
);
self.velocities.extend_from_slice(vels);
} else {
self.velocities.resize(nparticles, Vector::zeros());
}
self.accelerations.resize(nparticles, Vector::zeros());
self.volumes.resize(nparticles, particle_volume);
self.deleted_particles.resize(nparticles, false);
}
pub fn z_sort(&mut self) {
let order = crate::z_order::compute_points_z_order(&self.positions);
self.positions = crate::z_order::apply_permutation(&order, &self.positions);
self.velocities = crate::z_order::apply_permutation(&order, &self.velocities);
self.accelerations = crate::z_order::apply_permutation(&order, &self.accelerations);
self.volumes = crate::z_order::apply_permutation(&order, self.volumes.as_slice());
for forces in &mut self.nonpressure_forces {
forces.apply_permutation(&order);
}
}
pub fn transform_by(&mut self, t: &Isometry<Real>) {
self.positions.iter_mut().for_each(|p| *p = t * *p)
}
pub fn num_particles(&self) -> usize {
self.positions.len()
}
#[cfg(feature = "nphysics")]
pub fn compute_aabb(&self, particle_radius: Real) -> ncollide::bounding_volume::AABB<Real> {
use ncollide::bounding_volume::{self, BoundingVolume};
bounding_volume::local_point_cloud_aabb(&self.positions).loosened(particle_radius)
}
pub fn particle_mass(&self, i: usize) -> Real {
self.volumes[i] * self.density0
}
pub fn particle_inv_mass(&self, i: usize) -> Real {
if self.volumes[i].is_zero() {
na::zero::<Real>()
} else {
na::one::<Real>() / (self.volumes[i] * self.density0)
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FluidHandle(ContiguousArenaIndex);
pub type FluidSet = ContiguousArena<FluidHandle, Fluid>;
impl From<ContiguousArenaIndex> for FluidHandle {
#[inline]
fn from(i: ContiguousArenaIndex) -> Self {
FluidHandle(i)
}
}
impl Into<ContiguousArenaIndex> for FluidHandle {
#[inline]
fn into(self) -> ContiguousArenaIndex {
self.0
}
}