diff --git a/Cargo.lock b/Cargo.lock index c89b266..e7131f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,6 +135,7 @@ name = "glow" version = "0.1.0" dependencies = [ "macroquad", + "smallvec 1.7.0", ] [[package]] @@ -175,7 +176,7 @@ checksum = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0" dependencies = [ "byteorder", "ogg", - "smallvec", + "smallvec 0.6.14", ] [[package]] @@ -402,6 +403,12 @@ dependencies = [ "maybe-uninit", ] +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + [[package]] name = "ttf-parser" version = "0.12.3" diff --git a/Cargo.toml b/Cargo.toml index 91862de..9acb85e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,7 @@ edition = "2021" [dependencies] macroquad = "0.3" -#itertools = "0.10.1" + +[dependencies.smallvec] +version = "1.7.0" +features = ["union", "const_generics", "const_new"] diff --git a/src/main.rs b/src/main.rs index b28cb6a..f3c9b4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ pub mod util; +use smallvec::SmallVec; +use smallvec::smallvec; +use std::collections::HashSet; use macroquad::prelude::set_camera; use macroquad::prelude::DVec2; use macroquad::prelude::Camera2D; @@ -83,6 +86,8 @@ async fn main() { pub struct DelaunayDemo { pub nodes: Vec, pub triangles: Vec, + pub spanning_tree: HashSet, + spanning_tree_conn: HashMap>, // usize is casted from a noderef adjacency: HashMap, poisoned: bool, random_state: u128, @@ -177,6 +182,8 @@ impl Default for DelaunayDemo { width: WIDTH, random_state: 1312_1312_1312, poisoned: false, + spanning_tree: HashSet::with_capacity(100), + spanning_tree_conn: HashMap::with_capacity(100), }; /*let n_v_nodes = ((height / 50.0) - 1.0).round(); @@ -232,6 +239,15 @@ type NodeRef = Arc>; #[derive(Clone)] pub struct Edge(NodeRef, NodeRef); +impl Edge { + fn node_thats_not(&self, node: &NodeRef) -> Result<&NodeRef, String> { + [&self.0, &self.1].into_iter() + .filter(|n| !Arc::ptr_eq(n, node)) + .next() + .ok_or_else(|| format!("Edge {:?} connects two identical nodes", self)) + } +} + impl PartialEq for Edge { fn eq(&self, other: &Edge) -> bool { let mut our_ptrs = [ @@ -522,10 +538,10 @@ impl DelaunayDemo { draw_circle_lines(c.x as f32, c.y as f32, r as f32, 1.0, color); } for edge in self.adjacency.keys() { - let color = if highlight_segments.contains(&edge) { + let color = if self.spanning_tree.contains(&edge) { HIL_FG } else { - FG + DIM_FG }; let edge_0 = edge.0.get().as_f32(); let edge_1 = edge.1.get().as_f32(); @@ -555,15 +571,17 @@ impl DelaunayDemo { fn draw_voronoi(&self) { for neighbors in self.adjacency.values() { if let (Occupant(tri1), Friend(tri2)) = neighbors { - let (center1, _) = tri1.circumcenter(); - let (center2, _) = tri2.circumcenter(); - draw_line( - center1.x as f32, - center1.y as f32, - center2.x as f32, - center2.y as f32, - 3.0, FG - ); + if !self.spanning_tree.contains(tri1.common_edge(tri2).unwrap()) { + let (center1, _) = tri1.circumcenter(); + let (center2, _) = tri2.circumcenter(); + draw_line( + center1.x as f32, + center1.y as f32, + center2.x as f32, + center2.y as f32, + 3.0, FG + ); + } } } } @@ -616,12 +634,62 @@ impl DelaunayDemo { // For every edge form a new triangle with that edge and this point let node = Arc::new(Cell::new(node)); + let mut node_connected_edges = HashSet::with_capacity(orphaned_edges.len()); for edge in orphaned_edges { - self.create_triangle([ + let edges = [ Edge(edge.0.clone(), node.clone()), Edge(edge.1.clone(), node.clone()), edge, - ])?; + ]; + for edge in &edges[..2] { + if self.is_point_inbounds(*edge.0.get()) { + node_connected_edges.insert(edge.clone()); + } + } + self.create_triangle(edges)?; + } + + // Update the spanning tree + let our_id = node.as_ptr() as usize; + if self.spanning_tree.is_empty() && self.nodes.len() == 5 { + + // Connect the two non-border points + let other_node: &NodeRef = self.nodes.iter() + .filter(|n| self.is_point_inbounds(*n.get())) + .next() + .unwrap(); + + let joining_edge = Edge(other_node.clone(), node.clone()); + let node_a_id = other_node.as_ptr() as usize; + let node_b_id = node.as_ptr() as usize; + + self.spanning_tree.insert(joining_edge); + self.spanning_tree_conn.insert(node_a_id, smallvec![node_b_id]); + self.spanning_tree_conn.insert(node_b_id, smallvec![node_a_id]); + + } else if !self.spanning_tree.is_empty() { + // Pick a random edge off this node and add it to the spanning tree + let random = util::pcg64(&mut self.random_state); + let random = random as usize % node_connected_edges.len(); + let lucky_edge = node_connected_edges.into_iter() + .skip(random) + .next() + .unwrap(); + let connected_node = lucky_edge.node_thats_not(&node) + .unwrap() + .as_ptr() as usize; + + self.spanning_tree.insert(lucky_edge.clone()); + self.spanning_tree_conn.insert(our_id, smallvec![connected_node]); + if let Some(other_edges) = self.spanning_tree_conn.get_mut(&connected_node) { + other_edges.push(our_id); + } else { + return Err(format!( + "Found node {} in triangle list, but it does not have an entry in \ + the spanning tree connections list", + *lucky_edge.node_thats_not(&node)?.get() + )); + } } self.nodes.push(node); @@ -656,7 +724,7 @@ impl DelaunayDemo { edge, triangle )); } - self.adjacency.remove(&edge); + self.remove_edge(&edge)?; }, Some((Border, our_side @ Friend(_))) => { *our_side = Hole; @@ -678,6 +746,105 @@ impl DelaunayDemo { Ok(triangle) } + fn remove_edge(&mut self, edge: &Edge) -> Result { + // Remove edge from the edge graph + let edge = if let Some((edge, _)) = self.adjacency.remove_entry(edge) { + edge + } else { + return Err(format!( + "Tried to remove edge {:?} from the list of edges, but it wasn't present", + edge + )); + }; + + // If the edge was part of the spanning tree and the tree is initialized + if !self.spanning_tree.is_empty() && self.spanning_tree.remove(&edge) { + let left_id = edge.0.as_ptr() as usize; + let right_id = edge.1.as_ptr() as usize; + // Remove it completely from the spanning tree + for (node, to_node) in [(left_id, right_id), (right_id, left_id)] { + if let Some(connected_nodes) = self.spanning_tree_conn.get_mut(&node) { + connected_nodes.retain(|n| *n != to_node); + } else { + return Err(format!( + "Edge {:?} was previously in edge graph, but it's node {} was not in \ + the connections list", edge, node + )); + } + } + + // Traverse the tree on either side of the edge + let tree_a = self.traverse_spanning_tree(edge.0.as_ptr() as usize)?; + let tree_b = self.traverse_spanning_tree(edge.1.as_ptr() as usize)?; + + // Find a list of edges that: + // - Have one node from each tree + // - Do not connect to an out-of-bounds node + let candidate_edges: Vec<_> = self.adjacency.keys() + .filter(|e| + ( + tree_a.contains(&(e.0.as_ptr() as usize)) && + tree_b.contains(&(e.1.as_ptr() as usize)) + ) || ( + tree_a.contains(&(e.1.as_ptr() as usize)) && + tree_b.contains(&(e.0.as_ptr() as usize)) + ) + ) + .filter(|e| + self.is_point_inbounds(*e.0.get()) && + self.is_point_inbounds(*e.1.get()) + ) + .collect(); + + // Pick one at random and add it to the spanning tree + let random = util::pcg64(&mut self.random_state); + let random = random as usize % candidate_edges.len(); + let new_edge = candidate_edges[random]; + + let node_a = new_edge.0.as_ptr() as usize; + let node_b = new_edge.1.as_ptr() as usize; + self.spanning_tree.insert(new_edge.clone()); + for (from, to) in [(node_a, node_b), (node_b, node_a)] { + let connected = self.spanning_tree_conn.get_mut(&from).unwrap(); + connected.push(to); + } + } + + Ok(edge) + } + + fn traverse_spanning_tree(&self, root: usize) -> Result, String> { + let mut visited = HashSet::with_capacity(self.nodes.len()); + let mut to_traverse: SmallVec<[usize; 40]> = smallvec![root]; + + while let Some(node) = to_traverse.pop() { + visited.insert(node); + + if let Some(c) = self.spanning_tree_conn.get(&node) { + to_traverse.extend( + c.iter() + .filter(|c| !visited.contains(c)) + .map(|c| *c) + ); + } else { + return Err(format!( + "Spanning tree connection database lists a connection to node id {}, \ + but that node lacks an entry in the database", node + )); + }; + + } + + Ok(visited) + } + + fn is_point_inbounds(&self, p: DVec2) -> bool { + p.x > 0. && + p.y > 0. && + p.x < self.width && + p.y < self.height + } + fn create_triangle(&mut self, edges: [Edge; 3]) -> Result<&Triangle, String> { let triangle = Triangle::new(edges.clone()); for edge in edges {