From 7216fd561e7d1fc17ffa94aaf9914704fbedfa44 Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Wed, 8 Mar 2023 16:16:44 +0000 Subject: [PATCH] A great re-architecture of the cli --- src/cli/cli.rs | 56 ++++++++++ src/cli/mod.rs | 24 +++++ src/cli/output.rs | 49 +++++++++ src/example_systems.rs | 147 -------------------------- src/main.rs | 130 ++++++++++++----------- src/surface_probability_measure.rs | 54 ++++++++++ src/system/mod.rs | 1 - src/system/model.rs | 4 +- src/system/spaces/continuous.rs | 8 +- src/system/spaces/grid.rs | 110 ------------------- src/system/spaces/hexagonal.rs | 1 - src/system/spaces/mod.rs | 8 +- src/system/spaces/nd.rs | 124 ---------------------- src/system/spaces/ns.rs | 20 ---- src/system/spaces/square_grid.rs | 158 ++++++++++++++++++++++++++++ src/system/spaces/vector_storage.rs | 23 ++++ src/system/spawner.rs | 40 ++----- src/system/sticker.rs | 2 +- src/system/walker.rs | 14 +-- 19 files changed, 455 insertions(+), 518 deletions(-) create mode 100644 src/cli/cli.rs create mode 100644 src/cli/mod.rs create mode 100644 src/cli/output.rs delete mode 100644 src/example_systems.rs create mode 100644 src/surface_probability_measure.rs delete mode 100644 src/system/spaces/grid.rs delete mode 100644 src/system/spaces/nd.rs delete mode 100644 src/system/spaces/ns.rs create mode 100644 src/system/spaces/square_grid.rs create mode 100644 src/system/spaces/vector_storage.rs diff --git a/src/cli/cli.rs b/src/cli/cli.rs new file mode 100644 index 0000000..65f2b75 --- /dev/null +++ b/src/cli/cli.rs @@ -0,0 +1,56 @@ +use std::path::PathBuf; +use clap::{Parser, Args, Subcommand, ValueEnum}; + +#[derive(ValueEnum, Clone, Debug, Copy)] +pub enum OutputFormat { + FullDataJson, + Positions, +} + +#[derive(Args, Debug)] +pub struct InitialCli { + pub grid_size: u32 +} + +#[derive(Args, Debug)] +pub struct StickProbabilityCli { + pub grid_size: u32, + pub stick_probability: f32, +} + +#[derive(Args, Debug)] +pub struct BallsCli { + pub ball_radius: f32, + pub stick_distance: f32, + pub walk_step: f32, +} + +#[derive(Subcommand, Debug)] +pub enum PCM { + Initial(InitialCli), + StickProbability(StickProbabilityCli), + Grid3(StickProbabilityCli), + Hex(StickProbabilityCli), + Balls(BallsCli) +} + +#[derive(Parser, Debug)] +pub struct ModelCli { + #[command(subcommand)] + pub preconfigured_model: PCM, + + #[arg(long)] + pub max_frames: Option, + + #[arg(short = 'N', long)] + pub max_particles: usize, + + #[arg(long, short)] + pub seed: u64, + + #[arg(value_enum, short, long, default_value_t = OutputFormat::Positions)] + pub format: OutputFormat, + + #[arg(value_enum, short, long)] + pub output: PathBuf, +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..9955276 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,24 @@ +use rand::Rng; +use crate::system::model::DLASystem; +use crate::system::{Position, Storage}; +use crate::system::spawner::Spawner; +use crate::system::sticker::Sticker; +use crate::system::walker::Walker; + +pub mod cli; +pub mod output; + +pub fn drive_system, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &mut DLASystem, max_frames: Option) { + while sys.running { + sys.update(); + + match max_frames { + Some(max_frames) if max_frames <= sys.frame => { + sys.running = false; + eprintln!("System halted as it ran to {frame} frames (max_frames = {max_frames}) and did not complete", frame = sys.frame) + } + _ => {} + } + } +} + diff --git a/src/cli/output.rs b/src/cli/output.rs new file mode 100644 index 0000000..f5e32b9 --- /dev/null +++ b/src/cli/output.rs @@ -0,0 +1,49 @@ +use std::fs::File; +use std::path::Path; +use rand::Rng; +use crate::cli::cli::OutputFormat; +use crate::system::model::DLASystem; +use crate::system::{Position, Storage}; +use crate::system::spawner::Spawner; +use crate::system::sticker::Sticker; +use crate::system::walker::Walker; + +pub fn write, W: Walker

, Sp: Spawner

