//! Polygon graphs are used for a more general approach than polygons. //! //! They are not polygons, but often describe a polygon and make some algorithms on polygons faster //! or possible, which may not be practical on the polygon data alone. use super::Polygon; use crate::math::{self, LineSegment, Vec2}; use nalgebra::{RealField, Scalar}; use std::cmp::{Ordering, PartialOrd}; #[derive(Debug)] struct Node { pub vec: Vec2, pub adjacent: Vec>, } struct EdgeIterator<'a, T: Scalar + Copy> { nodes: &'a [Node], pos: (usize, usize), } /// An undirected graph, that is optimised for polygon edge operations. Since edges of a polygon /// are an undirected graph, this structure also holds both directions. This makes it rather space /// inefficient, but makes edge operations rather swift. #[derive(Debug)] pub struct PolygonGraph { /// The nodes of the graph, together with their adjacency list. nodes: Vec>, } // Helper functions to find nodes/vecs in sorted fields, so It doesn't always have to be written // out. #[inline] fn find_vec2( field: &[Vec2], lookup: &Vec2, ) -> Result { field.binary_search_by(|n| n.partial_cmp(lookup).unwrap_or(Ordering::Greater)) } #[inline] fn find_node( field: &[Node], lookup: &Vec2, ) -> Result { field.binary_search_by(|n| n.vec.partial_cmp(lookup).unwrap_or(Ordering::Greater)) } impl<'a, T: Scalar + Copy> EdgeIterator<'a, T> { pub fn new(nodes: &'a [Node]) -> Self { Self { nodes, pos: (0, 0) } } } impl<'a, T: Scalar + Copy> Iterator for EdgeIterator<'a, T> { type Item = LineSegment; fn next(&mut self) -> Option { // Try to find the element in the nodes vector, if it exists. if let Some(node) = self.nodes.get(self.pos.0) { let end = node.adjacent[self.pos.1]; // Advance the iterator to the next possible element if self.pos.1 + 1 < node.adjacent.len() { self.pos.1 += 1; } else { self.pos.1 = 0; self.pos.0 += 1; } Some(LineSegment::new(node.vec, end)) } else { None } } } impl PolygonGraph { /// Create a new, empty polygon graph. pub fn new() -> Self { Self { nodes: Vec::new() } } /// Count the number of edges in the graph. Internally, for each two connected points there are /// two edges, but this returns the amount of polygon edges. pub fn num_edges(&self) -> usize { let mut num_edges = 0; for node in &self.nodes { for _ in &node.adjacent { num_edges += 1; } } assert!(num_edges % 2 == 0); num_edges / 2 } /// Count the number of nodes in this graph. If this graph consists of multiple polygons, this /// can be different than the amount of corners, since corners with the same position are only /// counted once. pub fn num_nodes(&self) -> usize { self.nodes.len() } /// Checks if there is an edge between the two given vectors. Is commutative in respect to the /// two arguments. pub fn has_edge(&self, from: &Vec2, to: &Vec2) -> bool { // Binary search the starting and then the end node. if let Ok(from) = find_node(&self.nodes, from) { find_vec2(&self.nodes[from].adjacent, to).is_ok() } else { false } } // Helper function to add the edge into the internal graph representation for one side only. // Since to the outside the graph should always be undirected, this must be private. fn add_edge_onesided(&mut self, from: &Vec2, to: &Vec2) -> bool { match find_node(&self.nodes, from) { Ok(pos) => match find_vec2(&self.nodes[pos].adjacent, to) { Ok(_) => return false, Err(i) => self.nodes[pos].adjacent.insert(i, *to), }, Err(pos) => self.nodes.insert( pos, Node { vec: *from, adjacent: vec![*to], }, ), } true } /// Add an edge between the given vectors. If the edge already exists, it does nothing and /// returns false, otherwise it returns true after addition. pub fn add_edge(&mut self, from: &Vec2, to: &Vec2) -> bool { if !self.add_edge_onesided(from, to) { return false; } let back_edge_succ = self.add_edge_onesided(to, from); assert!(back_edge_succ); true } // Helper function to remove the edge in the internal graph representation for one side only. // Since to the outside the graph should always be undirected, this must be private. fn remove_edge_onesided(&mut self, from: &Vec2, to: &Vec2) -> bool { if let Ok(from) = find_node(&self.nodes, from) { if let Ok(to) = find_vec2(&self.nodes[from].adjacent, to) { // Remove the edge from the vector. self.nodes[from].adjacent.remove(to); // If the node has no adjacent nodes anymore, remove it entirely. if self.nodes[from].adjacent.is_empty() { self.nodes.remove(from); } true } else { false } } else { false } } /// Remove an edge between the given vectors. If there is no edge between them, it does nothing /// and returns false, otherwise it returns true after deletion. pub fn remove_edge(&mut self, from: &Vec2, to: &Vec2) -> bool { if !self.remove_edge_onesided(from, to) { return false; } let back_edge_succ = self.remove_edge_onesided(to, from); assert!(back_edge_succ); true } /// Constructs a new PolygonGraph from the provided polygon. Adds a node for every corner and /// an edge to all connected corners (which should be exactly two, for regular polygons) pub fn from_polygon(polygon: &Polygon) -> Self { let mut graph = PolygonGraph { nodes: Vec::with_capacity(polygon.corners.len()), }; graph.add_all(polygon); graph } /// Add all edges of the provided polygon into the graph. Requires roughly double as much space /// as the normal polygon. pub fn add_all(&mut self, polygon: &Polygon) { for i in 0..polygon.corners.len() { self.add_edge( &polygon.corners[i], &polygon.corners[(i + 1) % polygon.corners.len()], ); } } /// Calculates all points where the graph edges intersect with one another. It then adds them /// into the adjacency list such that the intersection point lies between the nodes of the /// lines. pub fn intersect_self(&mut self) where T: RealField, { // Find all intersections with all other edges. let mut to_delete: Vec> = Vec::new(); let mut to_add: Vec<(Vec2, Vec2)> = Vec::new(); for segment in EdgeIterator::new(&self.nodes) { /* Save all intersections of this line with any other line, and the line that it's * intersecting with. */ let mut intersections: Vec> = Vec::new(); for compare_segment in EdgeIterator::new(&self.nodes) { if segment.eq_ignore_dir(&compare_segment) { continue; } if let Some(intersection) = LineSegment::intersection(&segment, &compare_segment) { intersections.push(intersection); } } if intersections.is_empty() { continue; } to_delete.push(segment.clone()); // Safe, since at least the line segment itself is represented. let segments = segment.segments(&intersections); for i in 1..segments.len() { to_add.push((segments[i - 1], segments[i])); } } for segment in to_delete { self.remove_edge(&segment.start, &segment.end); } for (start, end) in to_add { self.add_edge(&start, &end); } } /// Finds the minimal polygon that could hold this graph together, i.e. could contain the /// entire graph, but with the minimal amount of space. It may however still contain extra /// corner points, meaning an extra edge for three collinear points on this edge, that can be /// cleaned up. pub fn bounding_polygon(mut self) -> Polygon where T: RealField, { assert!(self.num_nodes() >= 3); self.intersect_self(); /* Start with the top-left node. Since the nodes are always sorted by y over x from top to * bottom and left to right, this is the very first element in the vector. This is also a * corner, because for such a node to be enveloped, there would have to be a node further * to the top, in which case that node would have been selected. */ let mut current_node = &self.nodes[0]; // Pretend we're coming from the top to start in the right direction. let mut last_vec = current_node.vec - Vec2::new(T::zero(), T::one()); let mut bounding_corners = vec![current_node.vec]; loop { /* Find the next point by choosing the one with the greatest angle. This means we are * "bending" to the leftmost edge at each step. Since we are going around the polygon * in a clockwise direction, this yields the hull around the polygon. * NOTE: Going left is just as viable, but we would have to handle the case where the * algorithm would try to go back because the edge back has 0 degrees, which would be * always preferred. Going right makes going back the absolute worst option. */ let next_corner = current_node .adjacent .iter() .max_by(|&a, &b| { math::triplet_angle(last_vec, current_node.vec, *a) .partial_cmp(&math::triplet_angle(last_vec, current_node.vec, *b)) .unwrap_or(Ordering::Equal) }) .expect("Adjacency list is empty. The polygon has an open edge (is broken)"); // When we have come back to the start, the traversal is completed if *next_corner == bounding_corners[0] { break; } bounding_corners.push(*next_corner); last_vec = current_node.vec; current_node = &self.nodes[find_node(&self.nodes, &next_corner) .expect("Failure to find node that should be inside list.")]; } Polygon::new(bounding_corners).expect("PolygonGraph produced invalid polygon") } } impl Default for PolygonGraph { fn default() -> Self { Self::new() } } #[cfg(test)] mod test { use super::*; #[test] fn from_polygon() { let a = Vec2::new(0., 0.); let b = Vec2::new(0., 1.); let c = Vec2::new(0.5, 1.); let triangle = Polygon::new(vec![a, b, c]).unwrap(); let graph = PolygonGraph::from_polygon(&triangle); assert_eq!(graph.num_edges(), 3); assert!(graph.has_edge(&a, &b)); assert!(graph.has_edge(&b, &a)); assert!(graph.has_edge(&a, &c)); assert!(graph.has_edge(&c, &a)); assert!(graph.has_edge(&b, &c)); assert!(graph.has_edge(&c, &b)); } #[test] fn add_all() { let top_left = Vec2::new(0., 0.); let top_right = Vec2::new(1., 0.); let bot_left = Vec2::new(0., 1.); let bot_right = Vec2::new(1., 1.); let triangle = Polygon::new(vec![top_left, bot_right, top_right]).unwrap(); let square = Polygon::new(vec![bot_left, bot_right, top_right, top_left]).unwrap(); let mut graph = PolygonGraph::new(); graph.add_all(&triangle); graph.add_all(&square); assert_eq!(graph.num_edges(), 5); assert_eq!(graph.num_nodes(), 4); assert!(graph.has_edge(&top_left, &top_right)); assert!(graph.has_edge(&top_right, &top_left)); assert!(graph.has_edge(&top_left, &bot_left)); assert!(graph.has_edge(&bot_left, &top_left)); assert!(graph.has_edge(&bot_left, &bot_right)); assert!(graph.has_edge(&bot_right, &bot_left)); assert!(graph.has_edge(&bot_right, &top_right)); assert!(graph.has_edge(&top_right, &bot_right)); assert!(graph.has_edge(&top_left, &bot_right)); assert!(graph.has_edge(&bot_right, &top_left)); } #[test] fn intersect_self() { let first = Polygon::new(vec![ Vec2::new(0., 0.), Vec2::new(0., 2.), Vec2::new(2., 2.), Vec2::new(3., 1.), Vec2::new(2., 0.), ]) .unwrap(); let second = Polygon::new(vec![ Vec2::new(2.5, -0.5), Vec2::new(0., 2.), Vec2::new(2., 2.), Vec2::new(2., 0.5), Vec2::new(2.5, 0.), ]) .unwrap(); let mut graph = PolygonGraph::from_polygon(&first); graph.add_all(&second); graph.intersect_self(); println!("Intersected graph:"); println!("{:#?}", &graph); assert_eq!(graph.num_nodes(), 9); assert_eq!(graph.num_edges(), 12); assert!(graph.has_edge(&Vec2::new(2., 0.), &Vec2::new(2.25, 0.25))); assert!(graph.has_edge(&Vec2::new(3., 1.), &Vec2::new(2.25, 0.25))); assert!(!graph.has_edge(&Vec2::new(2., 0.), &Vec2::new(3., 1.))); assert!(graph.has_edge(&Vec2::new(0., 2.), &Vec2::new(2., 2.))); assert!(graph.has_edge(&Vec2::new(2., 2.), &Vec2::new(0., 2.))); assert!(graph.has_edge(&Vec2::new(0., 2.), &Vec2::new(2., 0.))); assert!(!graph.has_edge(&Vec2::new(0., 2.), &Vec2::new(2.5, -0.5))); } #[test] fn bounding_polygon() { let first = Polygon::new(vec![ Vec2::new(0., 0.), Vec2::new(0., 2.), Vec2::new(2., 2.), Vec2::new(3., 1.), Vec2::new(2., 0.), ]) .unwrap(); let second = Polygon::new(vec![ Vec2::new(2.5, -0.5), Vec2::new(0., 2.), Vec2::new(2., 2.), Vec2::new(2., 0.5), Vec2::new(2.5, 0.), ]) .unwrap(); let mut graph = PolygonGraph::from_polygon(&first); graph.add_all(&second); let bounding = graph.bounding_polygon(); let num_corners = 8; assert_eq!(bounding.corners.len(), num_corners); // Run around the polygon to see if it was constructed correctly. let start_i = bounding .corners .iter() .position(|&x| x == Vec2::new(0., 0.)) .expect("Starting vector does not exist in polygon."); assert_eq!( bounding.corners[(start_i + 1) % num_corners], Vec2::new(0., 2.) ); assert_eq!( bounding.corners[(start_i + 2) % num_corners], Vec2::new(2., 2.) ); assert_eq!( bounding.corners[(start_i + 3) % num_corners], Vec2::new(3., 1.) ); assert_eq!( bounding.corners[(start_i + 4) % num_corners], Vec2::new(2.25, 0.25) ); assert_eq!( bounding.corners[(start_i + 5) % num_corners], Vec2::new(2.5, 0.0) ); assert_eq!( bounding.corners[(start_i + 6) % num_corners], Vec2::new(2.5, -0.5) ); assert_eq!( bounding.corners[(start_i + 7) % num_corners], Vec2::new(2., 0.) ); } }