diff --git a/src/components/background.rs b/src/components/background.rs index 16dbac1..64923ed 100644 --- a/src/components/background.rs +++ b/src/components/background.rs @@ -25,19 +25,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let window_height = u32::try_from(height).unwrap(); canvas.set_width(window_width); canvas.set_height(window_height); - let mut sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap()); - let nodes = Rc::new(RefCell::new(Vec::new())); - nodes.borrow_mut().push(Node::new(Point::new(( - (window_width / 2) as i32, - (window_height / 2) as i32, - )))); - // TODO Resize on window resize - log!( - "TODO resize on window resize canvas parent size = {} {}", - canvas_parent.client_width(), - canvas_parent.client_height() - ); - log!("in canvas"); + let context = canvas .get_context("2d") .ok() @@ -46,17 +34,54 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { .unchecked_into::(); log!("context = {:#?}", context); context.set_stroke_style(&JsValue::from("white")); + let context_to_render = context.clone(); + // let render_node_fn : 'a Fn(&Node, &Node) = |n: &Node, child: &Node| { + let render_node_fn = move |n: &Node, child: &Node| { + context_to_render.move_to(n.position.x.into(), n.position.y.into()); + context_to_render.line_to(child.position.x.into(), child.position.y.into()); + }; + let mut sc = SpaceColonization::new(width.try_into().unwrap(), height.try_into().unwrap(), render_node_fn); + let nodes = Rc::new(RefCell::new(Vec::new())); + nodes.borrow_mut().push(Node::new(Point::new(( + (window_width / 3) as i32, + (window_height / 3) as i32, + )))); + nodes.borrow_mut().push(Node::new(Point::new(( + (window_width / 2) as i32, + (window_height / 3) as i32, + )))); + nodes.borrow_mut().push(Node::new(Point::new(( + (window_width / 3) as i32, + (window_height / 2) as i32, + )))); + nodes.borrow_mut().push(Node::new(Point::new(( + (window_width - 200) as i32, + (window_height / 3) as i32, + )))); + nodes.borrow_mut().push(Node::new(Point::new(( + (window_width - 100) as i32, + (window_height - 100) as i32, + )))); + // TODO Resize on window resize + log!( + "TODO resize on window resize canvas parent size = {} {}", + canvas_parent.client_width(), + canvas_parent.client_height() + ); + context.set_stroke_style(&JsValue::from("white")); context.set_fill_style(&JsValue::from("yellow")); log!("About to render nodes"); let start_time = window().unwrap().performance().unwrap().now(); context.begin_path(); + /* let render_node_fn = |n: &Node, child: &Node| { context.move_to(n.position.x.into(), n.position.y.into()); context.line_to(child.position.x.into(), child.position.y.into()); }; - let render_id = window().unwrap().performance().unwrap().now(); - sc.render_nodes(&nodes.borrow(), render_id, render_node_fn); + */ + // let render_id = window().unwrap().performance().unwrap().now(); + // sc.render_all_nodes(&nodes.borrow(), render_id, render_node_fn); context.stroke(); let end_time = window().unwrap().performance().unwrap().now(); @@ -68,15 +93,12 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let context = context.clone(); let closure = Closure::::new(move |_: web_sys::MouseEvent| { let start_time = window().unwrap().performance().unwrap().now(); - let render_node_fn = |n: &Node, child: &Node| { - context.move_to(n.position.x.into(), n.position.y.into()); - context.line_to(child.position.x.into(), child.position.y.into()); - }; sc.grow(&mut nodes.borrow_mut()); - let render_id = window().unwrap().performance().unwrap().now(); - context.begin_path(); - sc.render_nodes(&nodes.borrow(), render_id, render_node_fn); + // let render_id = window().unwrap().performance().unwrap().now(); + // context.begin_path(); + // sc.render_nodes(&nodes.borrow(), render_id, render_node_fn); + /* context.set_fill_style(&JsValue::from("magenta")); for a in sc.attractors.iter().filter(|a| a.dead) { context.fill_rect(a.position.x.into(), a.position.y.into(), 5.0, 5.0); @@ -85,6 +107,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { for a in sc.attractors.iter().filter(|a| !a.dead) { context.fill_rect(a.position.x.into(), a.position.y.into(), 5.0, 5.0); } + */ context.stroke(); let end_time = window().unwrap().performance().unwrap().now(); log!( @@ -96,7 +119,10 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let window = window().unwrap(); window - .add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()) + .set_interval_with_callback_and_timeout_and_arguments_0( + closure.as_ref().unchecked_ref(), + 10, + ) .unwrap(); closure.forget(); }); diff --git a/src/space_colonization/mod.rs b/src/space_colonization/mod.rs index b10e132..2d6669e 100644 --- a/src/space_colonization/mod.rs +++ b/src/space_colonization/mod.rs @@ -62,9 +62,9 @@ impl std::hash::Hash for Node { } impl Node { - pub fn render(&self, render_id: f64, render_fn: F) + pub fn render(&self, render_id: f64, render_fn: G) where - F: Copy + Fn(&Node, &Node), + G: Copy + Fn(&Node, &Node), { for child in self.children.iter() { render_fn(self, &child); diff --git a/src/space_colonization/space_colonization.rs b/src/space_colonization/space_colonization.rs index 6db52fe..dcb6faf 100644 --- a/src/space_colonization/space_colonization.rs +++ b/src/space_colonization/space_colonization.rs @@ -6,7 +6,10 @@ use rand::thread_rng; use rand::Rng; use std::collections::HashMap; -pub struct SpaceColonization { +pub struct SpaceColonization +where + F: Fn(&Node, &Node), +{ max_point: Point, /// When a node grows within kill_distance of an attractor, the attractor is killed kill_distance: f64, @@ -42,10 +45,17 @@ pub struct SpaceColonization { /// If density is 10, then there will be an average distance of 10 between attractors density: i32, pub attractors: Vec, + render_fn: F, } -impl SpaceColonization { - pub fn new(width: i32, height: i32) -> SpaceColonization { +impl SpaceColonization +where + F: Fn(&Node, &Node), +{ + pub fn new(width: i32, height: i32, render_fn: F) -> SpaceColonization + where + F: Fn(&Node, &Node), + { let attractors = Vec::new(); let mut sc = SpaceColonization { @@ -58,6 +68,7 @@ impl SpaceColonization { segment_length: 5, density: 30, attractors, + render_fn, }; sc.place_attractors(); @@ -66,7 +77,15 @@ impl SpaceColonization { } #[cfg(test)] - pub fn new_for_tests(width: i32, height: i32, attractors: Vec) -> SpaceColonization { + pub fn new_for_tests( + width: i32, + height: i32, + attractors: Vec, + render_fn: F, + ) -> SpaceColonization + where + F: Fn(&Node, &Node), + { SpaceColonization { max_point: Point { x: width, @@ -77,12 +96,13 @@ impl SpaceColonization { segment_length: 3, density: 3, attractors, + render_fn, } } - pub fn render_nodes(&self, nodes: &Vec, render_id: f64, render_fn: F) + pub fn render_all_nodes(&self, nodes: &Vec, render_id: f64, render_fn: G) where - F: Copy + Fn(&Node, &Node), + G: Copy + Fn(&Node, &Node), { info!("Rendering {} nodes", nodes.len()); for n in nodes.iter() { @@ -129,16 +149,7 @@ impl SpaceColonization { } } - pub fn grow<'a>(&mut self, mut nodes: &'a mut Vec) { - // TODO - // [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 + pub fn grow<'b>(&mut self, mut nodes: &'b mut Vec) { info!("Growing nodes {:?}", nodes); self.grow_nodes(&mut nodes) } @@ -211,14 +222,15 @@ impl SpaceColonization { (**a).dead = true; } }); + (self.render_fn)(&**node, &new_node); (**node).children.push(new_node); } } }); } - fn build_attractor_to_closest_node<'a>( - &'a mut self, + fn build_attractor_to_closest_node<'b>( + &'b mut self, mut attractor_to_closest_node: &mut HashMap<*mut Attractor, Attraction>, n: &mut Node, ) { @@ -246,7 +258,6 @@ impl SpaceColonization { attractor_to_closest_node.insert(a.0, Attraction::new(n, a.1)); } } - } fn find_attractors_in_range(&mut self, n: &Node) -> Vec<(*mut Attractor, f64)> { @@ -267,13 +278,15 @@ mod test { use super::*; - fn assert_vertices( - sc: &SpaceColonization, - nodes: &Vec, + fn assert_vertices<'a, F>( + sc: &SpaceColonization, + nodes: &'a Vec, mut expected_nodes: Vec<(Point, Point)>, - ) { + ) where + F: Copy + Fn(&Node, &Node), + { let rendered_nodes = RefCell::new(Vec::new()); - sc.render_nodes(&nodes, 0.0, |n1, n2| { + sc.render_all_nodes(&nodes, 0.0, |n1: &Node, n2: &Node| { rendered_nodes.borrow_mut().push((n1.position, n2.position)); }); let sort_points = |line1: &(Point, Point), line2: &(Point, Point)| { @@ -298,7 +311,7 @@ mod test { let mut attractors = Vec::new(); attractors.push(Attractor::new(Point::new((10, 0)))); - let mut sc = SpaceColonization::new_for_tests(100, 100, attractors); + let mut sc = SpaceColonization::new_for_tests(100, 100, attractors, |_, _|{}); assert_eq!(sc.attractors.len(), 1); assert!(sc.attractors.iter().find(|a| a.dead == true).is_none()); @@ -344,7 +357,7 @@ mod test { dead: true, }); - let mut sc = SpaceColonization::new_for_tests(100, 100, attractors); + let mut sc = SpaceColonization::new_for_tests(100, 100, attractors, |_, _|{}); assert_eq!(sc.attractors.len(), 1); assert!(sc.attractors.iter().find(|a| a.dead == true).is_some());