, St: Sticker>( + sys: &DLASystem, + format: OutputFormat, + output: &Path, +) { + match format { + OutputFormat::FullDataJson => write_json_full_data(sys, output), + OutputFormat::Positions => write_csv_positions(sys, output), + } +} + +fn write_csv_positions, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &DLASystem, csv_path: &Path) { + let mut wtr = csv::Writer::from_path(csv_path) + .expect("Failed to open file"); + + // CSVs can only store the raw positions + let positions: Vec<&P> = sys.history + .iter() + .map(|line| &line.position) + .collect(); + + wtr.serialize(positions) + .unwrap(); + + wtr.flush() + .unwrap(); +} + +fn write_json_full_data, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &DLASystem, output_path: &Path) { + let file = File::create(output_path).expect("Failed to open file"); + + let vec: Vec

= sys.history + .iter() + .map(|l| l.position.clone()) + .collect(); + + serde_json::to_writer(file, &vec) + .expect("Failed to write json"); +} diff --git a/src/example_systems.rs b/src/example_systems.rs deleted file mode 100644 index 9f89a63..0000000 --- a/src/example_systems.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::fs::File; -use std::path::Path; -use bevy::render::render_resource::AsBindGroupShaderType; -use kd_tree::KdTree; -use rand::rngs::SmallRng; -use rand::{Rng, SeedableRng}; -use crate::system::model::DLASystem; -use crate::system::{Position, Storage}; -use crate::system::spaces::continuous::{ContinuousSticker, ContinuousStorage, ContinuousWalker, P3}; -use crate::system::spaces::grid::{Pos2D, VectorStorage}; -use crate::system::spaces::hexagonal::HexPosition; -use crate::system::spaces::nd::{NDPosition, NDVectorStorage}; -use crate::system::spawner::{Spawner, UniformSpawner}; -use crate::system::sticker::{ProbabilisticSticking, SimpleSticking, Sticker}; -use crate::system::walker::{LocalRandomWalker, Walker}; - -pub fn drive_system, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &mut DLASystem, max_frames: Option) { - while sys.running { - sys.update(); - - match max_frames { - Some(max_frames) if max_frames <= sys.frame => { - sys.running = false; - eprintln!("System halted as it ran to {frame} frames (max_frames = {max_frames}) and did not complete", frame = sys.frame) - } - _ => {} - } - } -} - -pub fn write_csv, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &DLASystem, csv_path: &Path) { - let mut wtr = csv::Writer::from_path(csv_path) - .expect("Failed to open file"); - - // CSVs can only store the raw positions - let positions: Vec<&P> = sys.history - .iter() - .map(|line| &line.position) - .collect(); - - wtr.serialize(positions) - .unwrap(); - - wtr.flush() - .unwrap(); -} - -pub fn write_json, W: Walker

, Sp: Spawner

, St: Sticker>(sys: &DLASystem, output_path: &Path) { - let file = File::create(output_path).expect("Failed to open file"); - - serde_json::to_writer(file, &sys.history) - .expect("Failed to write json"); -} - -pub fn initial_config(seed: u64, max_particles: usize) -> DLASystem { - DLASystem::new( - SmallRng::seed_from_u64(seed), - VectorStorage::new(1600), - LocalRandomWalker, - UniformSpawner, - SimpleSticking, - max_particles, - ) -} - -pub fn stick_probability(seed: u64, grid_size: u32, max_particles: usize, stick_probability: f32) -> DLASystem { - DLASystem::new( - SmallRng::seed_from_u64(seed), - VectorStorage::new(grid_size), - LocalRandomWalker, - UniformSpawner, - ProbabilisticSticking { stick_probability }, - max_particles, - ) -} - -pub fn three_dimensional(seed: u64, grid_size: u32, max_particles: usize, stick_probability: f32) -> DLASystem, NDVectorStorage<3>, LocalRandomWalker, UniformSpawner, ProbabilisticSticking> { - DLASystem::new( - SmallRng::seed_from_u64(seed), - NDVectorStorage::new(grid_size), - LocalRandomWalker, - UniformSpawner, - ProbabilisticSticking { stick_probability }, - max_particles, - ) -} - -pub fn cont(seed: u64, max_particles: usize) -> DLASystem { - DLASystem::new( - SmallRng::seed_from_u64(seed), - ContinuousStorage { inner: kiddo::KdTree::new() }, - ContinuousWalker { walk_step: 1f32 }, - UniformSpawner, - ContinuousSticker { range_sq: 4f32 }, - max_particles, - ) -} - -pub fn hex_grid(seed: u64, max_particles: usize, grid_size: u32, stick_probability: f32) -> DLASystem { - DLASystem::new( - SmallRng::seed_from_u64(seed), - VectorStorage::new(grid_size), - LocalRandomWalker, - UniformSpawner, - ProbabilisticSticking { stick_probability }, - max_particles, - ) -} - -struct LoggerSticker { - inner: SimpleSticking -} - -impl> Sticker for LoggerSticker { - fn should_stick(&self, rng: &mut R, space: &S, position: &Pos2D) -> bool { - if self.inner.should_stick(rng, space, position) { - println!("{:?}", position); - } - - false - } -} - -struct ReadOnlyVectorStorage { - inner: VectorStorage, -} - -impl Storage for ReadOnlyVectorStorage { - fn is_occupied(&self, position: &Pos2D) -> bool { - self.inner.is_occupied(position) - } - - fn deposit(&mut self, position: &Pos2D) { - eprintln!("Write ignore for space at {position:?}"); - } -} - -pub fn aenguses_thing(seed: u64, max_particles: usize, stick_probability: f32) -> DLASystem { - DLASystem::new( - SmallRng::seed_from_u64(seed), - VectorStorage::new(1600), - LocalRandomWalker, - UniformSpawner, - ProbabilisticSticking { stick_probability }, - max_particles, - ) -} diff --git a/src/main.rs b/src/main.rs index 4dc7e76..9eff2a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,92 +1,98 @@ #![feature(array_zip)] #![feature(generic_const_exprs)] -use std::path::PathBuf; +use clap::Parser; +use rand::prelude::*; +use crate::cli::{drive_system}; +use crate::cli::cli::{StickProbabilityCli, InitialCli, BallsCli, PCM, ModelCli}; +use crate::cli::output::write; +use crate::system::model::DLASystem; +use crate::system::spaces::continuous::{ContinuousSticker, ContinuousStorage, ContinuousWalker}; +use crate::system::spaces::hexagonal::HexPosition; +use crate::system::spaces::square_grid::{Grid2D, Grid3D}; +use crate::system::spaces::VectorStorage; +use crate::system::spawner::UniformSpawner; +use crate::system::sticker::{ProbabilisticSticking, SimpleSticking}; +use crate::system::walker::LocalRandomWalker; mod system; -mod example_systems; - -use clap::Parser; -use crate::example_systems::{cont, drive_system, stick_probability, three_dimensional, write_csv, write_json}; - -#[derive(clap::ValueEnum, Clone, Debug)] -enum PreConfiguredSystem { - Grid2, - Grid3, - Hex, - Balls, -} - -#[derive(Parser, Debug)] -struct Cli { - #[arg(value_enum, long, default_value_t = PreConfiguredSystem::Grid2)] - mode: PreConfiguredSystem, - - #[arg(long, default_value_t = 1600)] - grid_size: u32, - - #[arg(short, long)] - max_frames: Option, - - seed: u64, - max_particles: usize, - stick_probability: f32, - output: PathBuf, -} +mod surface_probability_measure; +mod cli; fn main() { - let cli = Cli::parse(); + let cli = ModelCli::parse(); println!("Running: {:?}", cli); - match cli.mode { - PreConfiguredSystem::Grid2 => { - let mut sys = stick_probability( - cli.seed, - cli.grid_size, + match cli.preconfigured_model { + PCM::Initial(InitialCli { grid_size }) => { + let mut sys = DLASystem::<_, Grid2D, _, _, _, _>::new( + SmallRng::seed_from_u64(cli.seed), + VectorStorage::new(grid_size, 2), + LocalRandomWalker, + UniformSpawner, + SimpleSticking, cli.max_particles, - cli.stick_probability, ); drive_system(&mut sys, cli.max_frames); - write_csv(&mut sys, &cli.output); + write(&sys, cli.format, &cli.output); } - PreConfiguredSystem::Grid3 => { - let mut sys = three_dimensional( - cli.seed, - cli.grid_size, + PCM::StickProbability(StickProbabilityCli { grid_size, stick_probability }) => { + let mut sys = DLASystem::<_, Grid2D, _, _, _, _>::new( + SmallRng::seed_from_u64(cli.seed), + VectorStorage::new(grid_size, 2), + LocalRandomWalker, + UniformSpawner, + ProbabilisticSticking { stick_probability }, cli.max_particles, - cli.stick_probability, ); drive_system(&mut sys, cli.max_frames); - - write_json(&mut sys, &cli.output); + write(&sys, cli.format, &cli.output); } - PreConfiguredSystem::Hex => { - let mut sys = three_dimensional( - cli.seed, - cli.grid_size, - cli.max_particles, - cli.stick_probability, - ); - - drive_system(&mut sys, cli.max_frames); - - write_json(&mut sys, &cli.output); - }, - - PreConfiguredSystem::Balls => { - let mut sys = cont( - cli.seed, + PCM::Grid3(StickProbabilityCli { grid_size, stick_probability }) => { + let mut sys = DLASystem::<_, Grid3D, _, _, _, _>::new( + SmallRng::seed_from_u64(cli.seed), + VectorStorage::new(grid_size, 3), + LocalRandomWalker, + UniformSpawner, + ProbabilisticSticking { stick_probability }, cli.max_particles, ); drive_system(&mut sys, cli.max_frames); + write(&sys, cli.format, &cli.output); + } - write_json(&mut sys, &cli.output); + PCM::Hex(StickProbabilityCli { grid_size, stick_probability }) => { + let mut sys = DLASystem::<_, HexPosition, _, _, _, _>::new( + SmallRng::seed_from_u64(cli.seed), + VectorStorage::new(grid_size, 2), + LocalRandomWalker, + UniformSpawner, + ProbabilisticSticking { stick_probability }, + cli.max_particles, + ); + + drive_system(&mut sys, cli.max_frames); + write(&sys, cli.format, &cli.output); + } + + PCM::Balls(BallsCli { ball_radius, stick_distance, walk_step }) => { + let mut sys = DLASystem::new( + SmallRng::seed_from_u64(cli.seed), + ContinuousStorage { inner: kiddo::KdTree::new(), ball_radius_sq: ball_radius * ball_radius }, + ContinuousWalker { walk_step }, + UniformSpawner, + ContinuousSticker { range_sq: stick_distance * stick_distance }, + cli.max_particles, + ); + + drive_system(&mut sys, cli.max_frames); + write(&sys, cli.format, &cli.output); } } } diff --git a/src/surface_probability_measure.rs b/src/surface_probability_measure.rs new file mode 100644 index 0000000..759cd52 --- /dev/null +++ b/src/surface_probability_measure.rs @@ -0,0 +1,54 @@ +use std::fs::File; +use std::path::Path; +use bevy::render::render_resource::AsBindGroupShaderType; +use kd_tree::KdTree; +use rand::rngs::SmallRng; +use rand::{Rng, SeedableRng}; +use crate::system::model::DLASystem; +use crate::system::{Position, Storage}; +use crate::system::spaces::continuous::{ContinuousSticker, ContinuousStorage, ContinuousWalker, P3}; +use crate::system::spaces::hexagonal::HexPosition; +use crate::system::spaces::square_grid::{Grid2D, Grid3D}; +use crate::system::spaces::VectorStorage; +use crate::system::spawner::{Spawner, UniformSpawner}; +use crate::system::sticker::{ProbabilisticSticking, SimpleSticking, Sticker}; +use crate::system::walker::{LocalRandomWalker, Walker}; + +struct LoggerSticker { + inner: SimpleSticking +} + +impl> Sticker for LoggerSticker { + fn should_stick(&self, rng: &mut R, space: &S, position: &Grid2D) -> bool { + if self.inner.should_stick(rng, space, position) { + println!("{:?}", position); + } + + false + } +} + +struct ReadOnlyVectorStorage { + inner: VectorStorage, +} + +impl Storage for ReadOnlyVectorStorage { + fn is_occupied(&self, position: &Grid2D) -> bool { + self.inner.is_occupied(position) + } + + fn deposit(&mut self, position: &Grid2D) { + eprintln!("Write ignore for space at {position:?}"); + } +} + +pub fn surface_probability_measure(seed: u64, max_particles: usize, stick_probability: f32) -> DLASystem { + DLASystem::new( + SmallRng::seed_from_u64(seed), + VectorStorage::new(1600, 3), + LocalRandomWalker, + UniformSpawner, + ProbabilisticSticking { stick_probability }, + max_particles, + ) +} diff --git a/src/system/mod.rs b/src/system/mod.rs index 8b27186..3e5963a 100644 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -1,5 +1,4 @@ use std::ops::Add; -use rand::Rng; use serde::Serialize; pub mod walker; diff --git a/src/system/model.rs b/src/system/model.rs index 17f1ba1..0ce10ab 100644 --- a/src/system/model.rs +++ b/src/system/model.rs @@ -1,8 +1,6 @@ -use std::io; -use std::path::Path; use rand::prelude::*; use serde::Serialize; -use crate::system::{GriddedPosition, Position, Storage}; +use crate::system::{Position, Storage}; use crate::system::spawner::Spawner; use crate::system::sticker::Sticker; use crate::system::walker::Walker; diff --git a/src/system/spaces/continuous.rs b/src/system/spaces/continuous.rs index ee73551..9019a16 100644 --- a/src/system/spaces/continuous.rs +++ b/src/system/spaces/continuous.rs @@ -1,9 +1,6 @@ use std::f32::consts::PI; use std::ops::Add; -use bevy::utils::tracing::Instrument; use kiddo::distance::squared_euclidean; -use kiddo::ErrorKind; -use nalgebra::{DimAdd, Point3}; use rand::Rng; use serde::Serialize; use crate::system::sticker::Sticker; @@ -48,10 +45,9 @@ impl Add for P3 { } } -const BALL_RADIUS_SQ: f32 = 1.0; - pub struct ContinuousStorage { pub inner: kiddo::KdTree, + pub ball_radius_sq: f32, } impl Position for P3 { @@ -75,7 +71,7 @@ impl Storage for ContinuousStorage { 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 * BALL_RADIUS_SQ + dist_sq < 2.0 * self.ball_radius_sq } fn deposit(&mut self, position: &P3) { diff --git a/src/system/spaces/grid.rs b/src/system/spaces/grid.rs deleted file mode 100644 index 26b2a94..0000000 --- a/src/system/spaces/grid.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::f32::consts::PI; -use std::ops::Add; -use num_integer::Integer; -use rand::Rng; -use crate::system::{GriddedPosition, Position, Storage}; -use serde::{Serialize, Deserialize}; -use crate::system::spaces::hexagonal::HexPosition; - -pub struct VectorStorage { - backing: Vec, - grid_size: u32, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Pos2D { - pub x: i32, - pub y: i32, -} - -impl Pos2D { - pub fn in_direction(direction: u32, value: i32) -> Self { - if direction == 0 { Pos2D { x: value, y: 0 } } else { Pos2D { x: 0, y: value } } - } -} - -impl Add for Pos2D { - type Output = Pos2D; - - fn add(self, rhs: Self) -> Self::Output { - Pos2D { x: self.x + rhs.x, y: self.y + rhs.y } - } -} - -impl GriddedPosition for Pos2D { - const NEIGHBOURS: u32 = 4; - - fn neighbour(&self, neighbour_index: u32) -> Self { - let (dim, sign) = neighbour_index.div_rem(&2); - let sign = if sign == 0 { 1 } else { -1 }; - let offset = Pos2D::in_direction(dim, sign); - - self.clone() + offset - } - - fn linear_index(&self, grid_size: u32) -> usize { - let grid_size = grid_size as i32; - - assert!(self.x < grid_size && -(grid_size) < self.x); - assert!(self.y < grid_size && -(grid_size) < self.y); - - let x = (self.x + (grid_size) / 2) as usize; - let y = (self.y + (grid_size) / 2) as usize; - - let linear_index = grid_size as usize * y + x; - - if linear_index >= (grid_size * grid_size) as usize { - eprintln!("AHHH SOMETHING WENT WRONG {:?} gives {}", self, linear_index); - } - - return linear_index; - } -} - -impl Position for Pos2D { - const DIM: usize = 2; - - fn zero() -> Pos2D { - Pos2D { x: 0, y: 0 } - } - fn abs(&self) -> f32 { - ((self.x.pow(2) + self.y.pow(2)) as f32).powf(0.5) - } - - fn from_cartesian(cartesian: [f32; Self::DIM]) -> Self { - Pos2D { x: cartesian[0] as i32, y: cartesian[1] as i32 } - } -} - -impl VectorStorage { - pub fn new(grid_size: u32) -> VectorStorage { - VectorStorage { grid_size, backing: vec![false; grid_size.pow(2) as usize] } - } - - /* - * Convenience function for c-binding - * */ - pub fn write(&mut self, position: &Pos2D, val: bool) { - self.backing[position.linear_index(self.grid_size)] = val; - } -} - -impl Storage for VectorStorage { - fn is_occupied(&self, position: &Pos2D) -> bool { - return self.backing[position.linear_index(self.grid_size)]; - } - - fn deposit(&mut self, position: &Pos2D) { - self.backing[position.linear_index(self.grid_size)] = true; - } -} - -impl Storage for VectorStorage { - fn is_occupied(&self, position: &HexPosition) -> bool { - return self.backing[position.linear_index(self.grid_size)]; - } - - fn deposit(&mut self, position: &HexPosition) { - self.backing[position.linear_index(self.grid_size)] = true; - } -} diff --git a/src/system/spaces/hexagonal.rs b/src/system/spaces/hexagonal.rs index 7567f7f..a43a25c 100644 --- a/src/system/spaces/hexagonal.rs +++ b/src/system/spaces/hexagonal.rs @@ -1,5 +1,4 @@ use std::ops::Add; -use rand::Rng; use crate::system::{GriddedPosition, Position}; use serde::{Serialize, Deserialize}; diff --git a/src/system/spaces/mod.rs b/src/system/spaces/mod.rs index 2595430..b62ff55 100644 --- a/src/system/spaces/mod.rs +++ b/src/system/spaces/mod.rs @@ -1,5 +1,7 @@ -pub mod grid; +pub mod vector_storage; +pub use vector_storage::VectorStorage; + +pub mod square_grid; pub mod hexagonal; -pub mod nd; + pub mod continuous; -pub mod ns; diff --git a/src/system/spaces/nd.rs b/src/system/spaces/nd.rs deleted file mode 100644 index 0dfeb8a..0000000 --- a/src/system/spaces/nd.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::ops::Add; -use num_integer::Integer; -use rand::Rng; -use serde::{Serialize, Serializer}; -use serde::ser::SerializeStruct; -use crate::system::{GriddedPosition, Position}; -use crate::system::Storage; - -pub struct NDVectorStorage { - backing: Vec, - grid_size: u32, -} - -impl NDVectorStorage { - pub fn new(grid_size: u32) -> Self { - Self { grid_size, backing: vec![false; grid_size.pow(DIM as u32) as usize] } - } -} - -impl Storage> for NDVectorStorage { - fn is_occupied(&self, position: &NDPosition) -> bool { - return self.backing[position.linear_index(self.grid_size)]; - } - - fn deposit(&mut self, position: &NDPosition) { - self.backing[position.linear_index(self.grid_size)] = true; - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct NDPosition([i32; DIM]); - -impl NDPosition { - pub fn in_direction(direction: usize, value: i32) -> Self { - let mut arr = [0; DIM]; - arr[direction] = value; - NDPosition(arr) - } -} - -impl Add for NDPosition { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0.zip(rhs.0).map(|(a, b)| a + b)) - } -} - -impl Serialize for NDPosition { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut ser = serializer.serialize_struct("NDPosition", DIM)?; - for (i, v) in self.0.iter().enumerate() { - ser.serialize_field(Box::leak(Box::new(format!("r{i}"))), v)?; - } - ser.end() - } -} - -impl GriddedPosition for NDPosition { - const NEIGHBOURS: u32 = { 2u32.pow(DIM as u32) } as u32; - - fn neighbour(&self, neighbour_index: u32) -> Self { - let (dim, sign) = neighbour_index.div_rem(&(DIM as u32)); - let sign = if sign == 0 { 1 } else { -1 }; - let offset = Self::in_direction(dim as usize, sign); - - self.clone() + offset - } - - fn linear_index(&self, grid_size: u32) -> usize { - self.0.iter() - .enumerate() - .map(|(i, v)| (grid_size.pow(i as u32) as usize) * (v + ((grid_size / 2) as i32)) as usize) - .sum() - } -} - -impl Position for NDPosition { - const DIM: usize = DIM; - - fn zero() -> Self { - NDPosition([0; DIM]) - } - - fn abs(&self) -> f32 { - let a: i32 = self.0.iter() - .map(|r| r.pow(2)) - .sum(); - - (a as f32).powf(0.5) - } - - fn from_cartesian(cartesian: [f32; Self::DIM]) -> Self { - let mut a: [i32; DIM] = [0; DIM]; - - for (i, x) in cartesian.iter().enumerate() { - a[i] = *x as i32; - } - - Self(a) - } -} - -mod test { - use rand::rngs::SmallRng; - use rand::SeedableRng; - use crate::system::{GriddedPosition, Position}; - use super::NDPosition; - - #[test] - fn abs() { - assert_eq!(NDPosition([1, 2]).abs(), 2.23606798); - assert_eq!(NDPosition([1, 1, 1, 1]).abs(), 2.0); - assert_eq!(NDPosition([10]).abs(), 10.0); - } - - #[test] - fn spawn() { - let mut rng = SmallRng::seed_from_u64(0); - for _ in 0..10 { - println!("{:?}", NDPosition::<2>::spawn(&mut rng, 10.0)); - } - } -} diff --git a/src/system/spaces/ns.rs b/src/system/spaces/ns.rs deleted file mode 100644 index 6b89273..0000000 --- a/src/system/spaces/ns.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Grid2D { - pub x: i32, - pub y: i32, -} - -impl Add for Grid2D { - type Output = Grid2D; - - fn add(self, rhs: Self) -> Self::Output { - Pos2D { x: self.x + rhs.x, y: self.y + rhs.y } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Grid3D { - pub x: i32, - pub y: i32, - pub z: i32, -} diff --git a/src/system/spaces/square_grid.rs b/src/system/spaces/square_grid.rs new file mode 100644 index 0000000..1f61b69 --- /dev/null +++ b/src/system/spaces/square_grid.rs @@ -0,0 +1,158 @@ +use std::ops::Add; +use num_integer::Integer; +use crate::system::{GriddedPosition, Position}; +use serde::{Serialize, Deserialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Grid2D { + pub x: i32, + pub y: i32, +} + +impl Grid2D { + pub fn in_direction(direction: u32, value: i32) -> Self { + match direction { + 0 => Grid2D { x: value, y: 0 }, + 1 => Grid2D { x: 0, y: value }, + _ => panic!("Invalid direction"), + } + } +} + +impl Add for Grid2D { + type Output = Grid2D; + + fn add(self, rhs: Self) -> Self::Output { + Grid2D { x: self.x + rhs.x, y: self.y + rhs.y } + } +} + +impl Position for Grid2D { + const DIM: usize = 2; + + fn zero() -> Self { + Grid2D { x: 0, y: 0 } + } + + fn abs(&self) -> f32 { + (((self.x * self.x) + (self.y * self.y)) as f32).powf(0.5) + } + + fn from_cartesian(cartesian: [f32; Self::DIM]) -> Self { + Grid2D { + x: cartesian[0] as i32, + y: cartesian[1] as i32, + } + } +} + +impl GriddedPosition for Grid2D { + const NEIGHBOURS: u32 = 4; + + fn neighbour(&self, neighbour_index: u32) -> Self { + let (dim, sign) = neighbour_index.div_rem(&2); + let sign = if sign == 0 { 1 } else { -1 }; + let offset = Self::in_direction(dim, sign); + + self.clone() + offset + } + + fn linear_index(&self, grid_size: u32) -> usize { + let grid_size = grid_size as i32; + + assert!(self.x < grid_size && -(grid_size) < self.x); + assert!(self.y < grid_size && -(grid_size) < self.y); + + let x = (self.x + (grid_size) / 2) as usize; + let y = (self.y + (grid_size) / 2) as usize; + + let linear_index = grid_size as usize * y + x; + + if linear_index >= (grid_size * grid_size) as usize { + eprintln!("AHHH SOMETHING WENT WRONG {:?} gives {}", self, linear_index); + } + + return linear_index; + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Grid3D { + pub x: i32, + pub y: i32, + pub z: i32, +} + +impl Grid3D { + pub fn in_direction(direction: u32, value: i32) -> Self { + match direction { + 0 => Grid3D { x: value, y: 0, z: 0 }, + 1 => Grid3D { x: 0, y: value, z: 0 }, + 2 => Grid3D { x: 0, y: 0, z: value }, + _ => panic!("Invalid direction"), + } + } +} + +impl Add for Grid3D { + type Output = Grid3D; + + fn add(self, rhs: Self) -> Self::Output { + Grid3D { x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z } + } +} + +impl Position for Grid3D { + const DIM: usize = 3; + + fn zero() -> Self { + Grid3D { x: 0, y: 0, z: 0 } + } + + fn abs(&self) -> f32 { + (((self.x * self.x) + (self.y * self.y) + (self.z * self.z)) as f32).powf(0.5) + } + + fn from_cartesian(cartesian: [f32; Self::DIM]) -> Self { + Self { + x: cartesian[0] as i32, + y: cartesian[1] as i32, + z: cartesian[2] as i32, + } + } +} + +impl GriddedPosition for Grid3D { + const NEIGHBOURS: u32 = 6; + + fn neighbour(&self, neighbour_index: u32) -> Self { + let (dim, sign) = neighbour_index.div_rem(&3); + let sign = if sign == 0 { 1 } else { -1 }; + let offset = Self::in_direction(dim, sign); + + self.clone() + offset + } + + fn linear_index(&self, grid_size: u32) -> usize { + let grid_size = grid_size as i32; + + assert!(self.x < grid_size && -(grid_size) < self.x); + assert!(self.y < grid_size && -(grid_size) < self.y); + assert!(self.z < grid_size && -(grid_size) < self.z); + + let x = (self.x + (grid_size) / 2) as usize; + let y = (self.y + (grid_size) / 2) as usize; + let z = (self.z + (grid_size) / 2) as usize; + + let linear_index = + (grid_size as usize * grid_size as usize) * z + + (grid_size as usize) * y + + x; + + if linear_index >= (grid_size * grid_size * grid_size) as usize { + eprintln!("AHHH SOMETHING WENT WRONG {:?} gives {}", self, linear_index); + } + + return linear_index; + } +} diff --git a/src/system/spaces/vector_storage.rs b/src/system/spaces/vector_storage.rs new file mode 100644 index 0000000..2de57ab --- /dev/null +++ b/src/system/spaces/vector_storage.rs @@ -0,0 +1,23 @@ +use crate::system::{GriddedPosition, Storage}; + +pub struct VectorStorage { + backing: Vec, + grid_size: u32, + dimension: u32, +} + +impl VectorStorage { + pub fn new(grid_size: u32, dimension: u32) -> VectorStorage { + VectorStorage { grid_size, dimension, backing: vec![false; grid_size.pow(dimension) as usize] } + } +} + +impl Storage

for VectorStorage { + fn is_occupied(&self, position: &P) -> bool { + return self.backing[position.linear_index(self.grid_size)]; + } + + fn deposit(&mut self, position: &P) { + self.backing[position.linear_index(self.grid_size)] = true; + } +} diff --git a/src/system/spawner.rs b/src/system/spawner.rs index 138e365..916844a 100644 --- a/src/system/spawner.rs +++ b/src/system/spawner.rs @@ -2,9 +2,8 @@ use std::f32::consts::PI; use rand::Rng; use crate::system::Position; use crate::system::spaces::continuous::P3; -use crate::system::spaces::grid::Pos2D; use crate::system::spaces::hexagonal::HexPosition; -use crate::system::spaces::nd::NDPosition; +use crate::system::spaces::square_grid::{Grid2D, Grid3D}; pub trait Spawner { fn spawn(&self, rng: &mut R, radius: f32) -> P; @@ -12,12 +11,12 @@ pub trait Spawner { pub struct UniformSpawner; -impl Spawner for UniformSpawner { - fn spawn(&self, rng: &mut R, radius: f32) -> Pos2D { +impl Spawner for UniformSpawner { + fn spawn(&self, rng: &mut R, radius: f32) -> Grid2D { let theta = rng.gen_range(0f32..1.0) * 2.0 * PI; let (x, y) = (radius * theta.cos(), radius * theta.sin()); - Pos2D::from_cartesian([x, y]) + Grid2D::from_cartesian([x, y]) } } @@ -30,8 +29,8 @@ impl Spawner for UniformSpawner { } } -impl Spawner> for UniformSpawner { - fn spawn(&self, rng: &mut R, radius: f32) -> NDPosition<3> { +impl Spawner for UniformSpawner { + fn spawn(&self, rng: &mut R, radius: f32) -> Grid3D { let theta = rng.gen_range(0f32..1.0) * 2.0 * PI; let phi = rng.gen_range(0f32..1.0) * 2.0 * PI; @@ -41,7 +40,7 @@ impl Spawner> for UniformSpawner { radius * theta.cos() ); - NDPosition::<3>::from_cartesian([x, y, z]) + Grid3D::from_cartesian([x, y, z]) } } @@ -50,28 +49,3 @@ impl Spawner for UniformSpawner { P3::random_with_radius(rng, radius) } } - -// Does not work due to const generics issue, for now specialise for each dimension needed. -// pub struct NDUniformSpawner; -// -// impl Spawner> for NDUniformSpawner { -// fn spawn(&self, rng: &mut R, radius: f32) -> NDPosition { -// let mut a: [f32; DIM] = [0f32; DIM]; -// -// for i in 0..DIM { -// a[i] = rng.gen_range(0f32..1f32); -// } -// -// let norm = a.iter().sum::() -// .sqrt(); -// -// for i in 0..DIM { -// a[i] = a[i] * radius / norm; -// } -// -// unsafe { -// let b = transmute(a); -// NDPosition::from_cartesian(b) -// } -// } -// } diff --git a/src/system/sticker.rs b/src/system/sticker.rs index 48a1a1a..0136293 100644 --- a/src/system/sticker.rs +++ b/src/system/sticker.rs @@ -12,7 +12,7 @@ pub struct ProbabilisticSticking { } impl> Sticker for SimpleSticking { - fn should_stick(&self, rng: &mut R, space: &S, position: &P) -> bool { + fn should_stick(&self, _rng: &mut R, space: &S, position: &P) -> bool { (0..P::NEIGHBOURS) .map(|n| position.neighbour(n)) .any(|neighbour| space.is_occupied(&neighbour)) diff --git a/src/system/walker.rs b/src/system/walker.rs index df0a60d..8d848c7 100644 --- a/src/system/walker.rs +++ b/src/system/walker.rs @@ -17,16 +17,16 @@ mod test { use rand::rngs::SmallRng; use rand::{SeedableRng, thread_rng}; use crate::system::{GriddedPosition, Position}; - use crate::system::spaces::grid::Pos2D; + use crate::system::spaces::square_grid::Grid2D; use crate::system::walker::{LocalRandomWalker, Walker}; #[test] fn uniformity() { let walker = LocalRandomWalker; let mut rng = SmallRng::from_rng(thread_rng()).unwrap(); - let mut results: Vec = vec![]; + let mut results: Vec = vec![]; - let origin = &Pos2D::zero(); + let origin = &Grid2D::zero(); let x: u32 = (1_000_000); for i in 0..x { @@ -35,22 +35,22 @@ mod test { let a = results .iter() - .filter(|a| **a == Pos2D { x: 0, y: 1 }) + .filter(|a| **a == Grid2D { x: 0, y: 1 }) .count(); let b = results .iter() - .filter(|a| **a == Pos2D { x: 0, y: -1 }) + .filter(|a| **a == Grid2D { x: 0, y: -1 }) .count(); let c = results .iter() - .filter(|a| **a == Pos2D { x: 1, y: 0 }) + .filter(|a| **a == Grid2D { x: 1, y: 0 }) .count(); let d = results .iter() - .filter(|a| **a == Pos2D { x: -1, y: 0 }) + .filter(|a| **a == Grid2D { x: -1, y: 0 }) .count(); println!("{} {} {} {}", a as f32 / x as f32, b as f32 / x as f32, c as f32 / x as f32, d as f32 / x as f32);