From 4ef577e76b39f31877276588018a614cf7b38e36 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Sun, 23 Jul 2023 23:45:00 -0400 Subject: [PATCH] space colonization, fooling around with rust --- src/components/background.rs | 14 +- src/space_colonization/mod.rs | 22 +- src/space_colonization/point.rs | 2 +- src/space_colonization/space_colonization.rs | 143 ++++--------- .../space_colonization.rs.bak | 201 ++++++++++++++++++ 5 files changed, 267 insertions(+), 115 deletions(-) create mode 100644 src/space_colonization/space_colonization.rs.bak diff --git a/src/components/background.rs b/src/components/background.rs index d92faac..5399d00 100644 --- a/src/components/background.rs +++ b/src/components/background.rs @@ -18,7 +18,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let height = canvas_parent.client_height(); canvas.set_width(u32::try_from(width).unwrap()); canvas.set_height(u32::try_from(height).unwrap()); - let sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap()); + let mut sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap()); // TODO Resize on window resize log!( "TODO resize on window resize canvas parent size = {} {}", @@ -38,8 +38,8 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { context.set_fill_style(&JsValue::from("yellow")); log!("About to render nodes"); let start_time = window().unwrap().performance().unwrap().now(); - for n in sc.root_nodes.iter() { - context.fill_rect(n.read().unwrap().position.x.into(), n.read().unwrap().position.y.into(), 5.0, 5.0); + for n in sc.root_nodes.borrow().iter() { + context.fill_rect(n.position.x.into(), n.position.y.into(), 5.0, 5.0); } context.begin_path(); @@ -52,14 +52,14 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { context.stroke(); context.set_fill_style(&JsValue::from("magenta")); - for a in sc.attractors.iter() { - context.fill_rect(a.read().unwrap().position.x.into(), a.read().unwrap().position.y.into(), 5.0, 5.0); + for a in sc.attractors.borrow().iter() { + context.fill_rect(a.position.x.into(), a.position.y.into(), 5.0, 5.0); } let end_time = window().unwrap().performance().unwrap().now(); log!( "Rendering {} nodes and {} attractors took {}", - sc.root_nodes.len(), - sc.attractors.len(), + sc.root_nodes.borrow().len(), + sc.attractors.borrow().len(), end_time - start_time ); diff --git a/src/space_colonization/mod.rs b/src/space_colonization/mod.rs index 5e497f6..1d8d770 100644 --- a/src/space_colonization/mod.rs +++ b/src/space_colonization/mod.rs @@ -1,4 +1,4 @@ -use std::sync::RwLock; +use std::cell::RefCell; use wasm_bindgen::prelude::*; @@ -13,25 +13,33 @@ extern "C" { fn performance() -> web_sys::Performance; } +#[derive(Debug)] pub struct Attractor { pub position: Point, pub dead: bool, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct Node { pub position: Point, - pub children: Vec>, + pub children: RefCell>, +} + +impl std::hash::Hash for Node { + fn hash(&self, state: &mut H) { + self.position.hash(state); + } } impl Node { pub fn render(&self, render_id: f64, render_fn: F) where F: Copy + Fn(&Node, &Node) { - for child in self.children.iter() { - render_fn(self, &child.read().unwrap()); + let children = self.children.borrow(); + for child in children.iter() { + render_fn(self, &child); } - for child in self.children.iter() { - child.read().unwrap().render(render_id, render_fn); + for child in children.iter() { + child.render(render_id, render_fn); } } } diff --git a/src/space_colonization/point.rs b/src/space_colonization/point.rs index 80ff663..ed8a2be 100644 --- a/src/space_colonization/point.rs +++ b/src/space_colonization/point.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Hash)] pub struct Point { pub x: i32, pub y: i32, diff --git a/src/space_colonization/space_colonization.rs b/src/space_colonization/space_colonization.rs index 8692413..9003236 100644 --- a/src/space_colonization/space_colonization.rs +++ b/src/space_colonization/space_colonization.rs @@ -1,4 +1,6 @@ -use std::sync::RwLock; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; use super::{Attractor, Node, Point}; use rand::thread_rng; @@ -20,18 +22,18 @@ pub struct SpaceColonization { /// /// If density is 10, then there will be an average distance of 10 between attractors density: i32, - pub root_nodes: Vec>, - pub attractors: Vec>, + pub root_nodes: Rc>>, + pub attractors: Rc>>, } impl SpaceColonization { pub fn new(width: i32, height: i32) -> SpaceColonization { - let mut root_nodes = Vec::new(); - root_nodes.push(RwLock::new(Node { + let root_nodes = Rc::new(RefCell::new(Vec::new())); + root_nodes.borrow_mut().push(Node { position: Point { x: 100, y: 100 }, - children: Vec::new(), - })); - let attractors = Vec::new(); + children: RefCell::new(Vec::new()), + }); + let attractors = Rc::new(RefCell::new(Vec::new())); let mut sc = SpaceColonization { max_point: Point { @@ -55,8 +57,8 @@ impl SpaceColonization { where F: Copy + Fn(&Node, &Node), { - for n in self.root_nodes.iter() { - n.read().unwrap().render(render_id, render_fn); + for n in self.root_nodes.borrow().iter() { + n.render(render_id, render_fn); } } @@ -67,10 +69,10 @@ impl SpaceColonization { let mut y_pos = 0; while x_pos < self.max_point.x { while y_pos < self.max_point.y { - self.attractors.push(RwLock::new(Attractor { + self.attractors.borrow_mut().push(Attractor { position: self.get_random_point(x_pos.into(), y_pos.into()), dead: false, - })); + }); y_pos += self.density; } x_pos += self.density; @@ -105,97 +107,38 @@ impl SpaceColonization { } } - pub fn grow(&self) { - for n in self.root_nodes.iter() { - self.grow_node(n); - } - // iterate through the list of nodes that are not dead yet (still had an active attractor - // previous iteration) - // For each node : - // find attractors within attraction distance of node - // calculate distance to affecting attractors - // determine how many new nodes grow from here - // determine position of new nodes - // remove current node from leaves list - } + pub fn grow(&mut self) { + // iterate through attractors + // find closest node within attraction range + // build a map of nodes to affecting attractors + // attractors within the attraction range that this node is the closest to + // + // calculate new node position + let root_nodes = self.root_nodes.borrow(); + let attractors = self.attractors.borrow(); + let mut growing_paths : HashMap<&Node, Vec<&Attractor>> = HashMap::new(); + for a in attractors.iter() { + let mut closest_node: Option<&Node> = None; + let mut closest_node_distance = f64::MAX; - fn grow_node(&self, n: &RwLock) { - n.read() - .unwrap() - .children - .iter() - .for_each(|n| self.grow_node(n)); - let affecting_attractors = self.find_affecting_attractors(&n); - console::log_1( - &format!("Found {} affecting attractors", affecting_attractors.len()).into(), - ); - let new_node = self.create_new_node(n, &affecting_attractors); - for a in affecting_attractors { - let mut a = a.write().unwrap(); - if n.read().unwrap().position.distance(&a.position) < self.kill_distance as f64 { - a.dead = true; + for n in root_nodes.iter() { // TODO iterate on children nodes + let distance = n.position.distance(&a.position); + if distance <= self.attraction_distance as f64 { + console::log_1(&format!("found node within attraction distance {:?}", a).into()); + if distance < closest_node_distance { + closest_node = Some(n); + closest_node_distance = distance; + } + } + } + if let Some(node) = closest_node { + let attractors = match growing_paths.get_mut(node) { + Some(a) => a.take(), + None => Vec::new(), + }; } } - console::log_1(&format!("New node {:?}", new_node).into()); - n.write().unwrap().children.push(RwLock::new(new_node)); + console::log_1(&format!("found {} pairs ", growing_paths.len()).into()); } - fn find_affecting_attractors(&self, n: &RwLock) -> Vec<&RwLock> { - let mut affecting = Vec::new(); - for a in self.attractors.iter() { - let aread = a.read().unwrap(); - // TODO remove attractors instead of marking them dead - // used to display them at the moment but could also be moved - // to a dead queue - if aread.dead { - continue; - } - - let n_distance = n.read().unwrap().position.distance(&aread.position); - // todo for some reason I cannot verify closest node to attractor here - if n_distance < self.attraction_distance.into() && self.is_closest_node(&n.read().unwrap(), &aread) { - affecting.push(a) - } - } - - affecting - } - - fn create_new_node( - &self, - n: &RwLock, - affecting_attractors: &Vec<&RwLock>, - ) -> Node { - let n = n.read().unwrap(); - let mut attraction_sum_x = 0; - let mut attraction_sum_y = 0; - for a in affecting_attractors.iter() { - attraction_sum_x += n.position.x - a.read().unwrap().position.x; - attraction_sum_y += n.position.y - a.read().unwrap().position.y; - } - - Node { - position: Point { - x: n.position.x + attraction_sum_x / affecting_attractors.len() as i32, - y: n.position.y + attraction_sum_y / affecting_attractors.len() as i32, - }, - children: Vec::new(), - } - } - - fn is_closest_node(&self, node: &Node, a: &Attractor) -> bool { - let node_distance = node.position.distance(&a.position); - for n in self.root_nodes.iter() { - let n_read = match n.read() { - Ok(val) => val, - Err(e) => todo!("Cannot read node {}", e), - }; - if n_read.position.distance(&a.position) < node_distance { - return false; - } - // todo iterate the entire tree - todo!(); - } - return true; - } } diff --git a/src/space_colonization/space_colonization.rs.bak b/src/space_colonization/space_colonization.rs.bak new file mode 100644 index 0000000..8692413 --- /dev/null +++ b/src/space_colonization/space_colonization.rs.bak @@ -0,0 +1,201 @@ +use std::sync::RwLock; + +use super::{Attractor, Node, Point}; +use rand::thread_rng; +use rand::Rng; +use web_sys::console; +use web_sys::window; + +pub struct SpaceColonization { + max_point: Point, + /// When a node grows within kill_distance of an attractor, the attractor is killed + kill_distance: i32, + /// Maximum distance between an attractor and a node for the node to + /// be affected by the attractor. + /// + /// Must be greater than sqrt((density)**2 + (density)**2) + attraction_distance: i32, + segment_length: i32, + /// Size of the cells on which attractors are placed. + /// + /// If density is 10, then there will be an average distance of 10 between attractors + density: i32, + pub root_nodes: Vec>, + pub attractors: Vec>, +} + +impl SpaceColonization { + pub fn new(width: i32, height: i32) -> SpaceColonization { + let mut root_nodes = Vec::new(); + root_nodes.push(RwLock::new(Node { + position: Point { x: 100, y: 100 }, + children: Vec::new(), + })); + let attractors = Vec::new(); + + let mut sc = SpaceColonization { + max_point: Point { + x: width, + y: height, + }, + kill_distance: 10, + attraction_distance: 43, + segment_length: 5, + density: 30, + root_nodes, + attractors, + }; + + sc.place_attractors(); + + return sc; + } + + pub fn render_nodes(&self, render_id: f64, render_fn: F) + where + F: Copy + Fn(&Node, &Node), + { + for n in self.root_nodes.iter() { + n.read().unwrap().render(render_id, render_fn); + } + } + + fn place_attractors(&mut self) { + let start_time = window().unwrap().performance().unwrap().now(); + console::log_1(&format!("Start placing attractors {}", start_time).into()); + let mut x_pos = 0; + let mut y_pos = 0; + while x_pos < self.max_point.x { + while y_pos < self.max_point.y { + self.attractors.push(RwLock::new(Attractor { + position: self.get_random_point(x_pos.into(), y_pos.into()), + dead: false, + })); + y_pos += self.density; + } + x_pos += self.density; + y_pos = 0; + } + let end_time = window().unwrap().performance().unwrap().now(); + let elapsed = end_time - start_time; + console::log_1(&format!("Done placing attractors , took : {}", elapsed).into()); + } + + fn get_random_point(&self, x_pos: i32, y_pos: i32) -> Point { + let half_density: i32 = (self.density / 2).into(); + let mut x_min = x_pos - half_density; + if x_min < 0 { + x_min = 0; + } + + let mut y_min = y_pos - half_density; + if y_min < 0 { + y_min = 0; + } + + Point { + x: thread_rng() + .gen_range(x_min..x_pos + half_density) + .try_into() + .unwrap(), + y: thread_rng() + .gen_range(y_min..y_pos + half_density) + .try_into() + .unwrap(), + } + } + + pub fn grow(&self) { + for n in self.root_nodes.iter() { + self.grow_node(n); + } + // iterate through the list of nodes that are not dead yet (still had an active attractor + // previous iteration) + // For each node : + // find attractors within attraction distance of node + // calculate distance to affecting attractors + // determine how many new nodes grow from here + // determine position of new nodes + // remove current node from leaves list + } + + fn grow_node(&self, n: &RwLock) { + n.read() + .unwrap() + .children + .iter() + .for_each(|n| self.grow_node(n)); + let affecting_attractors = self.find_affecting_attractors(&n); + console::log_1( + &format!("Found {} affecting attractors", affecting_attractors.len()).into(), + ); + let new_node = self.create_new_node(n, &affecting_attractors); + for a in affecting_attractors { + let mut a = a.write().unwrap(); + if n.read().unwrap().position.distance(&a.position) < self.kill_distance as f64 { + a.dead = true; + } + } + console::log_1(&format!("New node {:?}", new_node).into()); + n.write().unwrap().children.push(RwLock::new(new_node)); + } + + fn find_affecting_attractors(&self, n: &RwLock) -> Vec<&RwLock> { + let mut affecting = Vec::new(); + for a in self.attractors.iter() { + let aread = a.read().unwrap(); + // TODO remove attractors instead of marking them dead + // used to display them at the moment but could also be moved + // to a dead queue + if aread.dead { + continue; + } + + let n_distance = n.read().unwrap().position.distance(&aread.position); + // todo for some reason I cannot verify closest node to attractor here + if n_distance < self.attraction_distance.into() && self.is_closest_node(&n.read().unwrap(), &aread) { + affecting.push(a) + } + } + + affecting + } + + fn create_new_node( + &self, + n: &RwLock, + affecting_attractors: &Vec<&RwLock>, + ) -> Node { + let n = n.read().unwrap(); + let mut attraction_sum_x = 0; + let mut attraction_sum_y = 0; + for a in affecting_attractors.iter() { + attraction_sum_x += n.position.x - a.read().unwrap().position.x; + attraction_sum_y += n.position.y - a.read().unwrap().position.y; + } + + Node { + position: Point { + x: n.position.x + attraction_sum_x / affecting_attractors.len() as i32, + y: n.position.y + attraction_sum_y / affecting_attractors.len() as i32, + }, + children: Vec::new(), + } + } + + fn is_closest_node(&self, node: &Node, a: &Attractor) -> bool { + let node_distance = node.position.distance(&a.position); + for n in self.root_nodes.iter() { + let n_read = match n.read() { + Ok(val) => val, + Err(e) => todo!("Cannot read node {}", e), + }; + if n_read.position.distance(&a.position) < node_distance { + return false; + } + // todo iterate the entire tree + todo!(); + } + return true; + } +}