//! Contains functions and structures to help with operations on polygons. pub mod polygon_graph; pub mod triangulate; pub use polygon_graph::*; pub use triangulate::*; use super::{LineSegment, Surface, Vec2}; use nalgebra::{ClosedDiv, ClosedMul, ClosedSub, RealField, Scalar}; use num_traits::Zero; use std::ops::Neg; #[derive(Debug)] // TODO: Support polygons with holes pub struct Polygon { pub corners: Vec>, } impl Polygon { pub fn new(corners: Vec>) -> Self { Self { corners } } /// Check whether a point is inside a polygon or not. If a point lies on an edge, it also /// counts as inside the polygon. /// Join this polygon with another, ensuring the area of the two stays the same, but the /// overlap is not doubled, but instead joined into one. /// Returns the Polygons themselves, if there is no overlap pub fn unite(self, other: Polygon) -> Vec> where T: RealField, { let mut graph = PolygonGraph::from_polygon(&self); graph.add_all(&other); // TODO: Make bounding box support multiple polygons vec![graph.bounding_polygon()] } } impl< T: Scalar + Copy + ClosedSub + ClosedMul + ClosedDiv + Neg + PartialOrd + Zero, > Surface for Polygon { fn contains_point(&self, p: &Vec2) -> bool { let n = self.corners.len(); let a = self.corners[n - 1]; let mut b = self.corners[n - 2]; let mut ax; let mut ay = a.y - p.y; let mut bx = b.x - p.x; let mut by = b.y - p.y; let mut lup = by > ay; for i in 0..n { // ax = bx; ay = by; b = self.corners[i]; bx = b.x - p.x; by = b.y - p.y; if ay == by { continue; } lup = by > ay; } let mut depth = 0; for i in 0..n { ax = bx; ay = by; let b = &self.corners[i]; bx = b.x - p.x; by = b.y - p.y; if ay < T::zero() && by < T::zero() { // both "up" or both "down" continue; } if ay > T::zero() && by > T::zero() { // both "up" or both "down" continue; } if ax < T::zero() && bx < T::zero() { // both points on the left continue; } if ay == by && (if ax < bx { ax } else { bx }) <= T::zero() { return true; } if ay == by { continue; } let lx = ax + (((bx - ax) * -ay) / (by - ay)); if lx == T::zero() { // point on edge return true; } if lx > T::zero() { depth += 1; } if ay == T::zero() && lup && by > ay { // hit vertex, both up depth -= 1; } if ay == T::zero() && !lup && by < ay { // hit vertex, both down depth -= 1; } lup = by > ay; } (depth & 1) == 1 } fn contains_line_segment(&self, line_segment: &LineSegment) -> bool { unimplemented!() } } #[cfg(test)] mod test { use super::*; #[test] fn polygon_contains() { let polygon = Polygon::new(vec![ Vec2::new(0., 0.), Vec2::new(-1., 1.), Vec2::new(0., 2.), Vec2::new(1., 3.), Vec2::new(3., 1.5), Vec2::new(2., 0.), Vec2::new(1., 1.), ]); assert!(!polygon.contains_point(&Vec2::new(1., -2.))); assert!(!polygon.contains_point(&Vec2::new(-1., 0.5))); assert!(polygon.contains_point(&Vec2::new(0., 0.5))); assert!(polygon.contains_point(&Vec2::new(0.5, 1.))); assert!(polygon.contains_point(&Vec2::new(0.5, 1.5))); assert!(!polygon.contains_point(&Vec2::new(-2., 1.9))); assert!(!polygon.contains_point(&Vec2::new(0., 3.))); assert!(polygon.contains_point(&Vec2::new(1., 3.))); } #[test] fn polygon_union() { let first = Polygon::new(vec![ Vec2::new(-2., 1.), Vec2::new(-0.5, 2.5), Vec2::new(2., 2.), Vec2::new(0.5, 1.5), Vec2::new(1., 0.), Vec2::new(-0.5, 1.), ]); let second = Polygon::new(vec![ Vec2::new(0., 0.), Vec2::new(-2., 2.), Vec2::new(3., 2.), Vec2::new(1.5, 0.), ]); let union = first.unite(second); assert_eq!(union.len(), 1); let union = &union[0]; println!("Union of the two polygons: {:?}", union); assert_eq!(union.corners.len(), 11); assert!(union .corners .iter() .find(|&p| p.x == 0. && p.y == 0.) .is_some()); } }