wip: Refactor space colonization, separate nodes vec from space colonization struct allows for simpler ownership management
This commit is contained in:
parent
cb9df9e79a
commit
66e1f813cf
@ -5,6 +5,7 @@ use wasm_bindgen::JsValue;
|
|||||||
use web_sys::window;
|
use web_sys::window;
|
||||||
|
|
||||||
use crate::space_colonization::Node;
|
use crate::space_colonization::Node;
|
||||||
|
use crate::space_colonization::Point;
|
||||||
use crate::space_colonization::SpaceColonization;
|
use crate::space_colonization::SpaceColonization;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -19,6 +20,8 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
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 mut sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap());
|
let mut sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap());
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
nodes.push(Node::new(Point::new((100, 100))));
|
||||||
// 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 = {} {}",
|
||||||
@ -44,7 +47,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
context.line_to(child.position.x.into(), child.position.y.into());
|
context.line_to(child.position.x.into(), child.position.y.into());
|
||||||
};
|
};
|
||||||
let render_id = window().unwrap().performance().unwrap().now();
|
let render_id = window().unwrap().performance().unwrap().now();
|
||||||
sc.render_nodes(render_id, render_node_fn);
|
sc.render_nodes(&nodes, render_id, render_node_fn);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.set_fill_style(&JsValue::from("magenta"));
|
context.set_fill_style(&JsValue::from("magenta"));
|
||||||
@ -59,10 +62,10 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for _i in 1..150 {
|
for _i in 1..150 {
|
||||||
sc.grow();
|
nodes = sc.grow(nodes);
|
||||||
let render_id = window().unwrap().performance().unwrap().now();
|
let render_id = window().unwrap().performance().unwrap().now();
|
||||||
context.begin_path();
|
context.begin_path();
|
||||||
sc.render_nodes(render_id, render_node_fn);
|
sc.render_nodes(&nodes, render_id, render_node_fn);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn new_node_moves_toward_single_attractor() {
|
fn new_node_moves_toward_single_attractor() {
|
||||||
let growth_cell = GrowthCell::from_positions([(0, 0), (0, 10)].to_vec());
|
let growth_cell = GrowthCell::from_positions([(0, 0), (0, 10)].to_vec());
|
||||||
let point = calculate_new_node_position(&growth_cell.as_refs(), SEGMENT_LENGTH);
|
|
||||||
|
let point = calculate_new_node_position(&(growth_cell.node, growth_cell.attractors.iter().collect()), SEGMENT_LENGTH);
|
||||||
assert_eq!(point, Point::new((0, 5)));
|
assert_eq!(point, Point::new((0, 5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ mod tests {
|
|||||||
let node = Node {
|
let node = Node {
|
||||||
position: Point::new(positions[0]),
|
position: Point::new(positions[0]),
|
||||||
children: Vec::new().into(),
|
children: Vec::new().into(),
|
||||||
|
growing: true,
|
||||||
};
|
};
|
||||||
let mut attractors = Vec::new();
|
let mut attractors = Vec::new();
|
||||||
for p in positions.iter().skip(1) {
|
for p in positions.iter().skip(1) {
|
||||||
@ -58,9 +60,5 @@ mod tests {
|
|||||||
}
|
}
|
||||||
Self { node, attractors }
|
Self { node, attractors }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_refs(&self) -> (Node, Vec<&Attractor>) {
|
|
||||||
(self.node, self.attractors.iter().collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,17 +66,16 @@ impl Node {
|
|||||||
where
|
where
|
||||||
F: Copy + Fn(&Node, &Node),
|
F: Copy + Fn(&Node, &Node),
|
||||||
{
|
{
|
||||||
let children = self.children;
|
for child in self.children.iter() {
|
||||||
for child in children.iter() {
|
|
||||||
render_fn(self, &child);
|
render_fn(self, &child);
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in children.iter() {
|
for child in self.children.iter() {
|
||||||
child.render(render_id, render_fn);
|
child.render(render_id, render_fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(position: Point) -> Self {
|
pub fn new(position: Point) -> Self {
|
||||||
Self {
|
Self {
|
||||||
position,
|
position,
|
||||||
growing: true,
|
growing: true,
|
||||||
|
@ -39,29 +39,11 @@ 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,
|
||||||
/// 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>,
|
pub attractors: Vec<Attractor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
nodes_vec.push(Node::new(Point::new((100, 100))));
|
|
||||||
let attractors = Vec::new();
|
let attractors = Vec::new();
|
||||||
|
|
||||||
let mut sc = SpaceColonization {
|
let mut sc = SpaceColonization {
|
||||||
@ -73,7 +55,6 @@ impl SpaceColonization {
|
|||||||
attraction_distance: 100,
|
attraction_distance: 100,
|
||||||
segment_length: 5,
|
segment_length: 5,
|
||||||
density: 30,
|
density: 30,
|
||||||
nodes: nodes_vec,
|
|
||||||
attractors,
|
attractors,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,7 +67,6 @@ impl SpaceColonization {
|
|||||||
pub fn new_for_tests(
|
pub fn new_for_tests(
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
nodes: Vec<Node>,
|
|
||||||
attractors: Vec<Attractor>,
|
attractors: Vec<Attractor>,
|
||||||
) -> SpaceColonization {
|
) -> SpaceColonization {
|
||||||
SpaceColonization {
|
SpaceColonization {
|
||||||
@ -98,17 +78,16 @@ impl SpaceColonization {
|
|||||||
attraction_distance: 12,
|
attraction_distance: 12,
|
||||||
segment_length: 3,
|
segment_length: 3,
|
||||||
density: 3,
|
density: 3,
|
||||||
nodes,
|
|
||||||
attractors,
|
attractors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_nodes<F>(&self, render_id: f64, render_fn: F)
|
pub fn render_nodes<F>(&self, nodes: &Vec<Node>, render_id: f64, render_fn: F)
|
||||||
where
|
where
|
||||||
F: Copy + Fn(&Node, &Node),
|
F: Copy + Fn(&Node, &Node),
|
||||||
{
|
{
|
||||||
info!("Rendering {} nodes", self.nodes.len());
|
info!("Rendering {} nodes", nodes.len());
|
||||||
for n in self.nodes.iter() {
|
for n in nodes.iter() {
|
||||||
n.render(render_id, render_fn);
|
n.render(render_id, render_fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +132,7 @@ impl SpaceColonization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow(&mut self) {
|
pub fn grow(&mut self, nodes: Vec<Node>) -> Vec<Node> {
|
||||||
// TODO
|
// TODO
|
||||||
// [x] 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
|
||||||
@ -163,62 +142,43 @@ impl SpaceColonization {
|
|||||||
// - 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(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow_nodes(&mut self) {
|
pub fn grow_nodes(&mut self, nodes: Vec<Node>) -> Vec<Node>{
|
||||||
// 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 mut growing_paths: HashMap<*mut Node, Vec<&Attractor>> = HashMap::new();
|
let mut influence_map: HashMap<&Attractor, (*mut Node, f64)> = 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 = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_closest_node_for_attractors(&self) -> HashMap<&Attractor, (&Node, f64)> {
|
let nodes = nodes.into_iter().map(|mut n| {
|
||||||
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 {
|
if !n.growing {
|
||||||
continue;
|
return n;
|
||||||
}
|
|
||||||
// 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 {
|
let attractors_in_range = self.find_attractors_in_range(&n);
|
||||||
if let Some(closest) = map.get(a.0) {
|
|
||||||
|
if attractors_in_range.is_empty() {
|
||||||
|
n.growing = false;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
for a in attractors_in_range {
|
||||||
|
if let Some(closest) = influence_map.get(a.0) {
|
||||||
if a.1 < closest.1 {
|
if a.1 < closest.1 {
|
||||||
map.insert(a.0, (n, a.1));
|
influence_map.insert(a.0, (&mut n, a.1));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
map.insert(a.0, (n, a.1));
|
influence_map.insert(a.0, (&mut n, a.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
n
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_attractors_in_range(&self, n: &Node) -> Vec<(&Attractor, f64)> {
|
fn find_attractors_in_range(&self, n: &Node) -> Vec<(&Attractor, f64)> {
|
||||||
@ -233,16 +193,15 @@ impl SpaceColonization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn assert_nodes(sc: &SpaceColonization, expected_nodes: Vec<(Point, Point)>) {
|
fn assert_nodes(sc: &SpaceColonization, nodes: &Vec<Node>, expected_nodes: Vec<(Point, Point)>) {
|
||||||
let rendered_nodes = RefCell::new(Vec::new());
|
let rendered_nodes = RefCell::new(Vec::new());
|
||||||
sc.render_nodes(0.0, |n1, n2| {
|
sc.render_nodes(&nodes, 0.0, |n1, n2| {
|
||||||
rendered_nodes.borrow_mut().push((n1.position, n2.position));
|
rendered_nodes.borrow_mut().push((n1.position, n2.position));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -252,7 +211,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return line1.1.cmp(&line2.1);
|
return line1.1.cmp(&line2.1);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -262,34 +221,29 @@ mod test {
|
|||||||
let mut attractors = Vec::new();
|
let mut attractors = Vec::new();
|
||||||
attractors.push(Attractor::new((10, 0)));
|
attractors.push(Attractor::new((10, 0)));
|
||||||
|
|
||||||
let sc = SpaceColonization::new_for_tests(100, 100, nodes, attractors);
|
let mut sc = SpaceColonization::new_for_tests(100, 100, attractors);
|
||||||
|
|
||||||
assert_nodes(&sc, Vec::from([(Point::new((0,0)), Point::new((10,0)))]));
|
assert_nodes(&sc, &nodes, Vec::from([(Point::new((0, 0)), Point::new((10, 0)))]));
|
||||||
assert_eq!(sc.attractors.len(), 1);
|
assert_eq!(sc.attractors.len(), 1);
|
||||||
assert!(sc
|
assert!(sc.attractors.iter().find(|a| a.dead == true).is_none());
|
||||||
.attractors
|
|
||||||
.iter()
|
|
||||||
.find(|a| a.dead == true)
|
|
||||||
.is_none());
|
|
||||||
|
|
||||||
sc.grow();
|
nodes = sc.grow(nodes);
|
||||||
|
|
||||||
assert!(sc
|
assert!(sc.attractors.iter().find(|a| a.dead == true).is_none());
|
||||||
.attractors
|
|
||||||
.iter()
|
|
||||||
.find(|a| a.dead == true)
|
|
||||||
.is_none());
|
|
||||||
// TODO assert point 3,0
|
// TODO assert point 3,0
|
||||||
|
|
||||||
sc.grow();
|
nodes = sc.grow(nodes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
sc.attractors
|
||||||
|
.iter()
|
||||||
|
.filter(|a| a.dead == true)
|
||||||
|
.collect::<Vec<&Attractor>>()
|
||||||
|
.len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(sc
|
|
||||||
.attractors
|
|
||||||
.iter()
|
|
||||||
.filter(|a| a.dead == true)
|
|
||||||
.collect::<Vec<&Attractor>>().len(), 1);
|
|
||||||
|
|
||||||
// TODO assert nodes 3,0 and 6,0
|
// TODO assert nodes 3,0 and 6,0
|
||||||
assert_eq!(sc.nodes.len(), 3);
|
assert_eq!(nodes.len(), 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user