Skip to main content

physdes/
polygon.rs

1#![allow(clippy::type_complexity)]
2
3use num_traits::Num;
4use std::cmp::Ordering;
5use std::ops::{AddAssign, SubAssign};
6
7use crate::point::Point;
8use crate::vector2::Vector2;
9
10/// Represents an arbitrary polygon with coordinates of type T
11///
12/// The `Polygon` struct stores the origin point and a vector of edges that define the polygon.
13/// It provides various operations and functionalities for working with polygons, such as
14/// area calculation, point containment checks, and geometric property verification.
15///
16/// ```svgbob
17///       *-----*-----*
18///      /     / \   \
19///     /     /   \   \
20///    *-----*     *---*
21///    |  origin
22///    *--> vecs[0]
23/// ```
24///
25/// Properties:
26///
27/// * `origin`: The origin point of the polygon
28/// * `vecs`: Vector of displacement vectors from origin to other vertices
29///
30/// # Examples
31///
32/// ```
33/// use physdes::point::Point;
34/// use physdes::polygon::Polygon;
35/// use physdes::vector2::Vector2;
36///
37/// let origin = Point::new(0, 0);
38/// let vecs = vec![Vector2::new(1, 0), Vector2::new(1, 1), Vector2::new(0, 1)];
39/// let poly = Polygon::from_origin_and_vectors(origin, vecs);
40/// assert_eq!(poly.origin, Point::new(0, 0));
41/// assert_eq!(poly.vecs.len(), 3);
42/// ```
43#[derive(Eq, Clone, Debug, Default)]
44#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
45pub struct Polygon<T> {
46    pub origin: Point<T, T>,
47    pub vecs: Vec<Vector2<T, T>>,
48}
49
50impl<T: Clone + Num + Ord + Copy + std::ops::AddAssign> Polygon<T> {
51    /// Constructs a new Polygon from a set of points
52    ///
53    /// The first point in the slice is used as the origin, and the remaining points
54    /// are used to construct displacement vectors relative to the origin.
55    ///
56    /// # Arguments
57    ///
58    /// * `coords` - A slice of points representing the vertices of the polygon
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use physdes::point::Point;
64    /// use physdes::polygon::Polygon;
65    /// use physdes::vector2::Vector2;
66    ///
67    /// let p1 = Point::new(1, 1);
68    /// let p2 = Point::new(2, 2);
69    /// let p3 = Point::new(3, 3);
70    /// let p4 = Point::new(4, 4);
71    /// let p5 = Point::new(5, 5);
72    /// let poly = Polygon::new(&[p1, p2, p3, p4, p5]);
73    /// assert_eq!(poly.origin, Point::new(1, 1));
74    /// assert_eq!(poly.vecs.len(), 4);
75    /// assert_eq!(poly.vecs[0], Vector2::new(1, 1));
76    /// ```
77    pub fn new(coords: &[Point<T, T>]) -> Self {
78        let (&origin, coords) = coords.split_first().unwrap();
79        let vecs = coords.iter().map(|pt| *pt - origin).collect();
80        Polygon { origin, vecs }
81    }
82
83    /// Constructs a new Polygon from origin and displacement vectors
84    ///
85    /// # Arguments
86    ///
87    /// * `origin` - The origin point of the polygon
88    /// * `vecs` - Vector of displacement vectors from origin
89    pub fn from_origin_and_vectors(origin: Point<T, T>, vecs: Vec<Vector2<T, T>>) -> Self {
90        Polygon { origin, vecs }
91    }
92
93    /// Constructs a new Polygon from a point set
94    ///
95    /// The first point in the set is used as the origin, and the remaining points
96    /// are used to construct displacement vectors relative to the origin.
97    pub fn from_pointset(pointset: &[Point<T, T>]) -> Self {
98        Self::new(pointset)
99    }
100
101    /// Calculates the area of the polygon
102    ///
103    /// # Returns
104    ///
105    /// The area of the polygon as a value of type T
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use physdes::point::Point;
111    /// use physdes::polygon::Polygon;
112    ///
113    /// // Create a unit square
114    /// let points = vec![
115    ///     Point::new(0, 0),
116    ///     Point::new(1, 0),
117    ///     Point::new(1, 1),
118    ///     Point::new(0, 1),
119    /// ];
120    /// let poly = Polygon::new(&points);
121    /// let area = poly.area();
122    /// // Note: area returns signed area (may be negative depending on vertex order)
123    /// ```
124    pub fn area(&self) -> T
125    where
126        T: std::ops::Sub<Output = T> + std::ops::AddAssign + std::ops::Mul<Output = T> + Copy,
127    {
128        let n = self.vecs.len();
129        if n < 2 {
130            return T::zero();
131        }
132
133        let vec0 = self.vecs[0];
134        let vec1 = self.vecs[1];
135        let itr = self.vecs.iter().skip(2);
136
137        let mut res = vec0.x_ * vec1.y_ - self.vecs[n - 1].x_ * self.vecs[n - 2].y_;
138
139        let mut vec0 = vec0;
140        let mut vec1 = vec1;
141
142        for vec2 in itr {
143            res += vec1.x_ * (vec2.y_ - vec0.y_);
144            vec0 = vec1;
145            vec1 = *vec2;
146        }
147
148        res
149    }
150
151    /// Gets all vertices of the polygon as points
152    ///
153    /// # Returns
154    ///
155    /// A vector of all polygon vertices in order
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// use physdes::point::Point;
161    /// use physdes::polygon::Polygon;
162    ///
163    /// let points = vec![
164    ///     Point::new(0, 0),
165    ///     Point::new(1, 0),
166    ///     Point::new(1, 1),
167    ///     Point::new(0, 1),
168    /// ];
169    /// let poly = Polygon::new(&points);
170    /// let vertices = poly.vertices();
171    /// assert_eq!(vertices.len(), 4);
172    /// assert_eq!(vertices[0], Point::new(0, 0));
173    /// ```
174    pub fn vertices(&self) -> Vec<Point<T, T>> {
175        let mut result = Vec::with_capacity(self.vecs.len() + 1);
176        result.push(self.origin);
177
178        for vec in &self.vecs {
179            result.push(self.origin + *vec);
180        }
181
182        result
183    }
184
185    /// Translates the polygon by adding a vector to its origin
186    pub fn add_assign(&mut self, rhs: Vector2<T, T>)
187    where
188        T: AddAssign,
189    {
190        self.origin += rhs;
191    }
192
193    /// Translates the polygon by subtracting a vector from its origin
194    pub fn sub_assign(&mut self, rhs: Vector2<T, T>)
195    where
196        T: SubAssign,
197    {
198        self.origin -= rhs;
199    }
200
201    /// Calculates the signed area of the polygon multiplied by 2
202    ///
203    /// This function calculates the signed area by summing the cross products
204    /// of adjacent edges. The result is multiplied by 2 to avoid the need for
205    /// floating-point arithmetic.
206    ///
207    /// # Returns
208    ///
209    /// The signed area of the polygon multiplied by 2
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// use physdes::point::Point;
215    /// use physdes::polygon::Polygon;
216    /// use physdes::vector2::Vector2;
217    ///
218    /// let p1 = Point::new(1, 1);
219    /// let p2 = Point::new(2, 2);
220    /// let p3 = Point::new(3, 3);
221    /// let p4 = Point::new(4, 4);
222    /// let p5 = Point::new(5, 5);
223    /// let poly = Polygon::new(&[p1, p2, p3, p4, p5]);
224    /// assert_eq!(poly.signed_area_x2(), 0);
225    /// ```
226    pub fn signed_area_x2(&self) -> T {
227        let vecs = &self.vecs;
228        let n = vecs.len();
229        if n < 2 {
230            return T::zero();
231        }
232
233        let mut itr = vecs.iter();
234        let vec0 = itr.next().unwrap();
235        let vec1 = itr.next().unwrap();
236        let mut res = vec0.x_ * vec1.y_ - vecs[n - 1].x_ * vecs[n - 2].y_;
237
238        let mut vec0 = vec0;
239        let mut vec1 = vec1;
240
241        for vec2 in itr {
242            res += vec1.x_ * (vec2.y_ - vec0.y_);
243            vec0 = vec1;
244            vec1 = vec2;
245        }
246
247        res
248    }
249
250    /// Gets all vertices of the polygon as points
251    pub fn get_vertices(&self) -> Vec<Point<T, T>> {
252        let mut result = Vec::with_capacity(self.vecs.len() + 1);
253        result.push(self.origin);
254
255        for vec in &self.vecs {
256            result.push(self.origin + *vec);
257        }
258
259        result
260    }
261
262    /// Gets the bounding box of the polygon
263    ///
264    /// # Returns
265    ///
266    /// A tuple of (min_point, max_point) representing the bounding box
267    ///
268    /// # Examples
269    ///
270    /// ```
271    /// use physdes::point::Point;
272    /// use physdes::polygon::Polygon;
273    ///
274    /// let points = vec![
275    ///     Point::new(0, 0),
276    ///     Point::new(2, 0),
277    ///     Point::new(2, 2),
278    ///     Point::new(0, 2),
279    /// ];
280    /// let poly = Polygon::new(&points);
281    /// let (min_pt, max_pt) = poly.bounding_box();
282    /// assert_eq!(min_pt, Point::new(0, 0));
283    /// assert_eq!(max_pt, Point::new(2, 2));
284    /// ```
285    pub fn bounding_box(&self) -> (Point<T, T>, Point<T, T>)
286    where
287        T: Ord + Copy,
288    {
289        let vertices = self.vertices();
290        let mut min_x = vertices[0].xcoord;
291        let mut min_y = vertices[0].ycoord;
292        let mut max_x = vertices[0].xcoord;
293        let mut max_y = vertices[0].ycoord;
294
295        for pt in &vertices {
296            if pt.xcoord < min_x {
297                min_x = pt.xcoord;
298            }
299            if pt.ycoord < min_y {
300                min_y = pt.ycoord;
301            }
302            if pt.xcoord > max_x {
303                max_x = pt.xcoord;
304            }
305            if pt.ycoord > max_y {
306                max_y = pt.ycoord;
307            }
308        }
309
310        (Point::new(min_x, min_y), Point::new(max_x, max_y))
311    }
312
313    /// Checks if the polygon is rectilinear
314    ///
315    /// A polygon is rectilinear if all its edges are either horizontal or vertical.
316    ///
317    /// # Returns
318    ///
319    /// `true` if the polygon is rectilinear, `false` otherwise
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// use physdes::point::Point;
325    /// use physdes::polygon::Polygon;
326    ///
327    /// let p1 = Point::new(0, 0);
328    /// let p2 = Point::new(0, 1);
329    /// let p3 = Point::new(1, 1);
330    /// let p4 = Point::new(1, 0);
331    /// let poly = Polygon::new(&[p1, p2, p3, p4]);
332    /// assert!(poly.is_rectilinear());
333    ///
334    /// let p5 = Point::new(0, 0);
335    /// let p6 = Point::new(1, 1);
336    /// let p7 = Point::new(0, 2);
337    /// let poly2 = Polygon::new(&[p5, p6, p7]);
338    /// assert!(!poly2.is_rectilinear());
339    /// ```
340    pub fn is_rectilinear(&self) -> bool {
341        if self.vecs.is_empty() {
342            return true;
343        }
344
345        // Check from origin to vecs[0]
346        if self.vecs[0].x_ != T::zero() && self.vecs[0].y_ != T::zero() {
347            return false;
348        }
349
350        // Check between vecs
351        for i in 0..self.vecs.len() - 1 {
352            let v1 = self.vecs[i];
353            let v2 = self.vecs[i + 1];
354            if v1.x_ != v2.x_ && v1.y_ != v2.y_ {
355                return false;
356            }
357        }
358
359        // Check from vecs[-1] to origin
360        let last_vec = self.vecs.last().unwrap();
361        last_vec.x_ == T::zero() || last_vec.y_ == T::zero()
362    }
363
364    /// Checks if the polygon is oriented anticlockwise
365    pub fn is_anticlockwise(&self) -> bool
366    where
367        T: PartialOrd,
368    {
369        let mut pointset = Vec::with_capacity(self.vecs.len() + 1);
370        pointset.push(Vector2::new(T::zero(), T::zero()));
371        pointset.extend(self.vecs.iter().cloned());
372
373        if pointset.len() < 3 {
374            panic!("Polygon must have at least 3 points");
375        }
376
377        // Find the point with minimum coordinates
378        let (min_index, _) = pointset
379            .iter()
380            .enumerate()
381            .min_by(|(_, a), (_, b)| {
382                a.x_.partial_cmp(&b.x_)
383                    .unwrap_or(Ordering::Equal)
384                    .then(a.y_.partial_cmp(&b.y_).unwrap_or(Ordering::Equal))
385            })
386            .unwrap();
387
388        // Get previous and next points with wrap-around
389        let n = pointset.len();
390        let prev_point = pointset[(min_index + n - 1) % n];
391        let current_point = pointset[min_index];
392        let next_point = pointset[(min_index + 1) % n];
393
394        // Calculate cross product
395        (current_point - prev_point).cross(&(next_point - current_point)) > T::zero()
396    }
397
398    /// Checks if the polygon is convex
399    ///
400    /// A polygon is convex if all its interior angles are less than 180 degrees
401    /// and no edges bend inward.
402    ///
403    /// # Returns
404    ///
405    /// `true` if the polygon is convex, `false` otherwise
406    ///
407    /// # Examples
408    ///
409    /// ```
410    /// use physdes::point::Point;
411    /// use physdes::polygon::Polygon;
412    ///
413    /// // Convex square
414    /// let convex_points = vec![
415    ///     Point::new(0, 0),
416    ///     Point::new(1, 0),
417    ///     Point::new(1, 1),
418    ///     Point::new(0, 1),
419    /// ];
420    /// let convex_poly = Polygon::new(&convex_points);
421    /// assert!(convex_poly.is_convex());
422    ///
423    /// // Concave polygon (L-shape)
424    /// let concave_points = vec![
425    ///     Point::new(0, 0),
426    ///     Point::new(2, 0),
427    ///     Point::new(2, 1),
428    ///     Point::new(1, 1),
429    ///     Point::new(1, 2),
430    ///     Point::new(0, 2),
431    /// ];
432    /// let concave_poly = Polygon::new(&concave_points);
433    /// assert!(!concave_poly.is_convex());
434    /// ```
435    pub fn is_convex(&self) -> bool
436    where
437        T: PartialOrd,
438    {
439        let n = self.vecs.len();
440        if n < 2 {
441            return false;
442        }
443        if n == 2 {
444            return true;
445        }
446
447        // Compute initial cross product sign using vecs[N-2] and vecs[0].
448        // In pointset terms: pointset[N-2] = vecs[N-2], pointset[1] = vecs[0].
449        // cross = -a.x*b.y + a.y*b.x  (rearranged to avoid unary negation)
450        let cross_product_sign =
451            self.vecs[n - 2].y_ * self.vecs[0].x_ - self.vecs[n - 2].x_ * self.vecs[0].y_;
452
453        for i in 0..n - 1 {
454            let v0 = if i == 0 {
455                Vector2::new(T::zero(), T::zero())
456            } else {
457                self.vecs[i - 1]
458            };
459            let v1 = self.vecs[i];
460            let v2 = self.vecs[i + 1];
461
462            let current_cross =
463                (v1.x_ - v0.x_) * (v2.y_ - v1.y_) - (v1.y_ - v0.y_) * (v2.x_ - v1.x_);
464
465            if (cross_product_sign > T::zero()) != (current_cross > T::zero()) {
466                return false;
467            }
468        }
469
470        true
471    }
472}
473
474/// Creates a monotone polygon from a set of points using a custom comparison function
475///
476/// # Arguments
477///
478/// * `pointset` - A slice of points to create the polygon from
479/// * `f` - A closure that defines the ordering of points
480pub fn create_mono_polygon<T, F>(pointset: &[Point<T, T>], func: F) -> Vec<Point<T, T>>
481where
482    T: Clone + Num + Ord + Copy + PartialOrd,
483    F: Fn(&Point<T, T>) -> (T, T),
484{
485    let max_pt = pointset
486        .iter()
487        .max_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
488        .unwrap();
489    let min_pt = pointset
490        .iter()
491        .min_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
492        .unwrap();
493    let diff = *max_pt - *min_pt;
494
495    let (mut lst1, mut lst2): (Vec<Point<T, T>>, Vec<Point<T, T>>) = pointset
496        .iter()
497        .partition(|&a| diff.cross(&(*a - *min_pt)) <= T::zero());
498
499    lst1.sort_by_key(|a| func(a));
500    lst2.sort_by_key(|a| func(a));
501    lst2.reverse();
502    lst1.append(&mut lst2);
503    lst1
504}
505
506/// Creates an x-monotone polygon from a set of points
507///
508/// Points are ordered primarily by x-coordinate, secondarily by y-coordinate
509///
510/// # Arguments
511///
512/// * `pointset` - A slice of points to order
513///
514/// # Returns
515///
516/// A vector of points ordered to form an x-monotone polygon
517///
518/// # Examples
519///
520/// ```
521/// use physdes::point::Point;
522/// use physdes::polygon::create_xmono_polygon;
523///
524/// let points = vec![
525///     Point::new(1, 1),
526///     Point::new(3, 2),
527///     Point::new(2, 0),
528///     Point::new(0, 2),
529/// ];
530/// let ordered = create_xmono_polygon(&points);
531/// // Result is ordered to create x-monotone polygon
532/// assert_eq!(ordered.len(), 4);
533/// ```
534#[inline]
535pub fn create_xmono_polygon<T>(pointset: &[Point<T, T>]) -> Vec<Point<T, T>>
536where
537    T: Clone + Num + Ord + Copy + PartialOrd,
538{
539    create_mono_polygon(pointset, |a| (a.xcoord, a.ycoord))
540}
541
542/// Creates a y-monotone polygon from a set of points
543///
544/// Points are ordered primarily by y-coordinate, secondarily by x-coordinate
545///
546/// # Arguments
547///
548/// * `pointset` - A slice of points to order
549///
550/// # Returns
551///
552/// A vector of points ordered to form a y-monotone polygon
553///
554/// # Examples
555///
556/// ```
557/// use physdes::point::Point;
558/// use physdes::polygon::create_ymono_polygon;
559///
560/// let points = vec![
561///     Point::new(1, 1),
562///     Point::new(2, 3),
563///     Point::new(0, 2),
564///     Point::new(2, 0),
565/// ];
566/// let ordered = create_ymono_polygon(&points);
567/// // Result is ordered to create y-monotone polygon
568/// assert_eq!(ordered.len(), 4);
569/// ```
570#[inline]
571pub fn create_ymono_polygon<T>(pointset: &[Point<T, T>]) -> Vec<Point<T, T>>
572where
573    T: Clone + Num + Ord + Copy + PartialOrd,
574{
575    create_mono_polygon(pointset, |a| (a.ycoord, a.xcoord))
576}
577
578/// Checks if a polygon is monotone in a given direction
579pub fn polygon_is_monotone<T, F>(lst: &[Point<T, T>], dir: F) -> bool
580where
581    T: Clone + Num + Ord + Copy + PartialOrd,
582    F: Fn(&Point<T, T>) -> (T, T),
583{
584    if lst.len() <= 3 {
585        return true;
586    }
587
588    let (min_index, _) = lst
589        .iter()
590        .enumerate()
591        .min_by(|(_, a), (_, b)| dir(a).partial_cmp(&dir(b)).unwrap())
592        .unwrap();
593
594    let (max_index, _) = lst
595        .iter()
596        .enumerate()
597        .max_by(|(_, a), (_, b)| dir(a).partial_cmp(&dir(b)).unwrap())
598        .unwrap();
599
600    let n = lst.len();
601
602    // Chain from min to max
603    let mut i = min_index;
604    while i != max_index {
605        let next_i = (i + 1) % n;
606        if dir(&lst[i]).0 > dir(&lst[next_i]).0 {
607            return false;
608        }
609        i = next_i;
610    }
611
612    // Chain from max to min
613    let mut i = max_index;
614    while i != min_index {
615        let next_i = (i + 1) % n;
616        if dir(&lst[i]).0 < dir(&lst[next_i]).0 {
617            return false;
618        }
619        i = next_i;
620    }
621
622    true
623}
624
625/// Checks if a polygon is x-monotone
626pub fn polygon_is_xmonotone<T>(lst: &[Point<T, T>]) -> bool
627where
628    T: Clone + Num + Ord + Copy + PartialOrd,
629{
630    polygon_is_monotone(lst, |pt| (pt.xcoord, pt.ycoord))
631}
632
633/// Checks if a polygon is y-monotone
634pub fn polygon_is_ymonotone<T>(lst: &[Point<T, T>]) -> bool
635where
636    T: Clone + Num + Ord + Copy + PartialOrd,
637{
638    polygon_is_monotone(lst, |pt| (pt.ycoord, pt.xcoord))
639}
640
641/// Determines if a point is inside a polygon using the winding number algorithm
642pub fn point_in_polygon<T>(pointset: &[Point<T, T>], ptq: &Point<T, T>) -> bool
643where
644    T: Clone + Num + Ord + Copy + PartialOrd,
645{
646    let n = pointset.len();
647    if n == 0 {
648        return false;
649    }
650
651    let mut pt0 = &pointset[n - 1];
652    let mut res = false;
653
654    for pt1 in pointset.iter() {
655        if (pt1.ycoord <= ptq.ycoord && ptq.ycoord < pt0.ycoord)
656            || (pt0.ycoord <= ptq.ycoord && ptq.ycoord < pt1.ycoord)
657        {
658            let det = (*ptq - *pt0).cross(&(*pt1 - *pt0));
659            if pt1.ycoord > pt0.ycoord {
660                if det < T::zero() {
661                    res = !res;
662                }
663            } else if det > T::zero() {
664                res = !res;
665            }
666        }
667        pt0 = pt1;
668    }
669
670    res
671}
672
673/// Determines if a polygon represented by points is oriented anticlockwise
674pub fn polygon_is_anticlockwise<T>(pointset: &[Point<T, T>]) -> bool
675where
676    T: Clone + Num + Ord + Copy + PartialOrd,
677{
678    if pointset.len() < 3 {
679        panic!("Polygon must have at least 3 points");
680    }
681
682    // Find the point with minimum coordinates
683    let (min_index, _) = pointset
684        .iter()
685        .enumerate()
686        .min_by(|(_, a), (_, b)| {
687            a.xcoord
688                .partial_cmp(&b.xcoord)
689                .unwrap_or(Ordering::Equal)
690                .then(a.ycoord.partial_cmp(&b.ycoord).unwrap_or(Ordering::Equal))
691        })
692        .unwrap();
693
694    // Get previous and next points with wrap-around
695    let n = pointset.len();
696    let prev_point = pointset[(min_index + n - 1) % n];
697    let current_point = pointset[min_index];
698    let next_point = pointset[(min_index + 1) % n];
699
700    // Calculate cross product
701    (current_point - prev_point).cross(&(next_point - current_point)) > T::zero()
702}
703
704// Implement PartialEq for Polygon
705impl<T: PartialEq> PartialEq for Polygon<T> {
706    fn eq(&self, other: &Self) -> bool {
707        self.origin == other.origin && self.vecs == other.vecs
708    }
709}
710
711// Implement AddAssign and SubAssign for Polygon
712impl<T: AddAssign + Clone + Num> AddAssign<Vector2<T, T>> for Polygon<T> {
713    fn add_assign(&mut self, rhs: Vector2<T, T>) {
714        self.origin += rhs;
715    }
716}
717
718impl<T: SubAssign + Clone + Num> SubAssign<Vector2<T, T>> for Polygon<T> {
719    fn sub_assign(&mut self, rhs: Vector2<T, T>) {
720        self.origin -= rhs;
721    }
722}
723
724#[cfg(test)]
725mod tests {
726    use super::*;
727    use crate::point::Point;
728    use crate::vector2::Vector2;
729
730    #[test]
731    fn test_polygon() {
732        let coords = [
733            (-2, 2),
734            (0, -1),
735            (-5, 1),
736            (-2, 4),
737            (0, -4),
738            (-4, 3),
739            (-6, -2),
740            (5, 1),
741            (2, 2),
742            (3, -3),
743            (-3, -3),
744            (3, 3),
745            (-3, -4),
746            (1, 4),
747        ];
748
749        let mut pointset = Vec::new();
750        for (x_coord, y_coord) in coords.iter() {
751            pointset.push(Point::new(*x_coord, *y_coord));
752        }
753
754        let poly_points = create_xmono_polygon(&pointset);
755        assert!(polygon_is_anticlockwise(&poly_points));
756
757        let poly = Polygon::from_pointset(&poly_points);
758        let mut poly2 = Polygon::from_pointset(&poly_points);
759        poly2.add_assign(Vector2::new(4, 5));
760        poly2.sub_assign(Vector2::new(4, 5));
761        assert_eq!(poly2, poly);
762    }
763
764    #[test]
765    fn test_ymono_polygon() {
766        let coords = [
767            (-2, 2),
768            (0, -1),
769            (-5, 1),
770            (-2, 4),
771            (0, -4),
772            (-4, 3),
773            (-6, -2),
774            (5, 1),
775            (2, 2),
776            (3, -3),
777            (-3, -3),
778            (3, 3),
779            (-3, -4),
780            (1, 4),
781        ];
782
783        let mut pointset = Vec::new();
784        for (x_coord, y_coord) in coords.iter() {
785            pointset.push(Point::new(*x_coord, *y_coord));
786        }
787
788        let poly_points = create_ymono_polygon(&pointset);
789        assert!(polygon_is_ymonotone(&poly_points));
790        assert!(!polygon_is_xmonotone(&poly_points));
791        assert!(polygon_is_anticlockwise(&poly_points));
792
793        let poly = Polygon::from_pointset(&poly_points);
794        assert_eq!(poly.signed_area_x2(), 102);
795        assert!(poly.is_anticlockwise());
796    }
797
798    #[test]
799    fn test_xmono_polygon() {
800        let coords = [
801            (-2, 2),
802            (0, -1),
803            (-5, 1),
804            (-2, 4),
805            (0, -4),
806            (-4, 3),
807            (-6, -2),
808            (5, 1),
809            (2, 2),
810            (3, -3),
811            (-3, -3),
812            (3, 3),
813            (-3, -4),
814            (1, 4),
815        ];
816
817        let mut pointset = Vec::new();
818        for (x_coord, y_coord) in coords.iter() {
819            pointset.push(Point::new(*x_coord, *y_coord));
820        }
821
822        let poly_points = create_xmono_polygon(&pointset);
823        assert!(polygon_is_xmonotone(&poly_points));
824        assert!(!polygon_is_ymonotone(&poly_points));
825        assert!(polygon_is_anticlockwise(&poly_points));
826
827        let poly = Polygon::from_pointset(&poly_points);
828        assert_eq!(poly.signed_area_x2(), 111);
829        assert!(poly.is_anticlockwise());
830    }
831
832    #[test]
833    fn test_is_rectilinear() {
834        // Create a rectilinear polygon
835        let rectilinear_coords = [(0, 0), (0, 1), (1, 1), (1, 0)];
836        let rectilinear_points: Vec<Point<i32, i32>> = rectilinear_coords
837            .iter()
838            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
839            .collect();
840        let rectilinear_polygon = Polygon::from_pointset(&rectilinear_points);
841        assert!(rectilinear_polygon.is_rectilinear());
842
843        // Create a non-rectilinear polygon
844        let non_rectilinear_coords = [(0, 0), (1, 1), (2, 0)];
845        let non_rectilinear_points: Vec<Point<i32, i32>> = non_rectilinear_coords
846            .iter()
847            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
848            .collect();
849        let non_rectilinear_polygon = Polygon::from_pointset(&non_rectilinear_points);
850        assert!(!non_rectilinear_polygon.is_rectilinear());
851    }
852
853    #[test]
854    fn test_is_convex() {
855        // Test case 1: Convex polygon
856        let convex_coords = [(0, 0), (2, 0), (2, 2), (0, 2)];
857        let convex_points: Vec<Point<i32, i32>> = convex_coords
858            .iter()
859            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
860            .collect();
861        let convex_polygon = Polygon::from_pointset(&convex_points);
862        assert!(convex_polygon.is_convex());
863
864        // Test case 2: Non-convex polygon
865        let non_convex_coords = [(0, 0), (2, 0), (1, 1), (2, 2), (0, 2)];
866        let non_convex_points: Vec<Point<i32, i32>> = non_convex_coords
867            .iter()
868            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
869            .collect();
870        let non_convex_polygon = Polygon::from_pointset(&non_convex_points);
871        assert!(!non_convex_polygon.is_convex());
872
873        // Test case 3: Triangle (always convex)
874        let triangle_coords = [(0, 0), (2, 0), (1, 2)];
875        let triangle_points: Vec<Point<i32, i32>> = triangle_coords
876            .iter()
877            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
878            .collect();
879        let triangle = Polygon::from_pointset(&triangle_points);
880        assert!(triangle.is_convex());
881    }
882
883    #[test]
884    fn test_is_anticlockwise() {
885        // Clockwise polygon
886        let clockwise_coords = [(0, 0), (0, 1), (1, 1), (1, 0)];
887        let clockwise_points: Vec<Point<i32, i32>> = clockwise_coords
888            .iter()
889            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
890            .collect();
891        let clockwise_polygon = Polygon::from_pointset(&clockwise_points);
892        assert!(!clockwise_polygon.is_anticlockwise());
893
894        // Counter-clockwise polygon
895        let counter_clockwise_coords = [(0, 0), (1, 0), (1, 1), (0, 1)];
896        let counter_clockwise_points: Vec<Point<i32, i32>> = counter_clockwise_coords
897            .iter()
898            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
899            .collect();
900        let counter_clockwise_polygon = Polygon::from_pointset(&counter_clockwise_points);
901        assert!(counter_clockwise_polygon.is_anticlockwise());
902    }
903
904    #[test]
905    fn test_is_convex_clockwise() {
906        // Convex clockwise polygon
907        let convex_coords = [(0, 0), (0, 2), (2, 2), (2, 0)];
908        let convex_points: Vec<Point<i32, i32>> = convex_coords
909            .iter()
910            .map(|(x, y)| Point::new(*x, *y))
911            .collect();
912        let convex_polygon = Polygon::from_pointset(&convex_points);
913        assert!(convex_polygon.is_convex());
914
915        // Non-convex clockwise polygon
916        let non_convex_coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)];
917        let non_convex_points: Vec<Point<i32, i32>> = non_convex_coords
918            .iter()
919            .map(|(x, y)| Point::new(*x, *y))
920            .collect();
921        let non_convex_polygon = Polygon::from_pointset(&non_convex_points);
922        assert!(!non_convex_polygon.is_convex());
923    }
924
925    #[test]
926    fn test_point_in_polygon_missed_branches() {
927        let coords = [(0, 0), (10, 0), (10, 10), (0, 10)];
928        let pointset: Vec<Point<i32, i32>> = coords
929            .iter()
930            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
931            .collect();
932
933        // Test case where ptq.ycoord == pt0.ycoord
934        assert!(!point_in_polygon(&pointset, &Point::new(5, 10)));
935
936        // Test case where ptq.ycoord == pt1.ycoord
937        assert!(point_in_polygon(&pointset, &Point::new(5, 0)));
938
939        // Test case where det == 0 (point on edge)
940        assert!(point_in_polygon(&pointset, &Point::new(5, 0)));
941    }
942
943    #[test]
944    #[should_panic(expected = "Polygon must have at least 3 points")]
945    fn test_polygon_is_anticlockwise_less_than_3_points() {
946        let coords = [(0, 0), (0, 1)];
947        let points: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
948        polygon_is_anticlockwise(&points);
949    }
950
951    #[test]
952    #[should_panic(expected = "Polygon must have at least 3 points")]
953    fn test_is_anticlockwise_less_than_3_points() {
954        let coords = [(0, 0), (0, 1)];
955        let points: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
956        let polygon = Polygon::from_pointset(&points);
957        polygon.is_anticlockwise();
958    }
959
960    #[test]
961    fn test_is_convex_more() {
962        // Non-convex anti-clockwise polygon
963        let non_convex_coords = [(0, 0), (2, 0), (1, 1), (2, 2), (0, 2)];
964        let non_convex_points: Vec<Point<i32, i32>> = non_convex_coords
965            .iter()
966            .map(|(x, y)| Point::new(*x, *y))
967            .collect();
968        let non_convex_polygon = Polygon::from_pointset(&non_convex_points);
969        assert!(!non_convex_polygon.is_convex());
970
971        // Convex anti-clockwise polygon
972        let convex_coords = [(0, 0), (2, 0), (2, 2), (0, 2)];
973        let convex_points: Vec<Point<i32, i32>> = convex_coords
974            .iter()
975            .map(|(x, y)| Point::new(*x, *y))
976            .collect();
977        let convex_polygon = Polygon::from_pointset(&convex_points);
978        assert!(convex_polygon.is_convex());
979    }
980
981    #[test]
982    fn test_point_in_polygon_more() {
983        // Create a polygon that will trigger the missed branches
984        let coords = [(0, 0), (10, 5), (0, 10)];
985        let pointset: Vec<Point<i32, i32>> = coords
986            .iter()
987            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
988            .collect();
989
990        // This should trigger `det > 0`
991        assert!(point_in_polygon(&pointset, &Point::new(1, 5)));
992
993        // Create a clockwise polygon to trigger `det < 0`
994        let coords_cw = [(0, 0), (0, 10), (10, 5)];
995        let pointset_cw: Vec<Point<i32, i32>> = coords_cw
996            .iter()
997            .map(|(x_coord, y_coord)| Point::new(*x_coord, *y_coord))
998            .collect();
999        assert!(point_in_polygon(&pointset_cw, &Point::new(1, 5)));
1000    }
1001}
1002
1003#[test]
1004fn test_polygon_signed_area_x2() {
1005    // Test signed_area_x2 for different polygons
1006    let coords = [(0, 0), (4, 0), (4, 3), (0, 3)];
1007    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1008    let poly = Polygon::from_pointset(&pointset);
1009    assert_eq!(poly.signed_area_x2(), 24); // 2 * (4*3) = 24
1010}
1011
1012#[test]
1013fn test_polygon_vertices() {
1014    let coords = [(0, 0), (4, 0), (4, 3), (0, 3)];
1015    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1016    let poly = Polygon::from_pointset(&pointset);
1017    let vertices = poly.vertices();
1018    assert_eq!(vertices.len(), 4);
1019}
1020
1021#[test]
1022fn test_polygon_bounding_box() {
1023    let coords = [(1, 1), (5, 1), (5, 4), (1, 4)];
1024    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1025    let poly = Polygon::from_pointset(&pointset);
1026    let (min, max) = poly.bounding_box();
1027    assert_eq!(min, Point::new(1, 1));
1028    assert_eq!(max, Point::new(5, 4));
1029}
1030
1031#[test]
1032fn test_polygon_add_assign() {
1033    let coords = [(0, 0), (4, 0), (4, 3), (0, 3)];
1034    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1035    let mut poly = Polygon::from_pointset(&pointset);
1036    poly.add_assign(Vector2::new(1, 2));
1037    assert_eq!(poly.origin, Point::new(1, 2));
1038}
1039
1040#[test]
1041fn test_polygon_sub_assign() {
1042    let coords = [(1, 2), (5, 2), (5, 5), (1, 5)];
1043    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1044    let mut poly = Polygon::from_pointset(&pointset);
1045    poly.sub_assign(Vector2::new(1, 2));
1046    assert_eq!(poly.origin, Point::new(0, 0));
1047}
1048
1049#[test]
1050fn test_polygon_partial_eq() {
1051    let coords = [(0, 0), (4, 0), (4, 3), (0, 3)];
1052    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1053    let poly1 = Polygon::from_pointset(&pointset);
1054    let poly2 = Polygon::from_pointset(&pointset);
1055    assert_eq!(poly1, poly2);
1056
1057    let coords2 = [(0, 0), (5, 0), (5, 3), (0, 3)];
1058    let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1059    let poly3 = Polygon::from_pointset(&pointset2);
1060    assert_ne!(poly1, poly3);
1061}
1062
1063#[test]
1064fn test_polygon_from_origin_and_vectors() {
1065    let origin = Point::new(0, 0);
1066    let vecs = vec![Vector2::new(4, 0), Vector2::new(0, 3), Vector2::new(-4, 0)];
1067    let poly = Polygon::from_origin_and_vectors(origin, vecs);
1068    assert_eq!(poly.origin, Point::new(0, 0));
1069    assert_eq!(poly.vecs.len(), 3);
1070}
1071
1072#[test]
1073fn test_polygon_default() {
1074    let poly: Polygon<i32> = Polygon::default();
1075    assert_eq!(poly.origin, Point::new(0, 0));
1076}
1077
1078#[test]
1079fn test_polygon_is_monotone_custom() {
1080    // Test polygon_is_monotone with custom direction
1081    let coords = [(0, 0), (1, 0), (2, 1), (1, 2), (0, 2)];
1082    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1083    // x-monotone
1084    assert!(polygon_is_monotone(&pointset, |pt| (pt.xcoord, pt.ycoord)));
1085}
1086
1087#[test]
1088fn test_create_mono_polygon_custom() {
1089    // Test create_mono_polygon with custom function
1090    let coords = [(0, 0), (2, 1), (1, 2), (3, 2)];
1091    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1092    let result = create_mono_polygon(&pointset, |pt| (pt.xcoord, pt.ycoord));
1093    assert!(!result.is_empty());
1094}
1095
1096#[test]
1097fn test_polygon_empty_vecs_is_rectilinear() {
1098    // Edge case: polygon with no vecs should be rectilinear
1099    let origin = Point::new(0, 0);
1100    let poly = Polygon::from_origin_and_vectors(origin, vec![]);
1101    assert!(poly.is_rectilinear());
1102}
1103
1104#[test]
1105fn test_polygon_signed_area_x2_triangle() {
1106    // Triangle area calculation
1107    let coords = [(0, 0), (3, 0), (0, 4)];
1108    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
1109    let poly = Polygon::from_pointset(&pointset);
1110    // Area = 0.5 * |0*(0-4) + 3*(4-0) + 0*(0-0)| = 0.5 * 12 = 6
1111    // signed_area_x2 = 2 * area = 12
1112    assert_eq!(poly.signed_area_x2(), 12);
1113}