use core::marker::PhantomData;
use rebound_bind as rb;
use crate::{
particles::{Orbit, Particle, ParticleRead},
types::{Rotation, Vec3d},
utils,
};
pub struct ParticleRef<'a> {
pub(crate) inner: *mut rb::reb_particle,
pub(crate) _marker: PhantomData<&'a ()>,
}
impl<'a> ParticleRef<'a> {
pub fn hash(&self) -> Option<u32> {
Some(self.particle()?.hash)
}
pub fn set_hash(&mut self, hash: u32) -> Option<()> {
self.particle_mut()?.hash = hash;
Some(())
}
pub fn set_hash_by_name(&mut self, hash: &str) -> Option<()> {
self.set_hash(utils::hash(hash))
}
pub fn position(&self) -> Option<Vec3d> {
let particle = self.particle()?;
Some(Vec3d(particle.x, particle.y, particle.z))
}
pub fn set_x(&mut self, x: f64) -> Option<()> {
self.particle_mut()?.x = x;
Some(())
}
pub fn set_y(&mut self, y: f64) -> Option<()> {
self.particle_mut()?.y = y;
Some(())
}
pub fn set_z(&mut self, z: f64) -> Option<()> {
self.particle_mut()?.z = z;
Some(())
}
pub fn set_position(&mut self, x: f64, y: f64, z: f64) -> Option<()> {
let particle = self.particle_mut()?;
particle.x = x;
particle.y = y;
particle.z = z;
Some(())
}
pub fn set_position_vec3d(&mut self, position: Vec3d) -> Option<()> {
let particle = self.particle_mut()?;
particle.x = position.0;
particle.y = position.1;
particle.z = position.2;
Some(())
}
pub fn velocity(&self) -> Option<Vec3d> {
let particle = self.particle()?;
Some(Vec3d(particle.vx, particle.vy, particle.vz))
}
pub fn set_vx(&mut self, vx: f64) -> Option<()> {
self.particle_mut()?.vx = vx;
Some(())
}
pub fn set_vy(&mut self, vy: f64) -> Option<()> {
self.particle_mut()?.vy = vy;
Some(())
}
pub fn set_vz(&mut self, vz: f64) -> Option<()> {
self.particle_mut()?.vz = vz;
Some(())
}
pub fn set_velocity(&mut self, vx: f64, vy: f64, vz: f64) -> Option<()> {
let particle = self.particle_mut()?;
particle.vx = vx;
particle.vy = vy;
particle.vz = vz;
Some(())
}
pub fn set_velocity_vec3d(&mut self, velocity: Vec3d) -> Option<()> {
let particle = self.particle_mut()?;
particle.vx = velocity.0;
particle.vy = velocity.1;
particle.vz = velocity.2;
Some(())
}
pub fn acceleration(&self) -> Option<Vec3d> {
let particle = self.particle()?;
Some(Vec3d(particle.ax, particle.ay, particle.az))
}
pub fn set_ax(&mut self, ax: f64) -> Option<()> {
self.particle_mut()?.ax = ax;
Some(())
}
pub fn set_ay(&mut self, ay: f64) -> Option<()> {
self.particle_mut()?.ay = ay;
Some(())
}
pub fn set_az(&mut self, az: f64) -> Option<()> {
self.particle_mut()?.az = az;
Some(())
}
pub fn set_acceleration(&mut self, ax: f64, ay: f64, az: f64) -> Option<()> {
let particle = self.particle_mut()?;
particle.ax = ax;
particle.ay = ay;
particle.az = az;
Some(())
}
pub fn set_acceleration_vec3d(&mut self, acceleration: Vec3d) -> Option<()> {
let particle = self.particle_mut()?;
particle.ax = acceleration.0;
particle.ay = acceleration.1;
particle.az = acceleration.2;
Some(())
}
pub fn mass(&self) -> Option<f64> {
Some(self.particle()?.m)
}
pub fn set_mass(&mut self, mass: f64) -> Option<()> {
self.particle_mut()?.m = mass;
Some(())
}
pub fn radius(&self) -> Option<f64> {
Some(self.particle()?.r)
}
pub fn set_radius(&mut self, radius: f64) -> Option<()> {
self.particle_mut()?.r = radius;
Some(())
}
pub fn last_collision(&self) -> Option<f64> {
Some(self.particle()?.last_collision)
}
pub fn snapshot(&self) -> Option<Particle> {
self.particle().copied().map(Into::into)
}
pub fn is_null(&self) -> bool {
self.inner.is_null()
}
pub fn orbit<P: ParticleRead + ?Sized>(&self, g: f64, primary: &P) -> Option<Orbit> {
Orbit::from_particles(g, self, primary)
}
pub fn irotate(&mut self, rotation: Rotation) -> Option<()> {
let mut pos = self.position()?;
pos.irotate(rotation);
self.set_position(pos.0, pos.1, pos.2);
let mut vel = self.velocity()?;
vel.irotate(rotation);
self.set_velocity(vel.0, vel.1, vel.2);
Some(())
}
fn particle(&self) -> Option<&rb::reb_particle> {
unsafe { self.inner.as_ref() }
}
fn particle_mut(&mut self) -> Option<&mut rb::reb_particle> {
unsafe { self.inner.as_mut() }
}
}
impl<'a> ParticleRead for ParticleRef<'a> {
fn snapshot(&self) -> Option<Particle> {
ParticleRef::snapshot(self)
}
}
#[cfg(test)]
mod tests {
use crate::{
create_particle,
simulation::{Simulation, SimulationParticlesRead, SimulationParticlesWrite},
types::Vec3d,
};
#[test]
fn setters_update_particle_fields() {
let mut sim = Simulation::new();
sim.add_particle(create_particle! {
mass: 1.0,
x: 1.0,
y: 2.0,
z: 3.0,
vx: 4.0,
vy: 5.0,
vz: 6.0,
})
.unwrap();
{
let mut particle = sim.get_particle(0).unwrap();
particle.set_hash(42).unwrap();
particle.set_mass(2.0).unwrap();
particle.set_radius(0.1).unwrap();
particle.set_position(7.0, 8.0, 9.0).unwrap();
particle
.set_position_vec3d(Vec3d(10.0, 11.0, 12.0))
.unwrap();
particle.set_velocity(1.1, 1.2, 1.3).unwrap();
particle.set_velocity_vec3d(Vec3d(4.1, 4.2, 4.3)).unwrap();
particle.set_ax(0.1).unwrap();
particle.set_ay(0.2).unwrap();
particle.set_az(0.3).unwrap();
particle.set_acceleration(2.1, 2.2, 2.3).unwrap();
particle
.set_acceleration_vec3d(Vec3d(5.1, 5.2, 5.3))
.unwrap();
}
let particle = sim.get_particle(0).unwrap();
assert_eq!(particle.hash(), Some(42));
assert_eq!(particle.mass(), Some(2.0));
assert_eq!(particle.radius(), Some(0.1));
assert_eq!(particle.position(), Some(Vec3d(10.0, 11.0, 12.0)));
assert_eq!(particle.velocity(), Some(Vec3d(4.1, 4.2, 4.3)));
assert_eq!(particle.acceleration(), Some(Vec3d(5.1, 5.2, 5.3)));
}
}