diff options
Diffstat (limited to 'src/math/polygon_graph.rs')
| -rw-r--r-- | src/math/polygon_graph.rs | 462 |
1 files changed, 0 insertions, 462 deletions
diff --git a/src/math/polygon_graph.rs b/src/math/polygon_graph.rs deleted file mode 100644 index 14b2b0d..0000000 --- a/src/math/polygon_graph.rs +++ /dev/null @@ -1,462 +0,0 @@ -use super::{LineSegment, Polygon, Vec2}; -use nalgebra::{RealField, Scalar}; -use std::cmp::{Ordering, PartialOrd}; - -#[derive(Debug)] -struct Node<T: Scalar + Copy> { - pub vec: Vec2<T>, - pub adjacent: Vec<Vec2<T>>, -} - -struct EdgeIterator<'a, T: Scalar + Copy> { - nodes: &'a [Node<T>], - 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<T: Scalar + Copy + PartialOrd> { - /// The nodes of the graph, together with their adjacency list. - nodes: Vec<Node<T>>, -} -// Helper functions to find nodes/vecs in sorted fields, so It doesn't always have to be written -// out. -#[inline] -fn find_vec2<T: Scalar + Copy + PartialOrd>( - field: &[Vec2<T>], - lookup: &Vec2<T>, -) -> Result<usize, usize> { - field.binary_search_by(|n| n.partial_cmp(lookup).unwrap_or(Ordering::Greater)) -} -#[inline] -fn find_node<T: Scalar + Copy + PartialOrd>( - field: &[Node<T>], - lookup: &Vec2<T>, -) -> Result<usize, usize> { - 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<T>]) -> Self { - Self { nodes, pos: (0, 0) } - } -} - -impl<'a, T: Scalar + Copy> Iterator for EdgeIterator<'a, T> { - type Item = LineSegment<T>; - - fn next(&mut self) -> Option<Self::Item> { - // 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<T: Scalar + Copy + PartialOrd> PolygonGraph<T> { - /// 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<T>, to: &Vec2<T>) -> 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<T>, to: &Vec2<T>) -> 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<T>, to: &Vec2<T>) -> 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<T>, to: &Vec2<T>) -> 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<T>, to: &Vec2<T>) -> 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<T>) -> 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<T>) { - 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<LineSegment<T>> = Vec::new(); - let mut to_add: Vec<(Vec2<T>, Vec2<T>)> = 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<Vec2<T>> = 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<T> - 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_polygon = Polygon::new(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| { - super::triplet_angle(last_vec, current_node.vec, *a) - .partial_cmp(&super::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_polygon.corners[0] { - break; - } - - bounding_polygon.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.")]; - } - - bounding_polygon - } -} - -impl<T: Scalar + Copy + PartialOrd> Default for PolygonGraph<T> { - 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]); - - 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]); - - let square = Polygon::new(vec![bot_left, bot_right, top_right, top_left]); - - 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.), - ]); - - 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.), - ]); - - 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.), - ]); - - 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.), - ]); - - 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(2., 0.) - ); - assert_eq!( - bounding.corners[(start_i + 2) % num_corners], - Vec2::new(2.5, -0.5) - ); - assert_eq!( - bounding.corners[(start_i + 3) % num_corners], - Vec2::new(2.5, 0.0) - ); - 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(3., 1.) - ); - assert_eq!( - bounding.corners[(start_i + 6) % num_corners], - Vec2::new(2., 2.) - ); - assert_eq!( - bounding.corners[(start_i + 7) % num_corners], - Vec2::new(0., 2.) - ); - } -} |
