use crate::engines::soa::phys_obj::{AttrsError, PhysObj};
pub const ATTR_R: &str = "r";
pub const ATTR_V: &str = "v";
pub const ATTR_A: &str = "a";
pub const ATTR_M: &str = "m";
pub const ATTR_M_INV: &str = "m_inv";
pub const ATTR_ALIVE: &str = "alive";
pub const ATTR_RIGID: &str = "rigid";
pub(crate) const ALIVE_FALSE: u8 = 0;
pub(crate) const ALIVE_TRUE: u8 = 1;
pub(crate) const RIGID_FALSE: u8 = 0;
pub(crate) const RIGID_TRUE: u8 = 1;
pub(crate) fn alive_value(alive: bool) -> u8 {
if alive { ALIVE_TRUE } else { ALIVE_FALSE }
}
pub(crate) fn is_alive_value(value: u8) -> bool {
value != ALIVE_FALSE
}
pub(crate) fn rigid_value(rigid: bool) -> u8 {
if rigid { RIGID_TRUE } else { RIGID_FALSE }
}
pub(crate) fn is_rigid_value(value: u8) -> bool {
value != RIGID_FALSE
}
pub fn set_alive(objects: &mut PhysObj, i: usize, alive: bool) -> Result<(), AttrsError> {
objects
.core
.set_vector_of::<u8>(ATTR_ALIVE, i, &[alive_value(alive)])
}
pub fn is_alive(objects: &PhysObj, i: usize) -> Result<bool, AttrsError> {
Ok(is_alive_value(
objects.core.vector_of::<u8>(ATTR_ALIVE, i)?[0],
))
}
pub fn set_rigid(objects: &mut PhysObj, i: usize, rigid: bool) -> Result<(), AttrsError> {
objects
.core
.set_vector_of::<u8>(ATTR_RIGID, i, &[rigid_value(rigid)])
}
pub fn is_rigid(objects: &PhysObj, i: usize) -> Result<bool, AttrsError> {
Ok(is_rigid_value(
objects.core.vector_of::<u8>(ATTR_RIGID, i)?[0],
))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParticleSelection {
AliveOnly,
All,
}
impl ParticleSelection {
pub fn includes_dead(self) -> bool {
matches!(self, Self::All)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MassError {
InvalidMass { mass: f64 },
State(crate::models::ParticleStateError),
}
impl std::fmt::Display for MassError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidMass { mass } => write!(
f,
"mass and its reciprocal must be finite and positive; got {mass}"
),
Self::State(error) => write!(f, "invalid mass state: {error}"),
}
}
}
impl std::error::Error for MassError {}
impl From<AttrsError> for MassError {
fn from(error: AttrsError) -> Self {
Self::State(error.into())
}
}
impl From<crate::models::ParticleStateError> for MassError {
fn from(error: crate::models::ParticleStateError) -> Self {
Self::State(error)
}
}
pub fn set_mass(objects: &mut PhysObj, particle: usize, mass: f64) -> Result<(), MassError> {
let inverse = 1.0 / mass;
if !mass.is_finite() || mass <= 0.0 || !inverse.is_finite() || inverse <= 0.0 {
return Err(MassError::InvalidMass { mass });
}
let (m, m_inv) = objects.core.get_two_mut::<f64>(ATTR_M, ATTR_M_INV)?;
let n = m.num_vectors();
super::state::validate_scalar_shape(ATTR_M, m.dim(), n, n)?;
super::state::validate_scalar_shape(ATTR_M_INV, m_inv.dim(), m_inv.num_vectors(), n)?;
if particle >= n {
return Err(MassError::State(
crate::models::ParticleStateError::ParticleOutOfBounds {
label: ATTR_M.to_string(),
particle,
particle_count: n,
},
));
}
m.set(particle, 0, mass).expect("validated mass index");
m_inv
.set(particle, 0, inverse)
.expect("validated inverse-mass index");
Ok(())
}