diff --git a/src/agent.rs b/src/agent.rs index f576600..fe038cc 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -1,6 +1,6 @@ use crate::{ - grid::Grid, util::wrap, + buffer::Buf, }; use rand::{seq::SliceRandom, Rng}; @@ -40,9 +40,10 @@ impl Agent { i, } } - + + // Tick an agent #[inline] - pub fn tick(&mut self, grid: &Grid, sensor_distance: f32, sensor_angle: f32, rotation_angle: f32, step_distance: f32, width: usize, height: usize) { + pub fn tick(&mut self, buf: &Buf, sensor_distance: f32, sensor_angle: f32, rotation_angle: f32, step_distance: f32, width: usize, height: usize) { let xc = self.x + cos(self.angle) * sensor_distance; let yc = self.y + sin(self.angle) * sensor_distance; @@ -55,9 +56,9 @@ impl Agent { let yr = self.y + sin(agent_add_sens) * sensor_distance; // We sense from the buffer because this is where we previously combined data from all the grid. - let center = grid.get_buf(xc, yc); - let left = grid.get_buf(xl, yl); - let right = grid.get_buf(xr, yr); + let center = buf.get_buf(xc, yc); + let left = buf.get_buf(xl, yl); + let right = buf.get_buf(xr, yr); // Rotate and move logic let mut rng = rand::thread_rng(); diff --git a/src/buffer.rs b/src/buffer.rs new file mode 100644 index 0000000..ce62e29 --- /dev/null +++ b/src/buffer.rs @@ -0,0 +1,39 @@ +#[derive(Debug)] +pub struct Buf { + pub width: usize, + pub height: usize, + pub buf: Vec, +} + +impl Clone for Buf { + fn clone(&self) -> Buf { + Buf { + width: self.width, + height: self.height, + buf: self.buf.clone(), + } + } +} + +impl Buf { + pub fn new(width: usize, height: usize, buf: Vec) -> Self { + Buf { + width, + height, + buf, + } + } + + // Truncate x and y and return a corresponding index into the data slice. + fn index(&self, x: f32, y: f32) -> usize { + // x/y can come in negative, hence we shift them by width/height. + let i = (x + self.width as f32) as usize & (self.width - 1); + let j = (y + self.height as f32) as usize & (self.height - 1); + j * self.width + i + } + + // Get the buffer value at a given position. The implementation effectively treats data as periodic, hence any finite position will produce a value. + pub fn get_buf(&self, x: f32, y: f32) -> f32 { + self.buf[self.index(x, y)] + } +} \ No newline at end of file diff --git a/src/grid.rs b/src/grid.rs index f71367b..003083a 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,6 +1,7 @@ use crate::{ blur::Blur, agent::Agent, + buffer::Buf, }; use rand::{distributions::Uniform, Rng}; @@ -89,7 +90,8 @@ pub struct Grid { pub data: Vec, // Scratch space for the blur operation. - pub buf: Vec, + // pub buf: Vec, + pub buf: Buf, pub blur: Blur, pub agents: Vec } @@ -122,7 +124,7 @@ impl Grid { height, data, config: PopulationConfig::new(rng), - buf: vec![0.0; width * height], + buf: Buf::new(width, height, vec![0.0; width * height]), blur: Blur::new(width), agents, } @@ -136,10 +138,12 @@ impl Grid { j * self.width + i } + /* // Get the buffer value at a given position. The implementation effectively treats data as periodic, hence any finite position will produce a value. pub fn get_buf(&self, x: f32, y: f32) -> f32 { - self.buf[self.index(x, y)] + self.buf.buf[self.index(x, y)] } + */ // Add a value to the grid data at a given position. pub fn deposit(&mut self, x: f32, y: f32) { @@ -151,7 +155,7 @@ impl Grid { pub fn diffuse(&mut self, radius: usize) { self.blur.run( &mut self.data, - &mut self.buf, + &mut self.buf.buf, self.width, self.height, radius as f32, @@ -170,10 +174,10 @@ impl Grid { .. } = self.config; - let self_imut = self.clone(); // Create immutable copy of self before ticking agents (this is a very bad solution, needs to be improved) + let buf = self.buf.clone(); self.agents.par_iter_mut().for_each(|agent| { - agent.tick(&self_imut, + agent.tick(&buf, sensor_distance, sensor_angle, rotation_angle, step_distance, width, height); @@ -214,7 +218,7 @@ where T: AsRef<[f32]> + Sync, { let datas: Vec<_> = grids.iter().map(|grid| &grid.data).collect(); - let bufs: Vec<_> = grids.iter().map(|grid| &grid.buf).collect(); + let bufs: Vec<_> = grids.iter().map(|grid| &grid.buf.buf).collect(); // We mutate grid buffers and read grid data. We use unsafe because we need shared/unique borrows on different fields of the same Grid struct. bufs.iter().enumerate().for_each(|(i, buf)| unsafe { diff --git a/src/imgdata.rs b/src/imgdata.rs index 2a730c8..b87b76a 100644 --- a/src/imgdata.rs +++ b/src/imgdata.rs @@ -22,7 +22,7 @@ impl Clone for ThinGridData { impl ThinGridData { // Convert Grid to ThinGridData - pub fn from_grid(in_grid: &Grid) -> Self { + pub fn new_from_grid(in_grid: &Grid) -> Self { ThinGridData { width: in_grid.width, height: in_grid.height, @@ -31,9 +31,9 @@ impl ThinGridData { } #[allow(dead_code)] - pub fn from_grid_vec(in_grids: Vec) -> Vec { + pub fn new_from_grid_vec(in_grids: Vec) -> Vec { return in_grids.iter().map(|grid|{ - Self::from_grid(grid) + Self::new_from_grid(grid) }).collect(); } diff --git a/src/lib.rs b/src/lib.rs index e08bdad..0cfd952 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,5 @@ mod imgdata; // for storing image data pub mod model; mod palette; mod util; // for math things -mod agent; \ No newline at end of file +mod agent; +mod buffer; \ No newline at end of file diff --git a/src/model.rs b/src/model.rs index f1f02f1..6e47c0e 100644 --- a/src/model.rs +++ b/src/model.rs @@ -165,14 +165,8 @@ impl Model { ); } - fn strip_grid_data(grids: Vec) -> Vec { - return grids.iter().map(|grid| { - ThinGridData::from_grid(grid) - }).collect(); - } - fn save_image_data(&mut self) { - let grids = Self::strip_grid_data(self.grids.clone()); + let grids = ThinGridData::new_from_grid_vec(self.grids.clone()); let img_data = ImgData::new(grids, self.palette, self.iteration); self.img_data_vec.push(img_data); if self.grids[0].width > 1024 && self.grids[0].height > 1024 && self.img_data_vec.len() > 100 {