Parallel agents movement.
This commit is contained in:
parent
cbc3fcbe06
commit
241e6ba6bf
@ -14,14 +14,10 @@ pub struct Grid {
|
|||||||
blur: Blur,
|
blur: Blur,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_power_of_two(x: usize) -> bool {
|
|
||||||
(x & (x - 1)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
/// Create a new grid filled with random floats in the [0.0..1.0) range.
|
/// Create a new grid filled with random floats in the [0.0..1.0) range.
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
|
use crate::util::is_power_of_two;
|
||||||
if !is_power_of_two(width) || !is_power_of_two(height) {
|
if !is_power_of_two(width) || !is_power_of_two(height) {
|
||||||
panic!("Grid dimensitions must be a power of two.");
|
panic!("Grid dimensitions must be a power of two.");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,3 +2,4 @@ mod blur;
|
|||||||
mod grid;
|
mod grid;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod trig;
|
pub mod trig;
|
||||||
|
mod util;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use physarum::model;
|
use physarum::model;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let model = model::Model::new(4, 4, 20, 1);
|
let mut model = model::Model::new(4, 4, 20, 1);
|
||||||
|
println!("{:#?}", model);
|
||||||
|
model.step();
|
||||||
println!("{:#?}", model);
|
println!("{:#?}", model);
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/model.rs
73
src/model.rs
@ -1,6 +1,7 @@
|
|||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
|
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{seq::SliceRandom, thread_rng, Rng};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
/// A single Physarum agent. The x and y positions are continuous, hence we use floating point
|
/// A single Physarum agent. The x and y positions are continuous, hence we use floating point
|
||||||
/// numbers instead of integers.
|
/// numbers instead of integers.
|
||||||
@ -13,8 +14,7 @@ struct Agent {
|
|||||||
|
|
||||||
impl Agent {
|
impl Agent {
|
||||||
/// Construct a new agent with random parameters.
|
/// Construct a new agent with random parameters.
|
||||||
fn new(width: usize, height: usize) -> Self {
|
fn new<R: Rng + ?Sized>(width: usize, height: usize, rng: &mut R) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
||||||
Agent {
|
Agent {
|
||||||
x: x * width as f32,
|
x: x * width as f32,
|
||||||
@ -22,6 +22,22 @@ impl Agent {
|
|||||||
angle: angle * std::f32::consts::TAU,
|
angle: angle * std::f32::consts::TAU,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update agent's orientation angle and position on the grid.
|
||||||
|
fn rotate_and_move(
|
||||||
|
&mut self,
|
||||||
|
direction: f32,
|
||||||
|
rotation_angle: f32,
|
||||||
|
step_distance: f32,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
) {
|
||||||
|
use crate::util::wrap;
|
||||||
|
let delta_angle = rotation_angle * direction;
|
||||||
|
self.angle = wrap(self.angle + delta_angle, std::f32::consts::TAU);
|
||||||
|
self.x = wrap(self.x + step_distance * self.angle.cos(), width as f32);
|
||||||
|
self.y = wrap(self.y + step_distance * self.angle.sin(), height as f32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A model configuration. We make it into a separate type, because we will eventually have multiple
|
/// A model configuration. We make it into a separate type, because we will eventually have multiple
|
||||||
@ -50,9 +66,7 @@ impl PopulationConfig {
|
|||||||
const DECAY_FACTOR_MAX: f32 = 0.1;
|
const DECAY_FACTOR_MAX: f32 = 0.1;
|
||||||
|
|
||||||
/// Construct a random configuration.
|
/// Construct a random configuration.
|
||||||
pub fn new() -> Self {
|
pub fn new<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
|
|
||||||
PopulationConfig {
|
PopulationConfig {
|
||||||
sensor_distance: rng.gen_range(Self::SENSOR_DISTANCE_MIN..=Self::SENSOR_DISTANCE_MAX),
|
sensor_distance: rng.gen_range(Self::SENSOR_DISTANCE_MIN..=Self::SENSOR_DISTANCE_MAX),
|
||||||
step_distance: rng.gen_range(Self::STEP_DISTANCE_MIN..=Self::STEP_DISTANCE_MAX),
|
step_distance: rng.gen_range(Self::STEP_DISTANCE_MIN..=Self::STEP_DISTANCE_MAX),
|
||||||
@ -81,28 +95,54 @@ pub struct Model {
|
|||||||
config: PopulationConfig,
|
config: PopulationConfig,
|
||||||
|
|
||||||
iteration: i32,
|
iteration: i32,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
/// Construct a new model with random initial conditions and random configuration.
|
/// Construct a new model with random initial conditions and random configuration.
|
||||||
pub fn new(width: usize, height: usize, n_particles: usize, diffusivity: usize) -> Self {
|
pub fn new(width: usize, height: usize, n_particles: usize, diffusivity: usize) -> Self {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
Model {
|
Model {
|
||||||
agents: (0..n_particles)
|
agents: (0..n_particles)
|
||||||
.map(|_| Agent::new(width, height))
|
.map(|_| Agent::new(width, height, &mut rng))
|
||||||
.collect(),
|
.collect(),
|
||||||
grid: Grid::new(width, height),
|
grid: Grid::new(width, height),
|
||||||
diffusivity,
|
diffusivity,
|
||||||
config: PopulationConfig::new(),
|
config: PopulationConfig::new(&mut rng),
|
||||||
iteration: 0,
|
iteration: 0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pick_direction<R: Rng + ?Sized>(center: f32, left: f32, right: f32, rng: &mut R) -> f32 {
|
||||||
|
if (center > left) && (center > right) {
|
||||||
|
0.0
|
||||||
|
} else if (center < left) && (center < right) {
|
||||||
|
*[-1.0, 1.0].choose(rng).unwrap()
|
||||||
|
} else if left < right {
|
||||||
|
1.0
|
||||||
|
} else if right < left {
|
||||||
|
-1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a single simulation step.
|
/// Perform a single simulation step.
|
||||||
pub fn step(&mut self) {
|
pub fn step(&mut self) {
|
||||||
|
// To avoid borrow-checker errors inside the parallel loop.
|
||||||
let sensor_distance = self.config.sensor_distance;
|
let sensor_distance = self.config.sensor_distance;
|
||||||
let sensor_angle = self.config.sensor_angle;
|
let sensor_angle = self.config.sensor_angle;
|
||||||
|
let rotation_angle = self.config.rotation_angle;
|
||||||
|
let step_distance = self.config.step_distance;
|
||||||
|
let (width, height) = (self.width, self.height);
|
||||||
|
let grid = &self.grid;
|
||||||
|
|
||||||
for agent in self.agents.iter_mut() {
|
self.agents.par_iter_mut().for_each(|agent| {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
let xc = agent.x + agent.angle.cos() * sensor_distance;
|
let xc = agent.x + agent.angle.cos() * sensor_distance;
|
||||||
let yc = agent.y + agent.angle.sin() * sensor_distance;
|
let yc = agent.y + agent.angle.sin() * sensor_distance;
|
||||||
let xl = agent.x + (agent.angle - sensor_angle).cos() * sensor_distance;
|
let xl = agent.x + (agent.angle - sensor_angle).cos() * sensor_distance;
|
||||||
@ -111,12 +151,15 @@ impl Model {
|
|||||||
let yr = agent.y + (agent.angle + sensor_angle).sin() * sensor_distance;
|
let yr = agent.y + (agent.angle + sensor_angle).sin() * sensor_distance;
|
||||||
|
|
||||||
// Sense
|
// Sense
|
||||||
let trail_c = self.grid.get(xc, yc);
|
let trail_c = grid.get(xc, yc);
|
||||||
let trail_l = self.grid.get(xl, yl);
|
let trail_l = grid.get(xl, yl);
|
||||||
let trail_r = self.grid.get(xr, yr);
|
let trail_r = grid.get(xr, yr);
|
||||||
// Rotate
|
|
||||||
// Move
|
// Rotate and move
|
||||||
}
|
let direction = Model::pick_direction(trail_c, trail_l, trail_r, &mut rng);
|
||||||
|
agent.rotate_and_move(direction, rotation_angle, step_distance, width, height);
|
||||||
|
});
|
||||||
|
|
||||||
// Deposit + Diffuse + Decay
|
// Deposit + Diffuse + Decay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/util.rs
Normal file
9
src/util.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#[inline(always)]
|
||||||
|
pub fn is_power_of_two(x: usize) -> bool {
|
||||||
|
(x & (x - 1)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn wrap(x: f32, max: f32) -> f32 {
|
||||||
|
x - max * ((x > max) as i32 as f32 - (x < 0.0_f32) as i32 as f32)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user