#![allow(clippy::type_complexity)]
use num_traits::Num;
use std::cmp::Ord;
use std::cmp::Ordering;
use std::ops::{AddAssign, SubAssign};
use crate::point::Point;
use crate::vector2::Vector2;
#[derive(Eq, Clone, Debug, Default)]
pub struct RPolygon<T> {
pub origin: Point<T, T>,
vecs: Vec<Vector2<T, T>>,
}
impl<T: Clone + Num + Copy + std::ops::AddAssign + Ord> RPolygon<T> {
pub fn new(coords: &[Point<T, T>]) -> Self {
let (&origin, coords) = coords.split_first().unwrap();
let vecs = coords.iter().map(|pt| *pt - origin).collect();
RPolygon { origin, vecs }
}
pub fn from_origin_and_vectors(origin: Point<T, T>, vecs: Vec<Vector2<T, T>>) -> Self {
RPolygon { origin, vecs }
}
pub fn from_pointset(pointset: &[Point<T, T>]) -> Self {
Self::new(pointset)
}
pub fn add_assign(&mut self, rhs: Vector2<T, T>)
where
T: AddAssign,
{
self.origin += rhs;
}
pub fn sub_assign(&mut self, rhs: Vector2<T, T>)
where
T: SubAssign,
{
self.origin -= rhs;
}
pub fn signed_area(&self) -> T {
let mut itr = self.vecs.iter();
let mut vec0 = itr.next().unwrap();
let mut res = vec0.x_ * vec0.y_;
for vec1 in itr {
res += vec1.x_ * (vec1.y_ - vec0.y_);
vec0 = vec1;
}
res
}
pub fn vertices(&self) -> Vec<Point<T, T>> {
let mut result = Vec::with_capacity(self.vecs.len() + 1);
result.push(self.origin);
for vec in &self.vecs {
result.push(self.origin + *vec);
}
result
}
pub fn bounding_box(&self) -> (Point<T, T>, Point<T, T>) {
let mut min_x = T::zero();
let mut min_y = T::zero();
let mut max_x = T::zero();
let mut max_y = T::zero();
for vec in &self.vecs {
if vec.x_ < min_x {
min_x = vec.x_;
}
if vec.y_ < min_y {
min_y = vec.y_;
}
if vec.x_ > max_x {
max_x = vec.x_;
}
if vec.y_ > max_y {
max_y = vec.y_;
}
}
(
Point::new(self.origin.xcoord + min_x, self.origin.ycoord + min_y),
Point::new(self.origin.xcoord + max_x, self.origin.ycoord + max_y),
)
}
pub fn is_rectilinear(&self) -> bool {
true
}
pub fn is_anticlockwise(&self) -> bool
where
T: PartialOrd,
{
let mut pointset = Vec::with_capacity(self.vecs.len() + 1);
pointset.push(Vector2::new(T::zero(), T::zero()));
pointset.extend(self.vecs.iter().cloned());
if pointset.len() < 2 {
panic!("Polygon must have at least 2 points");
}
let (min_index, _) = pointset
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| {
a.x_.partial_cmp(&b.x_)
.unwrap_or(Ordering::Equal)
.then(a.y_.partial_cmp(&b.y_).unwrap_or(Ordering::Equal))
})
.unwrap();
let n = pointset.len();
let prev_point = pointset[(min_index + n - 1) % n];
let current_point = pointset[min_index];
prev_point.y_ > current_point.y_
}
}
impl<T: PartialEq> PartialEq for RPolygon<T> {
fn eq(&self, other: &Self) -> bool {
self.origin == other.origin && self.vecs == other.vecs
}
}
impl<T: Clone + Num + Ord + Copy> RPolygon<T> {
pub fn create_mono_rpolygon<F>(pointset: &[Point<T, T>], func: F) -> (Vec<Point<T, T>>, bool)
where
F: Fn(&Point<T, T>) -> (T, T),
{
let rightmost = pointset
.iter()
.max_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
.unwrap();
let leftmost = pointset
.iter()
.min_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
.unwrap();
let is_anticlockwise = func(rightmost).1 <= func(leftmost).1;
let (mut lst1, mut lst2): (Vec<Point<T, T>>, Vec<Point<T, T>>) = if is_anticlockwise {
pointset
.iter()
.partition(|pt| func(pt).1 <= func(leftmost).1)
} else {
pointset
.iter()
.partition(|pt| func(pt).1 >= func(leftmost).1)
};
lst1.sort_by_key(|a| func(a));
lst2.sort_by_key(|a| func(a));
lst2.reverse();
lst1.append(&mut lst2);
(lst1, is_anticlockwise) }
#[inline]
pub fn create_xmono_rpolygon(pointset: &[Point<T, T>]) -> (Vec<Point<T, T>>, bool) {
Self::create_mono_rpolygon(pointset, |a| (a.xcoord, a.ycoord))
}
#[inline]
pub fn create_ymono_rpolygon(pointset: &[Point<T, T>]) -> (Vec<Point<T, T>>, bool) {
Self::create_mono_rpolygon(pointset, |a| (a.ycoord, a.xcoord))
}
pub fn point_in_rpolygon(pointset: &[Point<T, T>], query_pt: &Point<T, T>) -> bool {
let mut result = false;
let n = pointset.len();
let mut pt0 = &pointset[n - 1];
for pt1 in pointset.iter() {
if ((pt1.ycoord <= query_pt.ycoord && query_pt.ycoord < pt0.ycoord)
|| (pt0.ycoord <= query_pt.ycoord && query_pt.ycoord < pt1.ycoord))
&& pt1.xcoord > query_pt.xcoord
{
result = !result;
}
pt0 = pt1;
}
result
}
}
pub fn rpolygon_is_monotone<T, F>(lst: &[Point<T, T>], dir: F) -> bool
where
T: Clone + Num + Ord + Copy + PartialOrd,
F: Fn(&Point<T, T>) -> (T, T),
{
if lst.len() <= 3 {
return true;
}
let (min_index, _) = lst
.iter()
.enumerate()
.min_by_key(|(_, pt)| dir(pt))
.unwrap();
let (max_index, _) = lst
.iter()
.enumerate()
.max_by_key(|(_, pt)| dir(pt))
.unwrap();
let n = lst.len();
let mut i = min_index;
while i != max_index {
let next_i = (i + 1) % n;
if dir(&lst[i]).0 > dir(&lst[next_i]).0 {
return false;
}
i = next_i;
}
let mut i = max_index;
while i != min_index {
let next_i = (i + 1) % n;
if dir(&lst[i]).0 < dir(&lst[next_i]).0 {
return false;
}
i = next_i;
}
true
}
pub fn rpolygon_is_xmonotone<T>(lst: &[Point<T, T>]) -> bool
where
T: Clone + Num + Ord + Copy + PartialOrd,
{
rpolygon_is_monotone(lst, |pt| (pt.xcoord, pt.ycoord))
}
pub fn rpolygon_is_ymonotone<T>(lst: &[Point<T, T>]) -> bool
where
T: Clone + Num + Ord + Copy + PartialOrd,
{
rpolygon_is_monotone(lst, |pt| (pt.ycoord, pt.xcoord))
}
pub fn rpolygon_is_convex<T>(lst: &[Point<T, T>]) -> bool
where
T: Clone + Num + Ord + Copy + PartialOrd,
{
rpolygon_is_xmonotone(lst) && rpolygon_is_ymonotone(lst)
}
pub fn rpolygon_is_anticlockwise<T>(pointset: &[Point<T, T>]) -> bool
where
T: Clone + Num + Ord + Copy + PartialOrd,
{
if pointset.len() < 2 {
panic!("Polygon must have at least 2 points");
}
let (min_index, min_point) = pointset
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| {
a.xcoord
.partial_cmp(&b.xcoord)
.unwrap_or(Ordering::Equal)
.then(a.ycoord.partial_cmp(&b.ycoord).unwrap_or(Ordering::Equal))
})
.unwrap();
let n = pointset.len();
let prev_index = (min_index + n - 1) % n;
let prev_point = pointset[prev_index];
let current_point = *min_point;
prev_point.ycoord() > current_point.ycoord()
}
#[cfg(test)]
mod test {
#![allow(non_upper_case_globals)]
use super::*;
#[test]
pub fn test_ymono_rpolygon() {
let coords = [
(-2, 2),
(0, -1),
(-5, 1),
(-2, 4),
(0, -4),
(-4, 3),
(-6, -2),
(5, 1),
(2, 2),
(3, -3),
(-3, -4),
(1, 4),
];
let mut pointset = vec![];
for (x_coord, y_coord) in coords.iter() {
pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
}
let (poly_points, is_cw) = RPolygon::<i32>::create_ymono_rpolygon(&pointset);
assert!(rpolygon_is_anticlockwise(&poly_points));
assert!(rpolygon_is_ymonotone(&poly_points));
assert!(!rpolygon_is_xmonotone(&poly_points));
for pt in poly_points.iter() {
print!("({}, {}) ", pt.xcoord, pt.ycoord);
}
let poly = RPolygon::<i32>::new(&poly_points);
assert!(!is_cw);
assert_eq!(poly.signed_area(), 45);
}
#[test]
pub fn test_xmono_rpolygon() {
let coords = [
(-2, 2),
(0, -1),
(-5, 1),
(-2, 4),
(0, -4),
(-4, 3),
(-6, -2),
(5, 1),
(2, 2),
(3, -3),
(-3, -4),
(1, 4),
];
let mut pointset = vec![];
for (x_coord, y_coord) in coords.iter() {
pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
}
let (poly_points, is_anticw) = RPolygon::<i32>::create_xmono_rpolygon(&pointset);
assert!(!rpolygon_is_anticlockwise(&poly_points));
assert!(rpolygon_is_xmonotone(&poly_points));
assert!(!rpolygon_is_ymonotone(&poly_points));
for pt in poly_points.iter() {
print!("({}, {}) ", pt.xcoord, pt.ycoord);
}
let poly = RPolygon::<i32>::new(&poly_points);
assert!(!is_anticw);
assert_eq!(poly.signed_area(), -53);
assert!(!poly.is_anticlockwise())
}
#[test]
pub fn test_point_in_rpolygon() {
let coords = [
(-2, 2),
(0, -1),
(-5, 1),
(-2, 4),
(0, -4),
(-4, 3),
(-6, -2),
(5, 1),
(2, 2),
(3, -3),
(-3, -4),
(1, 4),
];
let mut pointset = vec![];
for (x_coord, y_coord) in coords.iter() {
pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
}
let query_pt = Point::<i32, i32>::new(0, -3);
assert!(!RPolygon::<i32>::point_in_rpolygon(&pointset, &query_pt));
}
#[test]
fn test_signed_area_more_cases() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let pt4 = Point::new(0, 1);
let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
assert_eq!(poly.signed_area(), 1);
let pt5 = Point::new(0, 0);
let pt6 = Point::new(0, 1);
let pt7 = Point::new(1, 1);
let pt8 = Point::new(1, 0);
let poly2 = RPolygon::new(&[pt5, pt6, pt7, pt8]);
assert_eq!(poly2.signed_area(), -1);
}
#[test]
fn test_point_in_rpolygon_more_cases() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let pt4 = Point::new(0, 1);
let pointset = &[pt1, pt2, pt3, pt4];
let query_pt1 = Point::new(0, 0);
assert!(RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt1));
let query_pt2 = Point::new(1, 1);
assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt2));
let query_pt3 = Point::new(0, 1);
assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt3));
let query_pt4 = Point::new(1, 0);
assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt4));
}
}
#[test]
fn test_rpolygon_is_xmonotone() {
let coords = [(0, 0), (1, 0), (2, 0), (2, 1), (1, 1), (0, 1)];
let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(rpolygon_is_xmonotone(&pointset));
let coords2 = [(0, 0), (2, 0), (1, 1), (0, 2), (2, 2)];
let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(!rpolygon_is_xmonotone(&pointset2));
}
#[test]
fn test_rpolygon_is_ymonotone() {
let coords = [(0, 0), (0, 1), (0, 2), (1, 2), (1, 1), (1, 0)];
let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(rpolygon_is_ymonotone(&pointset));
let coords2 = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)];
let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(!rpolygon_is_ymonotone(&pointset2));
}
#[test]
fn test_rpolygon_is_convex() {
let coords = [(0, 0), (0, 2), (2, 2), (2, 0)];
let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(rpolygon_is_convex(&pointset));
let coords2 = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)];
let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
assert!(!rpolygon_is_convex(&pointset2));
}
#[test]
fn test_rpolygon_vertices() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let poly = RPolygon::new(&[pt1, pt2, pt3]);
let vertices = poly.vertices();
assert_eq!(vertices.len(), 3);
assert_eq!(vertices[0], pt1);
assert_eq!(vertices[1], pt2);
assert_eq!(vertices[2], pt3);
}
#[test]
fn test_rpolygon_bounding_box() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(2, 0);
let pt3 = Point::new(2, 2);
let pt4 = Point::new(0, 2);
let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
let (min, max) = poly.bounding_box();
assert_eq!(min, Point::new(0, 0));
assert_eq!(max, Point::new(2, 2));
}
#[test]
fn test_rpolygon_add_assign() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let mut poly = RPolygon::new(&[pt1, pt2, pt3]);
poly.add_assign(Vector2::new(1, 1));
let vertices = poly.vertices();
assert_eq!(vertices[0], Point::new(1, 1));
}
#[test]
fn test_rpolygon_sub_assign() {
let pt1 = Point::new(1, 1);
let pt2 = Point::new(2, 1);
let pt3 = Point::new(2, 2);
let mut poly = RPolygon::new(&[pt1, pt2, pt3]);
poly.sub_assign(Vector2::new(1, 1));
let vertices = poly.vertices();
assert_eq!(vertices[0], Point::new(0, 0));
}
#[test]
fn test_rpolygon_is_rectilinear() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let pt4 = Point::new(0, 1);
let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
assert!(poly.is_rectilinear());
}
#[test]
fn test_rpolygon_from_origin_and_vectors() {
let origin = Point::new(0, 0);
let vecs = vec![Vector2::new(1, 0), Vector2::new(1, 1), Vector2::new(0, 1)];
let poly = RPolygon::from_origin_and_vectors(origin, vecs);
assert_eq!(poly.origin, Point::new(0, 0));
let vertices = poly.vertices();
assert_eq!(vertices.len(), 4);
}
#[test]
fn test_rpolygon_from_pointset() {
let pointset = [Point::new(0, 0), Point::new(1, 0), Point::new(1, 1)];
let poly = RPolygon::from_pointset(&pointset);
assert_eq!(poly.origin, Point::new(0, 0));
}
#[test]
fn test_rpolygon_partial_eq() {
let pt1 = Point::new(0, 0);
let pt2 = Point::new(1, 0);
let pt3 = Point::new(1, 1);
let poly1 = RPolygon::new(&[pt1, pt2, pt3]);
let poly2 = RPolygon::new(&[pt1, pt2, pt3]);
assert_eq!(poly1, poly2);
let poly3 = RPolygon::new(&[pt1, pt2, Point::new(0, 1)]);
assert_ne!(poly1, poly3);
}
#[test]
fn test_rpolygon_is_anticlockwise_standalone() {
let pointset = [Point::new(0, 0), Point::new(1, 0), Point::new(1, 1)];
assert!(rpolygon_is_anticlockwise(&pointset));
}
#[test]
fn test_rpolygon_default() {
let poly: RPolygon<i32> = RPolygon::default();
assert_eq!(poly.origin, Point::new(0, 0));
}