#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Particle {
pub x: f32,
pub y: f32,
pub vx: f32,
pub vy: f32,
pub fx: Option<f32>,
pub fy: Option<f32>,
pub z: f32,
pub vz: f32,
pub fz: Option<f32>,
}
impl Particle {
pub fn at(x: f32, y: f32) -> Self {
Self { x, y, ..Default::default() }
}
pub fn at3(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z, ..Default::default() }
}
pub fn is_pinned(&self) -> bool {
self.fx.is_some() || self.fy.is_some()
}
pub fn is_pinned_3d(&self) -> bool {
self.fx.is_some() || self.fy.is_some() || self.fz.is_some()
}
pub fn pin(&mut self, x: f32, y: f32) {
self.fx = Some(x);
self.fy = Some(y);
self.x = x;
self.y = y;
self.vx = 0.0;
self.vy = 0.0;
}
pub fn pin3(&mut self, x: f32, y: f32, z: f32) {
self.fx = Some(x);
self.fy = Some(y);
self.fz = Some(z);
self.x = x;
self.y = y;
self.z = z;
self.vx = 0.0;
self.vy = 0.0;
self.vz = 0.0;
}
pub fn unpin(&mut self) {
self.fx = None;
self.fy = None;
}
pub fn unpin3(&mut self) {
self.fx = None;
self.fy = None;
self.fz = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn at3_sets_z_and_leaves_the_rest_at_default() {
let p = Particle::at3(1.0, 2.0, 3.0);
assert_eq!((p.x, p.y, p.z), (1.0, 2.0, 3.0));
assert_eq!((p.vx, p.vy, p.vz), (0.0, 0.0, 0.0));
assert!(!p.is_pinned_3d());
}
#[test]
fn pin3_holds_all_three_axes_and_zeroes_velocity() {
let mut p = Particle::at3(0.0, 0.0, 0.0);
p.vx = 5.0;
p.vy = 5.0;
p.vz = 5.0;
p.pin3(1.0, 2.0, 3.0);
assert_eq!((p.x, p.y, p.z), (1.0, 2.0, 3.0));
assert_eq!((p.vx, p.vy, p.vz), (0.0, 0.0, 0.0));
assert!(p.is_pinned_3d());
p.unpin3();
assert!(!p.is_pinned_3d());
}
#[test]
fn is_pinned_3d_is_true_if_any_single_axis_is_set() {
let mut p = Particle::at3(0.0, 0.0, 0.0);
assert!(!p.is_pinned_3d());
p.fz = Some(4.0);
assert!(p.is_pinned_3d());
assert!(!p.is_pinned(), "2D is_pinned must ignore fz");
}
}