space colonization, fooling around with rust
This commit is contained in:
parent
6f2505736c
commit
4ef577e76b
@ -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
|
||||
);
|
||||
|
||||
|
||||
@ -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<RwLock<Node>>,
|
||||
pub children: RefCell<Vec<Node>>,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Node {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.position.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn render<F>(&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Point {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
|
||||
@ -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<RwLock<Node>>,
|
||||
pub attractors: Vec<RwLock<Attractor>>,
|
||||
pub root_nodes: Rc<RefCell<Vec<Node>>>,
|
||||
pub attractors: Rc<RefCell<Vec<Attractor>>>,
|
||||
}
|
||||
|
||||
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<Node>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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<Node>) -> Vec<&RwLock<Attractor>> {
|
||||
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<Node>,
|
||||
affecting_attractors: &Vec<&RwLock<Attractor>>,
|
||||
) -> 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 let Some(node) = closest_node {
|
||||
let attractors = match growing_paths.get_mut(node) {
|
||||
Some(a) => a.take(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
if n_read.position.distance(&a.position) < node_distance {
|
||||
return false;
|
||||
}
|
||||
// todo iterate the entire tree
|
||||
todo!();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
console::log_1(&format!("found {} pairs ", growing_paths.len()).into());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
201
src/space_colonization/space_colonization.rs.bak
Normal file
201
src/space_colonization/space_colonization.rs.bak
Normal file
@ -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<RwLock<Node>>,
|
||||
pub attractors: Vec<RwLock<Attractor>>,
|
||||
}
|
||||
|
||||
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<F>(&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<Node>) {
|
||||
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<Node>) -> Vec<&RwLock<Attractor>> {
|
||||
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<Node>,
|
||||
affecting_attractors: &Vec<&RwLock<Attractor>>,
|
||||
) -> 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user