Added a spanning tree
This commit is contained in:
parent
3e15269afb
commit
df4fa8789c
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"]
|
||||
|
|
195
src/main.rs
195
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<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 {
|
||||
|
|
Loading…
Reference in a new issue