wip: refactor space colonization to use mutables instead of refCell
This commit is contained in:
parent
0b892980ca
commit
b7401bef1b
@ -18,7 +18,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
let height = canvas_parent.client_height();
|
let height = canvas_parent.client_height();
|
||||||
canvas.set_width(u32::try_from(width).unwrap());
|
canvas.set_width(u32::try_from(width).unwrap());
|
||||||
canvas.set_height(u32::try_from(height).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
|
// TODO Resize on window resize
|
||||||
log!(
|
log!(
|
||||||
"TODO resize on window resize canvas parent size = {} {}",
|
"TODO resize on window resize canvas parent size = {} {}",
|
||||||
@ -38,10 +38,6 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
context.set_fill_style(&JsValue::from("yellow"));
|
context.set_fill_style(&JsValue::from("yellow"));
|
||||||
log!("About to render nodes");
|
log!("About to render nodes");
|
||||||
let start_time = window().unwrap().performance().unwrap().now();
|
let start_time = window().unwrap().performance().unwrap().now();
|
||||||
for n in sc.nodes_tree.borrow().iter() {
|
|
||||||
context.fill_rect(n.position.x.into(), n.position.y.into(), 5.0, 5.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.begin_path();
|
context.begin_path();
|
||||||
let render_node_fn = |n: &Node, child: &Node| {
|
let render_node_fn = |n: &Node, child: &Node| {
|
||||||
context.move_to(n.position.x.into(), n.position.y.into());
|
context.move_to(n.position.x.into(), n.position.y.into());
|
||||||
@ -52,14 +48,13 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.set_fill_style(&JsValue::from("magenta"));
|
context.set_fill_style(&JsValue::from("magenta"));
|
||||||
for a in sc.attractors.borrow().iter() {
|
for a in sc.attractors.iter() {
|
||||||
context.fill_rect(a.position.x.into(), a.position.y.into(), 5.0, 5.0);
|
context.fill_rect(a.position.x.into(), a.position.y.into(), 5.0, 5.0);
|
||||||
}
|
}
|
||||||
let end_time = window().unwrap().performance().unwrap().now();
|
let end_time = window().unwrap().performance().unwrap().now();
|
||||||
log!(
|
log!(
|
||||||
"Rendering {} nodes and {} attractors took {}",
|
"Rendering nodes and {} attractors took {}",
|
||||||
sc.nodes_tree.borrow().len(),
|
sc.attractors.len(),
|
||||||
sc.attractors.borrow().len(),
|
|
||||||
end_time - start_time
|
end_time - start_time
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ extern "C" {
|
|||||||
fn performance() -> web_sys::Performance;
|
fn performance() -> web_sys::Performance;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Attractor {
|
pub struct Attractor {
|
||||||
pub position: Point,
|
pub position: Point,
|
||||||
pub dead: bool,
|
pub dead: bool,
|
||||||
@ -38,6 +38,9 @@ impl Attractor {
|
|||||||
/// - Probably worth marking nodes as dead when no attractor is in range
|
/// - Probably worth marking nodes as dead when no attractor is in range
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
|
/// When a Node is born it is growing
|
||||||
|
/// it stops growing when there is no attractor in range
|
||||||
|
pub growing: bool,
|
||||||
pub position: Point,
|
pub position: Point,
|
||||||
pub children: Vec<Node>,
|
pub children: Vec<Node>,
|
||||||
}
|
}
|
||||||
@ -76,6 +79,7 @@ impl Node {
|
|||||||
fn new(position: Point) -> Self {
|
fn new(position: Point) -> Self {
|
||||||
Self {
|
Self {
|
||||||
position,
|
position,
|
||||||
|
growing: true,
|
||||||
children: Vec::new().into(),
|
children: Vec::new().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use super::math::calculate_new_node_position;
|
use super::{Attractor, Node, Point};
|
||||||
use super::{Attractor, Node, NodeRef, Point};
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -40,9 +39,21 @@ pub struct SpaceColonization {
|
|||||||
///
|
///
|
||||||
/// If density is 10, then there will be an average distance of 10 between attractors
|
/// If density is 10, then there will be an average distance of 10 between attractors
|
||||||
density: i32,
|
density: i32,
|
||||||
new_nodes: Vec<(NodeRef, Node)>,
|
/// Tree of all nodes in the space. There can be multiple root nodes.
|
||||||
/// Flat list of all nodes in the tree
|
/// ```yaml
|
||||||
/// [node, child1, grand-child, child2, grand-child2]
|
/// [
|
||||||
|
/// node: {
|
||||||
|
/// position: (x,y),
|
||||||
|
/// children : [
|
||||||
|
/// child1 : {
|
||||||
|
/// position, children: [ grand-child1, grand-child2 ]
|
||||||
|
/// },
|
||||||
|
/// child2 : {...}
|
||||||
|
/// ],
|
||||||
|
/// },
|
||||||
|
/// node2: { ...another tree },
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
nodes: Vec<Node>,
|
nodes: Vec<Node>,
|
||||||
pub attractors: Vec<Attractor>,
|
pub attractors: Vec<Attractor>,
|
||||||
}
|
}
|
||||||
@ -50,10 +61,7 @@ pub struct SpaceColonization {
|
|||||||
impl SpaceColonization {
|
impl SpaceColonization {
|
||||||
pub fn new(width: i32, height: i32) -> SpaceColonization {
|
pub fn new(width: i32, height: i32) -> SpaceColonization {
|
||||||
let mut nodes_vec = Vec::new();
|
let mut nodes_vec = Vec::new();
|
||||||
nodes_vec.push(Node {
|
nodes_vec.push(Node::new(Point::new((100, 100))));
|
||||||
position: Point { x: 100, y: 100 },
|
|
||||||
children: Vec::new(),
|
|
||||||
});
|
|
||||||
let attractors = Vec::new();
|
let attractors = Vec::new();
|
||||||
|
|
||||||
let mut sc = SpaceColonization {
|
let mut sc = SpaceColonization {
|
||||||
@ -67,7 +75,6 @@ impl SpaceColonization {
|
|||||||
density: 30,
|
density: 30,
|
||||||
nodes: nodes_vec,
|
nodes: nodes_vec,
|
||||||
attractors,
|
attractors,
|
||||||
new_nodes: Vec::new(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sc.place_attractors();
|
sc.place_attractors();
|
||||||
@ -93,7 +100,6 @@ impl SpaceColonization {
|
|||||||
density: 3,
|
density: 3,
|
||||||
nodes,
|
nodes,
|
||||||
attractors,
|
attractors,
|
||||||
new_nodes: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,78 +153,87 @@ impl SpaceColonization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow(&self) {
|
pub fn grow(&mut self) {
|
||||||
// TODO
|
// TODO
|
||||||
// Find a clean API that will be stable across refactoring
|
// [x] Find a clean API that will be stable across refactoring
|
||||||
// Write the test against this api including performance
|
// [ ] Write the test against this api including performance
|
||||||
// Find a way to make a compile-time safe datastructure that will ensure that
|
// [x] Find a way to make a compile-time safe datastructure that will ensure that
|
||||||
// - I can store my attractors and their state (remove them or update them when dead)
|
// - I can store my attractors and their state (remove them or update them when dead)
|
||||||
// - I can store my nodes and their state
|
// - I can store my nodes and their state
|
||||||
// - I can efficiently render my nodes on a canvas
|
// - I can efficiently render my nodes on a canvas
|
||||||
// - I use as little memory as possible
|
// - I use as little memory as possible
|
||||||
// - I can update my nodes
|
// - I can update my nodes
|
||||||
self.grow_nodes();
|
self.grow_nodes();
|
||||||
println!("new nodes for iteration {:?}", self.new_nodes);
|
|
||||||
let mut nodes_mut = self.nodes;
|
|
||||||
for new_pair in self.new_nodes.iter() {
|
|
||||||
new_pair.0.children.push(new_pair.1);
|
|
||||||
nodes_mut.push(new_pair.1);
|
|
||||||
}
|
|
||||||
self.new_nodes.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow_nodes(&self) {
|
pub fn grow_nodes(&mut self) {
|
||||||
// iterate through attractors
|
// iterate through attractors
|
||||||
// find closest node within attraction range
|
// find closest node within attraction range
|
||||||
// build a map of nodes to affecting attractors
|
// build a map of nodes to affecting attractors
|
||||||
// attractors within the attraction range that this node is the closest to
|
// attractors within the attraction range that this node is the closest to
|
||||||
//
|
//
|
||||||
// calculate new node position
|
// calculate new node position
|
||||||
let attractors = self.attractors;
|
let mut growing_paths: HashMap<*mut Node, Vec<&Attractor>> = HashMap::new();
|
||||||
let mut growing_paths: HashMap<<NodeRef>, Vec<AttractorRef>> = HashMap::new();
|
let mut influence_map = self.get_closest_node_for_attractors();
|
||||||
for a in attractors.iter() {
|
/*
|
||||||
if a.dead {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut closest_node: Option<Node> = None;
|
|
||||||
let mut closest_node_distance = f64::MAX;
|
|
||||||
|
|
||||||
for n in self.nodes.iter() {
|
|
||||||
let distance = n.position.distance(&a.position);
|
|
||||||
if distance <= self.attraction_distance as f64 {
|
|
||||||
// TODO make sure it is closest node amongs all nodes
|
|
||||||
if distance < closest_node_distance {
|
|
||||||
closest_node = Some(n);
|
|
||||||
closest_node_distance = distance;
|
|
||||||
if distance < self.kill_distance as f64 {
|
|
||||||
a.dead.replace(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(node) = closest_node {
|
|
||||||
if let Some(attractors) = growing_paths.get_mut(&node) {
|
|
||||||
attractors.push(a);
|
|
||||||
} else {
|
|
||||||
let mut attractors = Vec::new();
|
|
||||||
attractors.push(a);
|
|
||||||
growing_paths.insert(node, attractors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for growth_cell in growing_paths {
|
for growth_cell in growing_paths {
|
||||||
let position = calculate_new_node_position(&growth_cell, self.segment_length);
|
let position = calculate_new_node_position(&growth_cell, self.segment_length);
|
||||||
for a in growth_cell.1 {
|
for a in growth_cell.1 {
|
||||||
if position.distance(&a.position) < self.kill_distance as f64 {
|
if position.distance(&a.position) < self.kill_distance as f64 {
|
||||||
a.dead.replace(true);
|
a.dead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_closest_node_for_attractors(&self) -> HashMap<&Attractor, (&Node, f64)> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
self.build_attractor_map(&self.nodes, map);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use a struct instead of (&Node, f64) tuple
|
||||||
|
fn build_attractor_map<'a>(self, nodes: &'a Vec<Node>, mut map: HashMap<&'a Attractor, (&'a Node, f64)>) {
|
||||||
|
for n in nodes.iter() {
|
||||||
|
if !n.growing {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// TODO figure out that borrowing mess
|
||||||
|
let attractors: Vec<(&Attractor, f64)> = self.find_attractors_in_range(n);
|
||||||
|
if attractors.is_empty() {
|
||||||
|
n.growing = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for a in attractors {
|
||||||
|
if let Some(closest) = map.get(a.0) {
|
||||||
|
if a.1 < closest.1 {
|
||||||
|
map.insert(a.0, (n, a.1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map.insert(a.0, (n, a.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.new_nodes
|
|
||||||
.push((growth_cell.0, Node::new(position)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_attractors_in_range(&self, n: &Node) -> Vec<(&Attractor, f64)> {
|
||||||
|
let mut attractors_in_range = Vec::new();
|
||||||
|
for a in self.attractors.iter() {
|
||||||
|
let distance = n.position.distance(&a.position);
|
||||||
|
if distance < self.attraction_distance as f64 {
|
||||||
|
attractors_in_range.push((a, distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attractors_in_range
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -259,7 +274,6 @@ mod test {
|
|||||||
|
|
||||||
sc.grow();
|
sc.grow();
|
||||||
|
|
||||||
assert_eq!(sc.new_nodes.len(), 0);
|
|
||||||
assert!(sc
|
assert!(sc
|
||||||
.attractors
|
.attractors
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user