From a5a093d7d69368c9661099b8c3e39e771403828d Mon Sep 17 00:00:00 2001 From: jeangab Date: Mon, 21 Aug 2023 09:13:43 -0400 Subject: [PATCH] feat(space_colonization): Spatial index plugged in, performance is much better --- src/components/background.rs | 67 +++--- src/space_colonization/mod.rs | 2 +- src/space_colonization/space_colonization.rs | 88 +++++--- .../space_colonization.rs.bak | 201 ------------------ .../{spacial_index.rs => spatial_index.rs} | 173 +++++++++++---- 5 files changed, 223 insertions(+), 308 deletions(-) delete mode 100644 src/space_colonization/space_colonization.rs.bak rename src/space_colonization/{spacial_index.rs => spatial_index.rs} (52%) diff --git a/src/components/background.rs b/src/components/background.rs index 3c2e41c..48c3135 100644 --- a/src/components/background.rs +++ b/src/components/background.rs @@ -46,26 +46,29 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { 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, - )))); + { + let mut nodesMut = nodes.borrow_mut(); + nodesMut.push(Node::new(Point::new(( + (window_width / 3) as i32, + (window_height / 3) as i32, + )))); + nodesMut.push(Node::new(Point::new(( + (window_width / 2) as i32, + (window_height / 3) as i32, + )))); + nodesMut.push(Node::new(Point::new(( + (window_width / 3) as i32, + (window_height / 2) as i32, + )))); + nodesMut.push(Node::new(Point::new(( + (window_width - 200) as i32, + (window_height / 3) as i32, + )))); + nodesMut.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 = {} {}", @@ -91,13 +94,16 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let end_time = window().unwrap().performance().unwrap().now(); log!( "Rendering nodes and {} attractors took {}", - sc.attractors.len(), + sc.attractors.size(), end_time - start_time ); let context = context.clone(); let closure = Closure::::new(move |_: web_sys::MouseEvent| { let start_time = window().unwrap().performance().unwrap().now(); - sc.grow(&mut nodes.borrow_mut()); + { + let mut nodesMut = nodes.borrow_mut(); + sc.grow(&mut nodesMut); + } // let render_id = window().unwrap().performance().unwrap().now(); // context.begin_path(); // sc.render_nodes(&nodes.borrow(), render_id, render_node_fn); @@ -116,7 +122,7 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let end_time = window().unwrap().performance().unwrap().now(); log!( "Rendering nodes and {} attractors took {}", - sc.attractors.len(), + sc.attractors.size(), end_time - start_time ); }); @@ -124,10 +130,17 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView { let window = window().unwrap(); window .set_interval_with_callback_and_timeout_and_arguments_0( - closure.as_ref().unchecked_ref(), - 10, - ) - .unwrap(); + closure.as_ref().unchecked_ref(), + 30, + ) + .unwrap(); + + // window + // .add_event_listener_with_callback( + // "click", + // closure.as_ref().unchecked_ref(), + // ) + // .unwrap(); closure.forget(); }); diff --git a/src/space_colonization/mod.rs b/src/space_colonization/mod.rs index 85b6528..4c6e079 100644 --- a/src/space_colonization/mod.rs +++ b/src/space_colonization/mod.rs @@ -5,7 +5,7 @@ pub use point::*; mod space_colonization; pub use space_colonization::*; mod math; -mod spacial_index; +mod spatial_index; #[wasm_bindgen] extern "C" { diff --git a/src/space_colonization/space_colonization.rs b/src/space_colonization/space_colonization.rs index 21fd581..43598f1 100644 --- a/src/space_colonization/space_colonization.rs +++ b/src/space_colonization/space_colonization.rs @@ -1,4 +1,5 @@ use super::math::calculate_new_node_position; +use super::spatial_index::SpatialIndex; use super::Attraction; use super::{Attractor, Node, Point}; use log::info; @@ -44,7 +45,7 @@ where /// /// If density is 10, then there will be an average distance of 10 between attractors density: i32, - pub attractors: Vec, + pub attractors: SpatialIndex, render_fn: F, } @@ -56,7 +57,8 @@ where where F: Fn(&Node, &Node), { - let attractors = Vec::new(); + let attraction_distance = 100; + let attractors = SpatialIndex::new(Point::new((width, height)), attraction_distance); let mut sc = SpaceColonization { max_point: Point { @@ -64,7 +66,7 @@ where y: height, }, kill_distance: 5.0, - attraction_distance: 100, + attraction_distance, segment_length: 5, density: 30, attractors, @@ -80,7 +82,7 @@ where pub fn new_for_tests( width: i32, height: i32, - attractors: Vec, + attractors: SpatialIndex, render_fn: F, ) -> SpaceColonization where @@ -115,9 +117,8 @@ where let mut y_pos = 0; while x_pos < self.max_point.x { while y_pos < self.max_point.y { - self.attractors.push(Attractor::new( - self.get_random_point(x_pos.into(), y_pos.into()), - )); + let point = self.get_random_point(x_pos.into(), y_pos.into()); + self.attractors.add(&point, Attractor::new(point)); y_pos += self.density; } x_pos += self.density; @@ -150,7 +151,6 @@ where } pub fn grow<'b>(&mut self, mut nodes: &'b mut Vec) { - info!("Growing nodes {:?}", nodes); self.grow_nodes(&mut nodes) } @@ -207,11 +207,6 @@ where // have learned quite a lot about how rust programs work and should be designed. unsafe { - /* - let segfault_boy: *const u8 = usize::MAX as *const u8; - let memory_content = *segfault_boy; - println!("memory content at address 0 {}", memory_content); - */ let new_point = calculate_new_node_position(&(**node), attractor, self.segment_length); if let Some(new_point) = new_point { @@ -262,10 +257,17 @@ where fn find_attractors_in_range(&mut self, n: &Node) -> Vec<(*mut Attractor, f64)> { let mut attractors_in_range = Vec::new(); - for a in self.attractors.iter_mut().filter(|a| !a.dead) { - let distance = n.position.distance(&a.position); + for a in self + .attractors + .get_surrounding_elements_with_filter(&n.position, |a| !a.dead) + .iter() + { + let distance; + unsafe { + distance = n.position.distance(&(**a).position); + } if distance < self.attraction_distance as f64 { - attractors_in_range.push((a as *mut Attractor, distance)); + attractors_in_range.push((*a, distance)); } } attractors_in_range @@ -308,13 +310,18 @@ mod test { fn grow_should_reach_single_attractor_and_die() { let mut nodes = Vec::new(); nodes.push(Node::new(Point::new((0, 0)))); - let mut attractors = Vec::new(); - attractors.push(Attractor::new(Point::new((10, 0)))); + let mut attractors = SpatialIndex::new(Point::new((100, 100)), 10); + let point = Point::new((10, 0)); + attractors.add(&point, Attractor::new(point)); 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()); + assert_eq!( + sc.attractors + .get_surrounding_elements_with_filter(&point, |_| true) + .len(), + 1 + ); println!("before grow"); dbg!(&nodes); @@ -322,7 +329,13 @@ mod test { println!("after grow 1"); dbg!(&nodes); - assert!(sc.attractors.iter().find(|a| a.dead == true).is_none()); + assert_eq!( + sc.attractors + .get_surrounding_elements_with_filter(&point, |a| a.dead == false) + .len(), + 1 + ); + sc.grow(&mut nodes); println!("after grow 2"); @@ -336,12 +349,15 @@ mod test { (Point::new((3, 0)), Point::new((6, 0))), ]), ); - assert_eq!( sc.attractors - .iter() - .filter(|a| a.dead == true) - .collect::>() + .get_surrounding_elements_with_filter(&point, |a| a.dead == false) + .len(), + 0 + ); + assert_eq!( + sc.attractors + .get_surrounding_elements_with_filter(&point, |a| a.dead == true) .len(), 1 ); @@ -351,16 +367,24 @@ mod test { fn grow_should_ignore_dead_attractors() { let mut nodes = Vec::new(); nodes.push(Node::new(Point::new((0, 0)))); - let mut attractors = Vec::new(); - attractors.push(Attractor { - position: Point::new((10, 0)), - dead: true, - }); + let mut attractors = SpatialIndex::new(Point::new((100, 100)), 10); + let point = Point::new((10, 0)); + attractors.add( + &point, + Attractor { + position: point, + dead: true, + }, + ); 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()); + assert_eq!( + sc.attractors + .get_surrounding_elements_with_filter(&Point::new((10, 0)), |a| a.dead == true) + .len(), + 1 + ); sc.grow(&mut nodes); diff --git a/src/space_colonization/space_colonization.rs.bak b/src/space_colonization/space_colonization.rs.bak deleted file mode 100644 index 8692413..0000000 --- a/src/space_colonization/space_colonization.rs.bak +++ /dev/null @@ -1,201 +0,0 @@ -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>, - pub attractors: Vec>, -} - -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(&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) { - 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) -> Vec<&RwLock> { - 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, - affecting_attractors: &Vec<&RwLock>, - ) -> 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; - } -} diff --git a/src/space_colonization/spacial_index.rs b/src/space_colonization/spatial_index.rs similarity index 52% rename from src/space_colonization/spacial_index.rs rename to src/space_colonization/spatial_index.rs index c28fd09..c4246fc 100644 --- a/src/space_colonization/spacial_index.rs +++ b/src/space_colonization/spatial_index.rs @@ -1,13 +1,14 @@ use super::Point; #[derive(Debug)] -struct SpatialIndex { +pub struct SpatialIndex { elements: Vec>, /// The number of cells between a cell and the cell right below it. /// /// For a table of 100x100 with cell_size = 10, number_cells_x is 11 (100/10+1) number_cells_x: i32, cell_size: i32, + size: u32, max_point: Point, } @@ -32,6 +33,7 @@ where elements, cell_size, number_cells_x, + size: 0, max_point, } } @@ -45,18 +47,45 @@ where .get_mut(element_index as usize) .unwrap() .push(element); + self.size += 1; } - pub fn get_surrounding_elements<'a>(&'a self, point: &Point) -> Vec<&'a T> { + pub fn size(&self) -> u32 { + self.size + } + + /// Fetches elements in the cell corresponding to point and all the adjacent cells + /// filtering returned elements with filter + /// + /// May panic or return wrong values if the point is outside this SpatialIndex max point + pub fn get_surrounding_elements_with_filter<'a, F>( + self: &'a mut Self, + point: &Point, + mut filter: F, + ) -> Vec<*mut T> + where + F: FnMut(&T) -> bool, + { let surrounding_indices: Vec = self.get_surrounding_indices(point); - let mut surrounding_elements = Vec::new(); - dbg!(&surrounding_indices); - for i in surrounding_indices { - surrounding_elements.extend(self.elements.get(i).unwrap().iter()); + let mut surrounding_elements: Vec<*mut T> = Vec::new(); + for i in surrounding_indices.iter() { + println!("elements len {}", self.elements.len()); + let elements_for_index = self + .elements + .get_mut(*i as usize) + .expect( + format!( + "point {:?} is in range for spatial_index with max_point {:?}, currently getting index {}, among surrounding indices {:?}", + point, self.max_point, i, surrounding_indices + ) + .as_str(), + ); + let elements_for_index = elements_for_index + .iter_mut() + .filter(|el| filter(el)) + .map(|el| el as *mut T); + surrounding_elements.extend(elements_for_index); } - dbg!(&self.elements[115]); - dbg!(&surrounding_elements); - dbg!(&self.elements.len()); surrounding_elements } @@ -69,6 +98,9 @@ where let mut indices = Vec::from([element_index]); let row_offset = self.number_cells_x as usize; + let last_cell_x_start = self.max_point.x - (self.max_point.x % self.cell_size); + let last_row_y_min = self.max_point.y - (self.max_point.y % self.cell_size); + // top row if point.y >= self.cell_size { // top left @@ -78,7 +110,7 @@ where // top middle indices.push(element_index - row_offset); // top right - if point.x < self.max_point.x { + if point.x < last_cell_x_start { indices.push(element_index - row_offset + 1); } } @@ -90,11 +122,11 @@ where // middle middle can be skipped, already added // middle right - if point.x < self.max_point.x { + if point.x < last_cell_x_start { indices.push(element_index + 1); } - if point.y < self.max_point.y { + if point.y < last_row_y_min { // bottom left if point.x >= self.cell_size { indices.push(element_index + row_offset - 1); @@ -103,7 +135,7 @@ where // bottom middle indices.push(element_index + row_offset); // bottom right - if point.x < self.max_point.x { + if point.x < last_cell_x_start { indices.push(element_index + row_offset + 1); } } @@ -116,12 +148,21 @@ where mod test { use super::*; + fn assert_vec_values(actual: Vec<*mut usize>, expected: Vec) { + let resolved_actual: Vec; + unsafe { + resolved_actual = actual.iter().map(|n| **n).collect(); + } + + assert_eq!(resolved_actual, expected); + } + #[test] fn when_no_element_surrounding_nodes_returns_empty_vec() { - let index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); + let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); assert_eq!( - index.get_surrounding_elements(&Point::new((0, 0))), - Vec::<&usize>::new() + index.get_surrounding_elements_with_filter(&Point::new((0, 0)), |_| true), + Vec::<*mut usize>::new() ); } @@ -129,9 +170,9 @@ mod test { fn added_point_is_surrounding_itself() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((50, 50)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((50, 50))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((50, 50)), |_| true), + vec![132], ); } @@ -152,7 +193,9 @@ mod test { index.add(&Point::new((50, 60)), 5060); index.add(&Point::new((60, 60)), 6060); assert_eq!( - index.get_surrounding_elements(&Point::new((50, 50))).sort(), + index + .get_surrounding_elements_with_filter(&Point::new((50, 50)), |_| true) + .sort(), vec![ &4040, &4545, &5040, &6040, &4050, &5050, &6050, &5050, &5151, &6060, &4060, &5060, &6060, @@ -165,13 +208,13 @@ mod test { fn point_on_top_edge_is_close_to_first_cell() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((50, 0)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((50, 9))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((50, 9)), |_| true), + vec![132], ); - assert_eq!( - index.get_surrounding_elements(&Point::new((50, 0))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((50, 0)), |_| true), + vec![132], ); } @@ -179,13 +222,13 @@ mod test { fn point_on_bottom_edge_is_close_to_bottom_cell() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((50, 100)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((50, 95))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((50, 95)), |_| true), + vec![132], ); - assert_eq!( - index.get_surrounding_elements(&Point::new((50, 100))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((50, 100)), |_| true), + vec![132], ); } @@ -193,26 +236,26 @@ mod test { fn point_on_right_edge_is_close_to_first_cell() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((100, 50)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((95, 50))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((95, 50)), |_| true), + vec![132], ); - assert_eq!( - index.get_surrounding_elements(&Point::new((100, 50))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((100, 50)), |_| true), + vec![132], ); } #[test] fn point_on_left_edge_is_close_to_first_cell() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((0, 50)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((9, 50))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((9, 50)), |_| true), + vec![132], ); - assert_eq!( - index.get_surrounding_elements(&Point::new((0, 50))), - vec![&132] + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((0, 50)), |_| true), + vec![132], ); } @@ -220,9 +263,45 @@ mod test { fn when_elements_too_far_surrounding_is_empty() { let mut index: SpatialIndex = SpatialIndex::new(Point::new((100, 100)), 10); index.add(&Point::new((0, 0)), 132); - assert_eq!( - index.get_surrounding_elements(&Point::new((99, 99))), - Vec::<&usize>::new() + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((99, 99)), |_| true), + Vec::::new(), + ); + } + + #[test] + fn works_when_max_point_is_not_multiple_of_cell_size() { + let cell_size = 100; + let mut index: SpatialIndex = SpatialIndex::new(Point::new((1920, 580)), cell_size); + for x_cell in 0..19 as usize { + for y_cell in 0..5 as usize { + index.add( + &Point::new((x_cell as i32 * cell_size, y_cell as i32 * cell_size)), + x_cell * 100 + y_cell, + ); + } + } + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((1833, 502)), |_| true), + vec![1905, 1904, 1805, 1804] + ); + } + + #[test] + fn works_when_max_point_is_not_multiple_of_cell_size() { + let cell_size = 100; + let mut index: SpatialIndex = SpatialIndex::new(Point::new((1920, 580)), cell_size); + for x_cell in 0..19 as usize { + for y_cell in 0..5 as usize { + index.add( + &Point::new((x_cell as i32 * cell_size, y_cell as i32 * cell_size)), + x_cell * 100 + y_cell, + ); + } + } + assert_vec_values( + index.get_surrounding_elements_with_filter(&Point::new((1833, 502)), |_| true), + vec![1905, 1904, 1805, 1804] ); } }