Compare commits
7 Commits
ab226026c3
...
0b3abe71ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
0b3abe71ae
|
|||
|
e6cfab4a02
|
|||
|
e3fff76792
|
|||
|
ff769df97b
|
|||
|
4330101b68
|
|||
|
b4e2390690
|
|||
|
b0c9d3888e
|
892
Cargo.lock
generated
892
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -5,11 +5,11 @@ authors = ["Simon Gardling <titaniumtown@gmail.com>", "mindv0rtex <mindv0rtex@us
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.23"
|
image = "0.25"
|
||||||
indicatif = { version = "0.15", features = [ "rayon" ] }
|
indicatif = { version = "0.17", features = [ "rayon" ] }
|
||||||
itertools = "0.10"
|
itertools = "0.14"
|
||||||
rand = "0.8"
|
rand = "0.9"
|
||||||
rand_distr = "0.4"
|
rand_distr = "0.5"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
fastapprox = "0.3"
|
fastapprox = "0.3"
|
||||||
|
|
||||||
|
|||||||
25
src/agent.rs
25
src/agent.rs
@@ -1,6 +1,7 @@
|
|||||||
use crate::{buffer::Buf, util::wrap};
|
use crate::{buffer::Buf, util::wrap};
|
||||||
use fastapprox::faster::{cos, sin};
|
use fastapprox::faster::{cos, sin};
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::prelude::IndexedRandom;
|
||||||
|
use rand::Rng;
|
||||||
use std::f32::consts::TAU;
|
use std::f32::consts::TAU;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ use std::fmt::{Display, Formatter};
|
|||||||
pub struct Agent {
|
pub struct Agent {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
angle: f32,
|
heading: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Agent {
|
impl Display for Agent {
|
||||||
@@ -21,11 +22,11 @@ impl Display for Agent {
|
|||||||
impl Agent {
|
impl Agent {
|
||||||
/// Construct a new agent with random parameters.
|
/// Construct a new agent with random parameters.
|
||||||
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, rng: &mut R) -> Self {
|
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, rng: &mut R) -> Self {
|
||||||
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
let (x, y, angle) = rng.random::<(f32, f32, f32)>();
|
||||||
Agent {
|
Agent {
|
||||||
x: x * width as f32,
|
x: x * width as f32,
|
||||||
y: y * height as f32,
|
y: y * height as f32,
|
||||||
angle: angle * TAU,
|
heading: angle * TAU,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,11 +41,11 @@ impl Agent {
|
|||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
) {
|
) {
|
||||||
let xc = self.x + cos(self.angle) * sensor_distance;
|
let xc = self.x + cos(self.heading) * sensor_distance;
|
||||||
let yc = self.y + sin(self.angle) * sensor_distance;
|
let yc = self.y + sin(self.heading) * sensor_distance;
|
||||||
|
|
||||||
let agent_add_sens = self.angle + sensor_angle;
|
let agent_add_sens = self.heading + sensor_angle;
|
||||||
let agent_sub_sens = self.angle - sensor_angle;
|
let agent_sub_sens = self.heading - sensor_angle;
|
||||||
|
|
||||||
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
|
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
|
||||||
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
|
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
|
||||||
@@ -61,7 +62,7 @@ impl Agent {
|
|||||||
0.0
|
0.0
|
||||||
} else if (center < left) && (center < right) {
|
} else if (center < left) && (center < right) {
|
||||||
*[-1.0, 1.0]
|
*[-1.0, 1.0]
|
||||||
.choose(&mut rand::thread_rng())
|
.choose(&mut rand::rng())
|
||||||
.expect("unable to choose random direction")
|
.expect("unable to choose random direction")
|
||||||
} else if left < right {
|
} else if left < right {
|
||||||
1.0
|
1.0
|
||||||
@@ -73,8 +74,8 @@ impl Agent {
|
|||||||
|
|
||||||
let delta_angle = rotation_angle * direction;
|
let delta_angle = rotation_angle * direction;
|
||||||
|
|
||||||
self.angle = wrap(self.angle + delta_angle, TAU);
|
self.heading = wrap(self.heading + delta_angle, TAU);
|
||||||
self.x = wrap(self.x + step_distance * cos(self.angle), width as f32);
|
self.x = wrap(self.x + step_distance * cos(self.heading), width as f32);
|
||||||
self.y = wrap(self.y + step_distance * sin(self.angle), height as f32);
|
self.y = wrap(self.y + step_distance * sin(self.heading), height as f32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
41
src/grid.rs
41
src/grid.rs
@@ -1,17 +1,17 @@
|
|||||||
use crate::{agent::Agent, blur::Blur, buffer::Buf};
|
use crate::{agent::Agent, blur::Blur, buffer::Buf};
|
||||||
use rand::{distributions::Uniform, Rng};
|
use rand::Rng;
|
||||||
|
use rand_distr::Uniform;
|
||||||
use rayon::{iter::ParallelIterator, prelude::*};
|
use rayon::{iter::ParallelIterator, prelude::*};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
/// A population configuration.
|
/// A population configuration.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PopulationConfig {
|
pub struct PopulationConfig {
|
||||||
pub sensor_distance: f32,
|
sensor_distance: f32,
|
||||||
pub step_distance: f32,
|
step_distance: f32,
|
||||||
pub sensor_angle: f32,
|
sensor_angle: f32,
|
||||||
pub rotation_angle: f32,
|
rotation_angle: f32,
|
||||||
|
|
||||||
decay_factor: f32,
|
|
||||||
deposition_amount: f32,
|
deposition_amount: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,12 +25,11 @@ impl PopulationConfig {
|
|||||||
/// Construct a random configuration.
|
/// Construct a random configuration.
|
||||||
pub fn new<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
pub fn new<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||||
PopulationConfig {
|
PopulationConfig {
|
||||||
sensor_distance: rng.gen_range(0.0..=64.0),
|
sensor_distance: rng.random_range(0.0..=64.0),
|
||||||
step_distance: rng.gen_range(0.2..=2.0),
|
step_distance: rng.random_range(0.2..=2.0),
|
||||||
decay_factor: rng.gen_range(0.1..=0.1),
|
sensor_angle: rng.random_range(0.0_f32..=120.0).to_radians(),
|
||||||
sensor_angle: rng.gen_range(0.0_f32..=120.0).to_radians(),
|
rotation_angle: rng.random_range(0.0_f32..=120.0).to_radians(),
|
||||||
rotation_angle: rng.gen_range(0.0_f32..=120.0).to_radians(),
|
deposition_amount: rng.random_range(5.0..=5.0),
|
||||||
deposition_amount: rng.gen_range(5.0..=5.0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +58,7 @@ impl Grid {
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
agents: Vec<Agent>,
|
agents: Vec<Agent>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let range = Uniform::from(0.0..1.0);
|
let range = Uniform::new(0.0, 1.0).expect("unable to create uniform distr");
|
||||||
let data = rng.sample_iter(range).take(width * height).collect();
|
let data = rng.sample_iter(range).take(width * height).collect();
|
||||||
|
|
||||||
Grid {
|
Grid {
|
||||||
@@ -78,12 +77,6 @@ impl Grid {
|
|||||||
crate::util::index(self.width, self.height, x, y)
|
crate::util::index(self.width, self.height, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a value to the grid data at a given position.
|
|
||||||
pub fn deposit(&mut self, x: f32, y: f32) {
|
|
||||||
let idx = self.index(x, y);
|
|
||||||
self.data[idx] += self.config.deposition_amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Diffuse grid data and apply a decay multiplier.
|
/// Diffuse grid data and apply a decay multiplier.
|
||||||
pub fn diffuse(&mut self, radius: usize) {
|
pub fn diffuse(&mut self, radius: usize) {
|
||||||
self.blur.run(
|
self.blur.run(
|
||||||
@@ -92,7 +85,7 @@ impl Grid {
|
|||||||
self.width,
|
self.width,
|
||||||
self.height,
|
self.height,
|
||||||
radius as f32,
|
radius as f32,
|
||||||
self.config.decay_factor,
|
0.1, // decay is always 0.1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,9 +114,9 @@ impl Grid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deposit_all(&mut self) {
|
pub fn deposit_all(&mut self) {
|
||||||
let agent_list = self.agents.clone();
|
for agent in self.agents.iter() {
|
||||||
for agent in agent_list.iter() {
|
let idx = self.index(agent.x, agent.y);
|
||||||
self.deposit(agent.x, agent.y);
|
self.data[idx] += self.config.deposition_amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +149,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_grid_new() {
|
fn test_grid_new() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
let grid = Grid::new(8, 8, &mut rng, vec![]);
|
let grid = Grid::new(8, 8, &mut rng, vec![]);
|
||||||
assert_eq!(grid.index(0.5, 0.6), 0);
|
assert_eq!(grid.index(0.5, 0.6), 0);
|
||||||
assert_eq!(grid.index(1.5, 0.6), 1);
|
assert_eq!(grid.index(1.5, 0.6), 1);
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ pub struct ThinGridData {
|
|||||||
|
|
||||||
impl ThinGridData {
|
impl ThinGridData {
|
||||||
/// Convert Grid to ThinGridData
|
/// Convert Grid to ThinGridData
|
||||||
pub fn new_from_grid(in_grid: &Grid) -> Self {
|
pub fn new_from_grid(in_grid: Grid) -> Self {
|
||||||
ThinGridData {
|
ThinGridData {
|
||||||
width: in_grid.width,
|
width: in_grid.width,
|
||||||
height: in_grid.height,
|
height: in_grid.height,
|
||||||
data: in_grid.data.clone(),
|
data: in_grid.data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_grid_vec(in_grids: &[Grid]) -> Vec<Self> {
|
pub fn new_from_grid_vec(in_grids: &[Grid]) -> Vec<Self> {
|
||||||
in_grids.iter().map(Self::new_from_grid).collect()
|
in_grids.iter().cloned().map(Self::new_from_grid).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// from grid.rs (needed in image gen)
|
/// from grid.rs (needed in image gen)
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ impl Model {
|
|||||||
let particles_per_grid = (n_particles as f64 / n_populations as f64).ceil() as usize;
|
let particles_per_grid = (n_particles as f64 / n_populations as f64).ceil() as usize;
|
||||||
let _n_particles = particles_per_grid * n_populations;
|
let _n_particles = particles_per_grid * n_populations;
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
let attraction_distr =
|
let attraction_distr =
|
||||||
Normal::new(Self::ATTRACTION_FACTOR_MEAN, Self::ATTRACTION_FACTOR_STD).unwrap();
|
Normal::new(Self::ATTRACTION_FACTOR_MEAN, Self::ATTRACTION_FACTOR_STD).unwrap();
|
||||||
@@ -111,7 +111,7 @@ impl Model {
|
|||||||
pub fn run(&mut self, steps: usize) {
|
pub fn run(&mut self, steps: usize) {
|
||||||
let pb = ProgressBar::new(steps as u64);
|
let pb = ProgressBar::new(steps as u64);
|
||||||
pb.set_style(ProgressStyle::default_bar()
|
pb.set_style(ProgressStyle::default_bar()
|
||||||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})")
|
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})").expect("invalid progresstyle template")
|
||||||
.progress_chars("#>-"));
|
.progress_chars("#>-"));
|
||||||
|
|
||||||
for _ in 0..steps {
|
for _ in 0..steps {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Palette {
|
pub struct Palette {
|
||||||
@@ -6,8 +6,8 @@ pub struct Palette {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_palette() -> Palette {
|
pub fn random_palette() -> Palette {
|
||||||
let mut rng = thread_rng();
|
let mut rng = rand::rng();
|
||||||
let mut palette = PALETTES[rng.gen_range(0..PALETTES.len())];
|
let mut palette = PALETTES[rng.random_range(0..PALETTES.len())];
|
||||||
palette.colors.shuffle(&mut rng);
|
palette.colors.shuffle(&mut rng);
|
||||||
palette
|
palette
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn wrap(x: f32, max: f32) -> f32 {
|
pub fn wrap(x: f32, max: f32) -> f32 {
|
||||||
x - max * ((x > max) as i32 - x.is_sign_negative() as i32) as f32
|
// x - max * ((x > max) as i32 - x.is_sign_negative() as i32) as f32
|
||||||
|
x.rem_euclid(max)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Truncate x and y and return a corresponding index into the data slice.
|
/// Truncate x and y and return a corresponding index into the data slice.
|
||||||
|
|||||||
Reference in New Issue
Block a user