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();
|
||||
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,10 +38,6 @@ 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.nodes_tree.borrow().iter() {
|
||||
context.fill_rect(n.position.x.into(), n.position.y.into(), 5.0, 5.0);
|
||||
}
|
||||
|
||||
context.begin_path();
|
||||
let render_node_fn = |n: &Node, child: &Node| {
|
||||
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.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);
|
||||
}
|
||||
let end_time = window().unwrap().performance().unwrap().now();
|
||||
log!(
|
||||
"Rendering {} nodes and {} attractors took {}",
|
||||
sc.nodes_tree.borrow().len(),
|
||||
sc.attractors.borrow().len(),
|
||||
"Rendering nodes and {} attractors took {}",
|
||||
sc.attractors.len(),
|
||||
end_time - start_time
|
||||
);
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ extern "C" {
|
||||
fn performance() -> web_sys::Performance;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Attractor {
|
||||
pub position: Point,
|
||||
pub dead: bool,
|
||||
@ -38,6 +38,9 @@ impl Attractor {
|
||||
/// - Probably worth marking nodes as dead when no attractor is in range
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
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 children: Vec<Node>,
|
||||
}
|
||||
@ -76,6 +79,7 @@ impl Node {
|
||||
fn new(position: Point) -> Self {
|
||||
Self {
|
||||
position,
|
||||
growing: true,
|
||||
children: Vec::new().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use super::math::calculate_new_node_position;
|
||||
use super::{Attractor, Node, NodeRef, Point};
|
||||
use super::{Attractor, Node, Point};
|
||||
use log::info;
|
||||
use rand::thread_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
|
||||
density: i32,
|
||||
new_nodes: Vec<(NodeRef, Node)>,
|
||||
/// Flat list of all nodes in the tree
|
||||
/// [node, child1, grand-child, child2, grand-child2]
|
||||
/// Tree of all nodes in the space. There can be multiple root nodes.
|
||||
/// ```yaml
|
||||
/// [
|
||||
/// node: {
|
||||
/// position: (x,y),
|
||||
/// children : [
|
||||
/// child1 : {
|
||||
/// position, children: [ grand-child1, grand-child2 ]
|
||||
/// },
|
||||
/// child2 : {...}
|
||||
/// ],
|
||||
/// },
|
||||
/// node2: { ...another tree },
|
||||
/// ]
|
||||
/// ```
|
||||
nodes: Vec<Node>,
|
||||
pub attractors: Vec<Attractor>,
|
||||
}
|
||||
@ -50,10 +61,7 @@ pub struct SpaceColonization {
|
||||
impl SpaceColonization {
|
||||
pub fn new(width: i32, height: i32) -> SpaceColonization {
|
||||
let mut nodes_vec = Vec::new();
|
||||
nodes_vec.push(Node {
|
||||
position: Point { x: 100, y: 100 },
|
||||
children: Vec::new(),
|
||||
});
|
||||
nodes_vec.push(Node::new(Point::new((100, 100))));
|
||||
let attractors = Vec::new();
|
||||
|
||||
let mut sc = SpaceColonization {
|
||||
@ -67,7 +75,6 @@ impl SpaceColonization {
|
||||
density: 30,
|
||||
nodes: nodes_vec,
|
||||
attractors,
|
||||
new_nodes: Vec::new(),
|
||||
};
|
||||
|
||||
sc.place_attractors();
|
||||
@ -93,7 +100,6 @@ impl SpaceColonization {
|
||||
density: 3,
|
||||
nodes,
|
||||
attractors,
|
||||
new_nodes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,78 +153,87 @@ impl SpaceColonization {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow(&self) {
|
||||
pub fn grow(&mut self) {
|
||||
// TODO
|
||||
// Find a clean API that will be stable across refactoring
|
||||
// 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 clean API that will be stable across refactoring
|
||||
// [ ] Write the test against this api including performance
|
||||
// [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 nodes and their state
|
||||
// - I can efficiently render my nodes on a canvas
|
||||
// - I use as little memory as possible
|
||||
// - I can update my 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
|
||||
// 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 attractors = self.attractors;
|
||||
let mut growing_paths: HashMap<<NodeRef>, Vec<AttractorRef>> = HashMap::new();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut growing_paths: HashMap<*mut Node, Vec<&Attractor>> = HashMap::new();
|
||||
let mut influence_map = self.get_closest_node_for_attractors();
|
||||
/*
|
||||
for growth_cell in growing_paths {
|
||||
let position = calculate_new_node_position(&growth_cell, self.segment_length);
|
||||
for a in growth_cell.1 {
|
||||
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)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
@ -259,7 +274,6 @@ mod test {
|
||||
|
||||
sc.grow();
|
||||
|
||||
assert_eq!(sc.new_nodes.len(), 0);
|
||||
assert!(sc
|
||||
.attractors
|
||||
.iter()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user