use std::f32::consts::PI; use std::ops::Add; use kiddo::distance::squared_euclidean; use rand::Rng; use serde::Serialize; use crate::system::sticker::Sticker; use crate::system::{Position, Storage}; use crate::system::walker::Walker; #[derive(Serialize, Debug, Clone)] pub struct P3 { x: f32, y: f32, z: f32, } impl P3 { fn as_arr(&self) -> [f32; 3] { [self.x, self.y, self.z] } pub fn random_with_radius(rng: &mut R, radius: f32) -> P3 { let theta = rng.gen_range(0f32..1.0) * 2.0 * PI; let phi = rng.gen_range(0f32..1.0) * 2.0 * PI; let (x, y, z) = ( radius * theta.sin() * phi.cos(), radius * theta.sin() * phi.sin(), radius * theta.cos() ); P3 { x, y, z} } } impl Add for P3 { type Output = P3; fn add(self, rhs: Self) -> Self::Output { P3 { x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z, } } } pub struct ContinuousStorage { pub inner: kiddo::KdTree, pub ball_radius_sq: f32, } impl Position for P3 { type Cartesian = [f32; 3]; fn zero() -> Self { P3 { x: 0f32, y: 0f32, z: 0f32 } } fn abs(&self) -> f32 { (self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).powf(0.5) } fn from_cartesian(cartesian: Self::Cartesian) -> Self { P3 { x: cartesian[0], y: cartesian[1], z: cartesian[3] } } fn to_cartesian(&self) -> Self::Cartesian { [self.x, self.y, self.z] } } impl Storage for ContinuousStorage { fn is_occupied(&self, position: &P3) -> bool { let (dist_sq, _) = self.inner.nearest_one(&position.as_arr(), &squared_euclidean).unwrap(); // Is the distance of this point to the next one less than twice the ball radius dist_sq < 2.0 * self.ball_radius_sq } fn deposit(&mut self, position: &P3) { self.inner.add(&position.as_arr(), ()).expect("Failed to write to space") } } pub struct ContinuousSticker { /// INVARIANT: THIS SHOULD BE GREATER THAN THE BALL_RADIUS_SQ value pub range_sq: f32, } impl Sticker for ContinuousSticker { fn should_stick(&self, _rng: &mut R, space: &ContinuousStorage, position: &P3) -> bool { let (a, _) = space.inner.nearest_one(&position.as_arr(), &squared_euclidean).unwrap(); a < self.range_sq } } pub struct ContinuousWalker { pub walk_step: f32 } impl Walker for ContinuousWalker { fn walk(&self, rng: &mut R, position: &P3) -> P3 { position.clone() + P3::random_with_radius(rng, self.walk_step) } }