feat(space colonization): now pretty much works! still slowish but not too bad, about 10fps at worst on a good laptop with a ryzen 6800u
This commit is contained in:
parent
62208591fc
commit
97cf5b21d9
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -735,6 +735,19 @@ dependencies = [
|
|||||||
"syn 2.0.28",
|
"syn 2.0.28",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||||
|
dependencies = [
|
||||||
|
"humantime",
|
||||||
|
"is-terminal",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -1069,6 +1082,12 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.27"
|
version = "0.14.27"
|
||||||
@ -1427,6 +1446,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
|
"env_logger",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"leptos",
|
"leptos",
|
||||||
@ -1436,6 +1456,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
|
"test-log",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
@ -2349,6 +2370,26 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test-log"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9601d162c1d77e62c1ea0bc8116cd1caf143ce3af947536c3c9052a1677fe0c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.44"
|
version = "1.0.44"
|
||||||
|
@ -96,3 +96,7 @@ lib-features = ["hydrate"]
|
|||||||
#
|
#
|
||||||
# Optional. Defaults to false.
|
# Optional. Defaults to false.
|
||||||
lib-default-features = false
|
lib-default-features = false
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
test-log = "*"
|
||||||
|
env_logger = "*"
|
||||||
|
@ -59,10 +59,6 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
sc.render_nodes(&nodes.borrow(), render_id, render_node_fn);
|
sc.render_nodes(&nodes.borrow(), render_id, render_node_fn);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.set_fill_style(&JsValue::from("magenta"));
|
|
||||||
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();
|
let end_time = window().unwrap().performance().unwrap().now();
|
||||||
log!(
|
log!(
|
||||||
"Rendering nodes and {} attractors took {}",
|
"Rendering nodes and {} attractors took {}",
|
||||||
@ -80,6 +76,15 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
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(&nodes.borrow(), render_id, render_node_fn);
|
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);
|
||||||
|
}
|
||||||
|
context.set_fill_style(&JsValue::from("red"));
|
||||||
|
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();
|
context.stroke();
|
||||||
let end_time = window().unwrap().performance().unwrap().now();
|
let end_time = window().unwrap().performance().unwrap().now();
|
||||||
log!(
|
log!(
|
||||||
@ -90,7 +95,9 @@ pub fn Background(cx: Scope, class: &'static str) -> impl IntoView {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let window = window().unwrap();
|
let window = window().unwrap();
|
||||||
window.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).unwrap();
|
window
|
||||||
|
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
|
||||||
|
.unwrap();
|
||||||
closure.forget();
|
closure.forget();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,26 +1,107 @@
|
|||||||
|
use log::info;
|
||||||
|
|
||||||
use super::{Attractor, Node, Point};
|
use super::{Attractor, Node, Point};
|
||||||
|
|
||||||
pub fn calculate_new_node_position(
|
pub fn calculate_new_node_position(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
attractors: &Vec<*mut Attractor>,
|
attractors: &Vec<*mut Attractor>,
|
||||||
segment_length: u16,
|
segment_length: u16,
|
||||||
) -> Point {
|
) -> Option<Point> {
|
||||||
|
calculate_new_node_position_with_skip(node, attractors, segment_length, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_new_node_position_with_skip(
|
||||||
|
node: &Node,
|
||||||
|
attractors: &Vec<*mut Attractor>,
|
||||||
|
segment_length: u16,
|
||||||
|
attractors_to_skip: usize,
|
||||||
|
) -> Option<Point> {
|
||||||
|
assert!(
|
||||||
|
attractors.len() > 0,
|
||||||
|
"There must be at least one attractor!"
|
||||||
|
);
|
||||||
let mut attraction_sum_x = 0;
|
let mut attraction_sum_x = 0;
|
||||||
let mut attraction_sum_y = 0;
|
let mut attraction_sum_y = 0;
|
||||||
|
|
||||||
for a in attractors.iter() {
|
for a in attractors.iter().skip(attractors_to_skip) {
|
||||||
unsafe {
|
if attractors_to_skip > 0 {
|
||||||
attraction_sum_x += (**a).position.x - node.position.x;
|
info!("working on attractor {:?}", a);
|
||||||
attraction_sum_y += (**a).position.y - node.position.y;
|
|
||||||
}
|
}
|
||||||
|
let attractor: &Attractor;
|
||||||
|
unsafe {
|
||||||
|
attractor = &**a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if attractors_to_skip > 0 {
|
||||||
|
info!("successfully worked with attractor {:?}", a);
|
||||||
|
}
|
||||||
|
attraction_sum_x += attractor.position.x - node.position.x;
|
||||||
|
attraction_sum_y += attractor.position.y - node.position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here this fixes what happens when the sum of the vectors towards attractors is 0
|
||||||
|
// See test new_node_ignores_attractors_when_average_results_in_no_movement
|
||||||
|
if attraction_sum_y == 0 && attraction_sum_x == 0 {
|
||||||
|
if attractors.len() == 1 {
|
||||||
|
let a = attractors.first().unwrap();
|
||||||
|
unsafe {
|
||||||
|
(**a).dead = true;
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
attractors.len() > attractors_to_skip + 1,
|
||||||
|
"There must be more attractors in the array than the number to skip"
|
||||||
|
);
|
||||||
|
|
||||||
|
return calculate_new_node_position_with_skip(
|
||||||
|
node,
|
||||||
|
attractors,
|
||||||
|
segment_length,
|
||||||
|
attractors_to_skip + 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = Point {
|
let point = Point {
|
||||||
x: node.position.x + attraction_sum_x / attractors.len() as i32,
|
x: node.position.x + attraction_sum_x as i32,
|
||||||
y: node.position.y + attraction_sum_y / attractors.len() as i32,
|
y: node.position.y + attraction_sum_y as i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
node.position.movement(point, segment_length)
|
let new_point = node.position.movement(point, segment_length);
|
||||||
|
println!("just did movement");
|
||||||
|
dbg!(node.position, point, segment_length, new_point);
|
||||||
|
|
||||||
|
let new_distance = new_point.distance(&node.position);
|
||||||
|
if new_distance > (segment_length * 2) as f64 {
|
||||||
|
info!(
|
||||||
|
"new node {:?} is far from parent {:?}",
|
||||||
|
new_point, node.position
|
||||||
|
);
|
||||||
|
let mut readable_attractors: Vec<&Attractor> = Vec::new();
|
||||||
|
unsafe {
|
||||||
|
attractors.iter().for_each(|a| {
|
||||||
|
readable_attractors.push(&**a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"Calculations inputs are
|
||||||
|
attractors {:?}
|
||||||
|
node.position {:?}
|
||||||
|
segment_length {}
|
||||||
|
point {:?}",
|
||||||
|
readable_attractors, node.position, segment_length, point
|
||||||
|
);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Calculations outputs are
|
||||||
|
distance {}
|
||||||
|
attraction_sum x {} , y {}
|
||||||
|
new_point {:?}",
|
||||||
|
new_distance, attraction_sum_x, attraction_sum_y, new_point
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(new_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -30,19 +111,97 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_node_moves_toward_single_attractor() {
|
fn new_node_moves_toward_single_attractor() {
|
||||||
let mut growth_cell = GrowthCell::from_positions([(0, 0), (0, 10)].to_vec());
|
let mut growth_cell = GrowthCell::from_positions((0, 0), [(0, 10)].to_vec());
|
||||||
let attractors_as_ptr_mut = growth_cell.attractors_as_ptr_mut();
|
let mut attractors_as_ptr_mut = growth_cell.attractors_as_ptr_mut();
|
||||||
|
|
||||||
let point = calculate_new_node_position(
|
let point = calculate_new_node_position(
|
||||||
&growth_cell.node,
|
&growth_cell.node,
|
||||||
&attractors_as_ptr_mut,
|
&mut attractors_as_ptr_mut,
|
||||||
SEGMENT_LENGTH,
|
SEGMENT_LENGTH,
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(point, Point::new((0, 5)));
|
assert_eq!(point, Point::new((0, 5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_node_ignores_dead_attractor() {}
|
fn new_node_ignores_attractors_when_average_results_in_no_movement() {
|
||||||
|
let mut growth_cell = GrowthCell::from_positions((10, 0), [(0, 0), (20, 0)].to_vec());
|
||||||
|
let mut attractors_as_ptr_mut = growth_cell.attractors_as_ptr_mut();
|
||||||
|
|
||||||
|
let point = calculate_new_node_position(
|
||||||
|
&growth_cell.node,
|
||||||
|
&mut attractors_as_ptr_mut,
|
||||||
|
SEGMENT_LENGTH,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(point, Point::new((15, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_node_ignores_dead_attractor() {
|
||||||
|
// TODO
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn large_number_of_attractors() {
|
||||||
|
let attractors = [
|
||||||
|
(1048, 241),
|
||||||
|
(935, 268),
|
||||||
|
(953, 363),
|
||||||
|
(978, 333),
|
||||||
|
(988, 355),
|
||||||
|
(894, 234),
|
||||||
|
(892, 331),
|
||||||
|
(1050, 275),
|
||||||
|
(972, 261),
|
||||||
|
(882, 280),
|
||||||
|
(1013, 212),
|
||||||
|
(1023, 233),
|
||||||
|
(899, 197),
|
||||||
|
(911, 285),
|
||||||
|
(879, 291),
|
||||||
|
(977, 293),
|
||||||
|
(938, 233),
|
||||||
|
(975, 196),
|
||||||
|
(964, 295),
|
||||||
|
(967, 197),
|
||||||
|
(869, 242),
|
||||||
|
(945, 323),
|
||||||
|
(1035, 287),
|
||||||
|
(962, 243),
|
||||||
|
(1023, 297),
|
||||||
|
(952, 185),
|
||||||
|
(984, 261),
|
||||||
|
(1011, 264),
|
||||||
|
(931, 209),
|
||||||
|
(900, 281),
|
||||||
|
(1006, 319),
|
||||||
|
(929, 295),
|
||||||
|
(936, 325),
|
||||||
|
(1001, 182),
|
||||||
|
(995, 225),
|
||||||
|
];
|
||||||
|
let node = (960, 266);
|
||||||
|
|
||||||
|
let mut growth_cell = GrowthCell::from_positions(node, attractors.to_vec());
|
||||||
|
let mut attractors_as_ptr_mut = growth_cell.attractors_as_ptr_mut();
|
||||||
|
|
||||||
|
let point = calculate_new_node_position(
|
||||||
|
&growth_cell.node,
|
||||||
|
&mut attractors_as_ptr_mut,
|
||||||
|
SEGMENT_LENGTH,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(point, Point::new((965, 266)));
|
||||||
|
/*
|
||||||
|
segment_length 5
|
||||||
|
Calculations outputs are
|
||||||
|
distance 996.1706681086329
|
||||||
|
attraction_sum x 17 , y 1
|
||||||
|
resulting point Point { x: 960, y: 266 }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
struct GrowthCell {
|
struct GrowthCell {
|
||||||
node: Node,
|
node: Node,
|
||||||
@ -50,15 +209,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GrowthCell {
|
impl GrowthCell {
|
||||||
pub fn from_positions(positions: Vec<(i32, i32)>) -> Self {
|
pub fn from_positions(node: (i32, i32), attractors_positions: Vec<(i32, i32)>) -> Self {
|
||||||
assert!(positions.len() >= 2);
|
assert!(attractors_positions.len() >= 1);
|
||||||
let node = Node {
|
let node = Node {
|
||||||
position: Point::new(positions[0]),
|
position: Point::new(node),
|
||||||
children: Vec::new().into(),
|
children: Vec::new().into(),
|
||||||
growing: true,
|
growing: true,
|
||||||
};
|
};
|
||||||
let mut attractors = Vec::new();
|
let mut attractors = Vec::new();
|
||||||
for p in positions.iter().skip(1) {
|
for p in attractors_positions.iter() {
|
||||||
attractors.push(Attractor {
|
attractors.push(Attractor {
|
||||||
position: Point::new(*p),
|
position: Point::new(*p),
|
||||||
dead: false,
|
dead: false,
|
||||||
@ -68,7 +227,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn attractors_as_ptr_mut(&mut self) -> Vec<*mut Attractor> {
|
fn attractors_as_ptr_mut(&mut self) -> Vec<*mut Attractor> {
|
||||||
self.attractors.iter_mut().map(|a| { a as *mut Attractor }).collect()
|
self.attractors
|
||||||
|
.iter_mut()
|
||||||
|
.map(|a| a as *mut Attractor)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,7 @@ 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
|
||||||
|
info!("Growing nodes {:?}", nodes);
|
||||||
self.grow_nodes(&mut nodes)
|
self.grow_nodes(&mut nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,20 +190,29 @@ impl SpaceColonization {
|
|||||||
//
|
//
|
||||||
// Using raw fixed length arrays would solve that but its a fine line between too
|
// Using raw fixed length arrays would solve that but its a fine line between too
|
||||||
// large memory usage and enough children nodes
|
// large memory usage and enough children nodes
|
||||||
|
//
|
||||||
|
// LEARNING : using unsafe here sounded like not the worst idea and was a nice
|
||||||
|
// opportunity to learn, which did happen. I am closing in now and I can say that I
|
||||||
|
// have learned quite a lot about how rust programs work and should be designed.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let new_node = Node::new(calculate_new_node_position(
|
/*
|
||||||
&(**node),
|
let segfault_boy: *const u8 = usize::MAX as *const u8;
|
||||||
attractor,
|
let memory_content = *segfault_boy;
|
||||||
self.segment_length,
|
println!("memory content at address 0 {}", memory_content);
|
||||||
));
|
*/
|
||||||
attractor.iter().for_each(|a| {
|
let new_point =
|
||||||
if (**a).position.distance(&new_node.position) <= self.kill_distance {
|
calculate_new_node_position(&(**node), attractor, self.segment_length);
|
||||||
dead_attractors.push(*a);
|
if let Some(new_point) = new_point {
|
||||||
(**a).dead = true;
|
let new_node = Node::new(new_point);
|
||||||
}
|
attractor.iter().for_each(|a| {
|
||||||
});
|
if (**a).position.distance(&new_node.position) <= self.kill_distance {
|
||||||
(**node).children.push(new_node);
|
dead_attractors.push(*a);
|
||||||
|
(**a).dead = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(**node).children.push(new_node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -212,6 +222,10 @@ impl SpaceColonization {
|
|||||||
mut attractor_to_closest_node: &mut HashMap<*mut Attractor, Attraction>,
|
mut attractor_to_closest_node: &mut HashMap<*mut Attractor, Attraction>,
|
||||||
n: &mut Node,
|
n: &mut Node,
|
||||||
) {
|
) {
|
||||||
|
for child in n.children.iter_mut() {
|
||||||
|
self.build_attractor_to_closest_node(&mut attractor_to_closest_node, child);
|
||||||
|
}
|
||||||
|
|
||||||
if !n.growing {
|
if !n.growing {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -233,14 +247,11 @@ impl SpaceColonization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in n.children.iter_mut() {
|
|
||||||
self.build_attractor_to_closest_node(&mut attractor_to_closest_node, child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_attractors_in_range(&mut self, n: &Node) -> Vec<(*mut Attractor, f64)> {
|
fn find_attractors_in_range(&mut self, n: &Node) -> Vec<(*mut Attractor, f64)> {
|
||||||
let mut attractors_in_range = Vec::new();
|
let mut attractors_in_range = Vec::new();
|
||||||
for a in self.attractors.iter_mut() {
|
for a in self.attractors.iter_mut().filter(|a| !a.dead) {
|
||||||
let distance = n.position.distance(&a.position);
|
let distance = n.position.distance(&a.position);
|
||||||
if distance < self.attraction_distance as f64 {
|
if distance < self.attraction_distance as f64 {
|
||||||
attractors_in_range.push((a as *mut Attractor, distance));
|
attractors_in_range.push((a as *mut Attractor, distance));
|
||||||
@ -304,7 +315,6 @@ mod test {
|
|||||||
println!("after grow 2");
|
println!("after grow 2");
|
||||||
dbg!(&nodes);
|
dbg!(&nodes);
|
||||||
|
|
||||||
// TODO assert nodes 3,0 and 6,0
|
|
||||||
assert_vertices(
|
assert_vertices(
|
||||||
&sc,
|
&sc,
|
||||||
&nodes,
|
&nodes,
|
||||||
@ -323,4 +333,24 @@ mod test {
|
|||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 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());
|
||||||
|
|
||||||
|
sc.grow(&mut nodes);
|
||||||
|
|
||||||
|
assert_vertices(&sc, &nodes, Vec::new());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user