Added a spanning tree

This commit is contained in:
Emi Simpson 2021-11-17 12:41:33 -05:00
parent 3e15269afb
commit df4fa8789c
Signed by: Emi
GPG key ID: A12F2C2FFDC3D847
3 changed files with 193 additions and 16 deletions

9
Cargo.lock generated
View file

@ -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"

View file

@ -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"]

View file

@ -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<NodeRef>,
pub triangles: Vec<Triangle>,
pub spanning_tree: HashSet<Edge>,
spanning_tree_conn: HashMap<usize, SmallVec<[usize; 8]>>, // usize is casted from a noderef
adjacency: HashMap<Edge, (NeighborOne, NeighborTwo)>,
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<Cell<Node>>;
#[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<Edge, String> {
// 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<HashSet<usize>, 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 {