Skip to main content

physdes/
rpolygon.rs

1#![allow(clippy::type_complexity)]
2
3use num_traits::Num;
4use std::cmp::Ord;
5use std::cmp::Ordering;
6use std::ops::{AddAssign, SubAssign};
7
8use crate::point::Point;
9use crate::vector2::Vector2;
10
11/// The `RPolygon` struct represents a rectilinear polygon with an origin point and a vector of 2D
12/// vectors.
13///
14/// ```svgbob
15///    *-----*-----*
16///    |           |
17///    |           *-----*
18///    |                 |
19///    *-----------------*
20///    |
21///    *-> origin
22/// ```
23///
24/// Properties:
25///
26/// * `origin`: The origin property represents the starting point or the reference point of the
27///   rectilinear polygon. It is of type `Point<T, T>`, where T is the type of the coordinates of the point
28///   (e.g., integer or floating-point).
29/// * `vecs`: vecs is a vector that stores the vectors representing the sides of the rectilinear
30///   polygon.
31///
32/// # Examples
33///
34/// ```
35/// use physdes::point::Point;
36/// use physdes::rpolygon::RPolygon;
37/// use physdes::vector2::Vector2;
38///
39/// let origin = Point::new(0, 0);
40/// let vecs = vec![Vector2::new(1, 0), Vector2::new(1, 1), Vector2::new(0, 1)];
41/// let poly = RPolygon::from_origin_and_vectors(origin, vecs);
42/// assert_eq!(poly.origin, Point::new(0, 0));
43/// // Note: vecs field is private, so we can't access it directly in documentation
44/// ```
45#[derive(Eq, Clone, Debug, Default)]
46pub struct RPolygon<T> {
47    pub origin: Point<T, T>,
48    vecs: Vec<Vector2<T, T>>,
49}
50
51impl<T: Clone + Num + Copy + std::ops::AddAssign + Ord> RPolygon<T> {
52    /// The `new` function constructs a new `RPolygon` object by calculating the origin and vectors
53    /// based on the given coordinates.
54    ///
55    /// Arguments:
56    ///
57    /// * `coords`: The `coords` parameter is an array of `Point<T, T>` objects. It represents the
58    ///   coordinates of the points that define the polygon. The first element of the array (`coords[0]`)
59    ///   is considered as the origin of the polygon, and the remaining elements represent the vectors
60    ///   from the origin to the
61    ///
62    /// Returns:
63    ///
64    /// The `new` function is returning an instance of the `RPolygon` struct.
65    pub fn new(coords: &[Point<T, T>]) -> Self {
66        // let origin = coords[0];
67        // let mut vecs = vec![];
68        // for pt in coords.iter().skip(1) {
69        //     vecs.push(pt - origin);
70        // }
71        let (&origin, coords) = coords.split_first().unwrap();
72        let vecs = coords.iter().map(|pt| *pt - origin).collect();
73        RPolygon { origin, vecs }
74    }
75
76    /// Constructs a new Polygon from origin and displacement vectors
77    ///
78    /// # Arguments
79    ///
80    /// * `origin` - The origin point of the polygon
81    /// * `vecs` - Vector of displacement vectors from origin
82    pub fn from_origin_and_vectors(origin: Point<T, T>, vecs: Vec<Vector2<T, T>>) -> Self {
83        RPolygon { origin, vecs }
84    }
85
86    /// Constructs a new Polygon from a point set
87    ///
88    /// The first point in the set is used as the origin, and the remaining points
89    /// are used to construct displacement vectors relative to the origin.
90    pub fn from_pointset(pointset: &[Point<T, T>]) -> Self {
91        Self::new(pointset)
92    }
93
94    /// Translates the polygon by adding a vector to its origin
95    pub fn add_assign(&mut self, rhs: Vector2<T, T>)
96    where
97        T: AddAssign,
98    {
99        self.origin += rhs;
100    }
101
102    /// Translates the polygon by subtracting a vector from its origin
103    pub fn sub_assign(&mut self, rhs: Vector2<T, T>)
104    where
105        T: SubAssign,
106    {
107        self.origin -= rhs;
108    }
109
110    /// The `signed_area` function calculates the signed area of a polygon.
111    ///
112    /// Returns:
113    ///
114    /// The function `signed_area` returns a value of type `T`.
115    pub fn signed_area(&self) -> T {
116        // assert!(self.vecs.len() >= 1);
117        // let (mut vec0, vecs) = self.vecs.split_first().unwrap();
118        let mut itr = self.vecs.iter();
119        let mut vec0 = itr.next().unwrap();
120        let mut res = vec0.x_ * vec0.y_;
121        for vec1 in itr {
122            res += vec1.x_ * (vec1.y_ - vec0.y_);
123            vec0 = vec1;
124        }
125        res
126    }
127
128    /// Gets all vertices of the polygon as points
129    pub fn vertices(&self) -> Vec<Point<T, T>> {
130        let mut result = Vec::with_capacity(self.vecs.len() + 1);
131        result.push(self.origin);
132
133        for vec in &self.vecs {
134            result.push(self.origin + *vec);
135        }
136
137        result
138    }
139
140    /// Gets the bounding box of the polygon
141    pub fn bounding_box(&self) -> (Point<T, T>, Point<T, T>) {
142        let mut min_x = T::zero();
143        let mut min_y = T::zero();
144        let mut max_x = T::zero();
145        let mut max_y = T::zero();
146
147        for vec in &self.vecs {
148            if vec.x_ < min_x {
149                min_x = vec.x_;
150            }
151            if vec.y_ < min_y {
152                min_y = vec.y_;
153            }
154            if vec.x_ > max_x {
155                max_x = vec.x_;
156            }
157            if vec.y_ > max_y {
158                max_y = vec.y_;
159            }
160        }
161
162        (
163            Point::new(self.origin.xcoord + min_x, self.origin.ycoord + min_y),
164            Point::new(self.origin.xcoord + max_x, self.origin.ycoord + max_y),
165        )
166    }
167
168    /// Checks if the polygon is rectilinear
169    ///
170    /// A polygon is rectilinear if all its edges are either horizontal or vertical.
171    ///
172    /// # Returns
173    ///
174    /// `true` if the polygon is rectilinear, `false` otherwise
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use physdes::point::Point;
180    /// use physdes::rpolygon::RPolygon;
181    ///
182    /// let p1 = Point::new(0, 0);
183    /// let p2 = Point::new(0, 1);
184    /// let p3 = Point::new(1, 1);
185    /// let p4 = Point::new(1, 0);
186    /// let poly = RPolygon::new(&[p1, p2, p3, p4]);
187    /// assert!(poly.is_rectilinear());
188    ///
189    /// let p5 = Point::new(0, 0);
190    /// let p6 = Point::new(1, 1);
191    /// let p7 = Point::new(0, 2);
192    /// let poly2 = RPolygon::new(&[p5, p6, p7]);
193    /// assert!(poly2.is_rectilinear());
194    /// ```
195    pub fn is_rectilinear(&self) -> bool {
196        true
197    }
198
199    /// Checks if the polygon is oriented anticlockwise
200    pub fn is_anticlockwise(&self) -> bool
201    where
202        T: PartialOrd,
203    {
204        let mut pointset = Vec::with_capacity(self.vecs.len() + 1);
205        pointset.push(Vector2::new(T::zero(), T::zero()));
206        pointset.extend(self.vecs.iter().cloned());
207
208        if pointset.len() < 2 {
209            panic!("Polygon must have at least 2 points");
210        }
211
212        // Find the point with minimum coordinates
213        let (min_index, _) = pointset
214            .iter()
215            .enumerate()
216            .min_by(|(_, a), (_, b)| {
217                a.x_.partial_cmp(&b.x_)
218                    .unwrap_or(Ordering::Equal)
219                    .then(a.y_.partial_cmp(&b.y_).unwrap_or(Ordering::Equal))
220            })
221            .unwrap();
222
223        // Get previous and next points with wrap-around
224        let n = pointset.len();
225        let prev_point = pointset[(min_index + n - 1) % n];
226        let current_point = pointset[min_index];
227
228        prev_point.y_ > current_point.y_
229    }
230}
231
232// Implement PartialEq for RPolygon
233impl<T: PartialEq> PartialEq for RPolygon<T> {
234    fn eq(&self, other: &Self) -> bool {
235        self.origin == other.origin && self.vecs == other.vecs
236    }
237}
238
239impl<T: Clone + Num + Ord + Copy> RPolygon<T> {
240    /// The `create_mono_rpolygon` function creates a monotone polygon from a given set of points based
241    /// on a provided comparison function.
242    ///
243    /// Arguments:
244    ///
245    /// * `pointset`: `pointset` is a slice of `Point<T, T>` elements. It represents a set of points in a
246    ///   two-dimensional space.
247    /// * `f`: The parameter `f` is a closure that takes a reference to a reference of a `Point<T, T>` and
248    ///   returns a tuple of two values of type `T`. The closure is used to determine the ordering of the
249    ///   points in the `pointset`. The first value of the tuple represents the x-coordinate
250    pub fn create_mono_rpolygon<F>(pointset: &[Point<T, T>], func: F) -> (Vec<Point<T, T>>, bool)
251    where
252        F: Fn(&Point<T, T>) -> (T, T),
253    {
254        // Use x-mono as model
255        let rightmost = pointset
256            .iter()
257            .max_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
258            .unwrap();
259        let leftmost = pointset
260            .iter()
261            .min_by(|a, b| func(a).partial_cmp(&func(b)).unwrap())
262            .unwrap();
263        let is_anticlockwise = func(rightmost).1 <= func(leftmost).1;
264        let (mut lst1, mut lst2): (Vec<Point<T, T>>, Vec<Point<T, T>>) = if is_anticlockwise {
265            pointset
266                .iter()
267                .partition(|pt| func(pt).1 <= func(leftmost).1)
268        } else {
269            pointset
270                .iter()
271                .partition(|pt| func(pt).1 >= func(leftmost).1)
272        };
273        lst1.sort_by_key(|a| func(a));
274        lst2.sort_by_key(|a| func(a));
275        lst2.reverse();
276        lst1.append(&mut lst2);
277        (lst1, is_anticlockwise) // is_clockwise if y-mono
278    }
279
280    /// The function `create_xmono_rpolygon` creates a monotone RPolygon object using a given point set,
281    /// with the x-coordinate as the primary sorting criterion.
282    ///
283    /// Arguments:
284    ///
285    /// * `pointset`: A slice of Point objects
286    #[inline]
287    pub fn create_xmono_rpolygon(pointset: &[Point<T, T>]) -> (Vec<Point<T, T>>, bool) {
288        Self::create_mono_rpolygon(pointset, |a| (a.xcoord, a.ycoord))
289    }
290
291    /// The function `create_ymono_rpolygon` creates a y-monotone RPolygon object from a given point
292    /// set.
293    ///
294    /// Arguments:
295    ///
296    /// * `pointset`: A slice of Point objects, where each Point object has two fields: ycoord and
297    ///   xcoord.
298    #[inline]
299    pub fn create_ymono_rpolygon(pointset: &[Point<T, T>]) -> (Vec<Point<T, T>>, bool) {
300        Self::create_mono_rpolygon(pointset, |a| (a.ycoord, a.xcoord))
301    }
302
303    /// The function `point_in_rpolygon` determines if a given point is within a polygon.
304    ///
305    /// The code below is from Wm. Randolph Franklin <wrf@ecse.rpi.edu>
306    /// (see URL below) with some minor modifications for integer. It returns
307    /// true for strictly interior points, false for strictly exterior, and ub
308    /// for points on the boundary.  The boundary behavior is complex but
309    /// determined; in particular, for a partition of a region into polygons,
310    /// each Point is "in" exactly one Polygon.
311    /// (See p.243 of [O'Rourke (C)] for a discussion of boundary behavior.)
312    ///
313    /// See <http://www.faqs.org/faqs/graphics/algorithms-faq/> Subject 2.03
314    ///
315    /// Arguments:
316    ///
317    /// * `pointset`: A slice of points representing the vertices of the polygon. Each point has x and y
318    ///   coordinates.
319    /// * `q`: The parameter `q` represents the point that we want to determine if it is within the
320    ///   polygon or not.
321    ///
322    /// Returns:
323    ///
324    /// The function `point_in_polygon` returns a boolean value. It returns `true` if the given point
325    /// `q` is strictly inside the polygon defined by the `pointset` array, `false` if the point is
326    /// strictly outside the polygon, and `ub` (undefined behavior) if the point lies on the boundary of
327    /// the polygon.
328    pub fn point_in_rpolygon(pointset: &[Point<T, T>], query_pt: &Point<T, T>) -> bool {
329        let mut result = false;
330        let n = pointset.len();
331        let mut pt0 = &pointset[n - 1];
332        for pt1 in pointset.iter() {
333            if ((pt1.ycoord <= query_pt.ycoord && query_pt.ycoord < pt0.ycoord)
334                || (pt0.ycoord <= query_pt.ycoord && query_pt.ycoord < pt1.ycoord))
335                && pt1.xcoord > query_pt.xcoord
336            {
337                result = !result;
338            }
339            pt0 = pt1;
340        }
341        result
342    }
343}
344
345/// Checks if a polygon is monotone in a given direction
346pub fn rpolygon_is_monotone<T, F>(lst: &[Point<T, T>], dir: F) -> bool
347where
348    T: Clone + Num + Ord + Copy + PartialOrd,
349    F: Fn(&Point<T, T>) -> (T, T),
350{
351    if lst.len() <= 3 {
352        return true;
353    }
354
355    let (min_index, _) = lst
356        .iter()
357        .enumerate()
358        .min_by_key(|(_, pt)| dir(pt))
359        .unwrap();
360
361    let (max_index, _) = lst
362        .iter()
363        .enumerate()
364        .max_by_key(|(_, pt)| dir(pt))
365        .unwrap();
366
367    let n = lst.len();
368
369    // Chain from min to max
370    let mut i = min_index;
371    while i != max_index {
372        let next_i = (i + 1) % n;
373        if dir(&lst[i]).0 > dir(&lst[next_i]).0 {
374            return false;
375        }
376        i = next_i;
377    }
378
379    // Chain from max to min
380    let mut i = max_index;
381    while i != min_index {
382        let next_i = (i + 1) % n;
383        if dir(&lst[i]).0 < dir(&lst[next_i]).0 {
384            return false;
385        }
386        i = next_i;
387    }
388
389    true
390}
391
392/// Checks if a polygon is x-monotone
393pub fn rpolygon_is_xmonotone<T>(lst: &[Point<T, T>]) -> bool
394where
395    T: Clone + Num + Ord + Copy + PartialOrd,
396{
397    rpolygon_is_monotone(lst, |pt| (pt.xcoord, pt.ycoord))
398}
399
400/// Checks if a polygon is y-monotone
401pub fn rpolygon_is_ymonotone<T>(lst: &[Point<T, T>]) -> bool
402where
403    T: Clone + Num + Ord + Copy + PartialOrd,
404{
405    rpolygon_is_monotone(lst, |pt| (pt.ycoord, pt.xcoord))
406}
407
408/// Checks if a polygon is rectilinearly convex
409pub fn rpolygon_is_convex<T>(lst: &[Point<T, T>]) -> bool
410where
411    T: Clone + Num + Ord + Copy + PartialOrd,
412{
413    rpolygon_is_xmonotone(lst) && rpolygon_is_ymonotone(lst)
414}
415
416/// Determines if a polygon represented by points is oriented anticlockwise
417pub fn rpolygon_is_anticlockwise<T>(pointset: &[Point<T, T>]) -> bool
418where
419    T: Clone + Num + Ord + Copy + PartialOrd,
420{
421    if pointset.len() < 2 {
422        panic!("Polygon must have at least 2 points");
423    }
424
425    // Find the point with minimum coordinates
426    let (min_index, min_point) = pointset
427        .iter()
428        .enumerate()
429        .min_by(|(_, a), (_, b)| {
430            a.xcoord
431                .partial_cmp(&b.xcoord)
432                .unwrap_or(Ordering::Equal)
433                .then(a.ycoord.partial_cmp(&b.ycoord).unwrap_or(Ordering::Equal))
434        })
435        .unwrap();
436
437    // Get previous and next points with wrap-around
438    let n = pointset.len();
439    let prev_index = (min_index + n - 1) % n;
440
441    let prev_point = pointset[prev_index];
442    let current_point = *min_point;
443
444    prev_point.ycoord() > current_point.ycoord()
445}
446
447#[cfg(test)]
448mod test {
449    #![allow(non_upper_case_globals)]
450
451    use super::*;
452
453    #[test]
454    pub fn test_ymono_rpolygon() {
455        let coords = [
456            (-2, 2),
457            (0, -1),
458            (-5, 1),
459            (-2, 4),
460            (0, -4),
461            (-4, 3),
462            (-6, -2),
463            (5, 1),
464            (2, 2),
465            (3, -3),
466            (-3, -4),
467            (1, 4),
468        ];
469        let mut pointset = vec![];
470        for (x_coord, y_coord) in coords.iter() {
471            pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
472        }
473        let (poly_points, is_cw) = RPolygon::<i32>::create_ymono_rpolygon(&pointset);
474        assert!(rpolygon_is_anticlockwise(&poly_points));
475        assert!(rpolygon_is_ymonotone(&poly_points));
476        assert!(!rpolygon_is_xmonotone(&poly_points));
477        for pt in poly_points.iter() {
478            print!("({}, {}) ", pt.xcoord, pt.ycoord);
479        }
480        let poly = RPolygon::<i32>::new(&poly_points);
481        assert!(!is_cw);
482        assert_eq!(poly.signed_area(), 45);
483    }
484
485    #[test]
486    pub fn test_xmono_rpolygon() {
487        let coords = [
488            (-2, 2),
489            (0, -1),
490            (-5, 1),
491            (-2, 4),
492            (0, -4),
493            (-4, 3),
494            (-6, -2),
495            (5, 1),
496            (2, 2),
497            (3, -3),
498            (-3, -4),
499            (1, 4),
500        ];
501        let mut pointset = vec![];
502        for (x_coord, y_coord) in coords.iter() {
503            pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
504        }
505        let (poly_points, is_anticw) = RPolygon::<i32>::create_xmono_rpolygon(&pointset);
506        assert!(!rpolygon_is_anticlockwise(&poly_points));
507        assert!(rpolygon_is_xmonotone(&poly_points));
508        assert!(!rpolygon_is_ymonotone(&poly_points));
509        for pt in poly_points.iter() {
510            print!("({}, {}) ", pt.xcoord, pt.ycoord);
511        }
512        let poly = RPolygon::<i32>::new(&poly_points);
513        assert!(!is_anticw);
514        assert_eq!(poly.signed_area(), -53);
515        assert!(!poly.is_anticlockwise())
516    }
517
518    #[test]
519    pub fn test_point_in_rpolygon() {
520        let coords = [
521            (-2, 2),
522            (0, -1),
523            (-5, 1),
524            (-2, 4),
525            (0, -4),
526            (-4, 3),
527            (-6, -2),
528            (5, 1),
529            (2, 2),
530            (3, -3),
531            (-3, -4),
532            (1, 4),
533        ];
534        let mut pointset = vec![];
535        for (x_coord, y_coord) in coords.iter() {
536            pointset.push(Point::<i32, i32>::new(*x_coord, *y_coord));
537        }
538        let query_pt = Point::<i32, i32>::new(0, -3);
539        // let poly = RPolygon::<i32>::new(&pointset);
540        assert!(!RPolygon::<i32>::point_in_rpolygon(&pointset, &query_pt));
541    }
542
543    #[test]
544    fn test_signed_area_more_cases() {
545        let pt1 = Point::new(0, 0);
546        let pt2 = Point::new(1, 0);
547        let pt3 = Point::new(1, 1);
548        let pt4 = Point::new(0, 1);
549        let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
550        assert_eq!(poly.signed_area(), 1);
551
552        let pt5 = Point::new(0, 0);
553        let pt6 = Point::new(0, 1);
554        let pt7 = Point::new(1, 1);
555        let pt8 = Point::new(1, 0);
556        let poly2 = RPolygon::new(&[pt5, pt6, pt7, pt8]);
557        assert_eq!(poly2.signed_area(), -1);
558    }
559
560    #[test]
561    fn test_point_in_rpolygon_more_cases() {
562        let pt1 = Point::new(0, 0);
563        let pt2 = Point::new(1, 0);
564        let pt3 = Point::new(1, 1);
565        let pt4 = Point::new(0, 1);
566        let pointset = &[pt1, pt2, pt3, pt4];
567
568        let query_pt1 = Point::new(0, 0);
569        assert!(RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt1));
570
571        let query_pt2 = Point::new(1, 1);
572        assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt2));
573
574        let query_pt3 = Point::new(0, 1);
575        assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt3));
576
577        let query_pt4 = Point::new(1, 0);
578        assert!(!RPolygon::<i32>::point_in_rpolygon(pointset, &query_pt4));
579    }
580}
581
582#[test]
583fn test_rpolygon_is_xmonotone() {
584    // Test x-monotone polygon
585    let coords = [(0, 0), (1, 0), (2, 0), (2, 1), (1, 1), (0, 1)];
586    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
587    assert!(rpolygon_is_xmonotone(&pointset));
588
589    // Test non-x-monotone polygon
590    let coords2 = [(0, 0), (2, 0), (1, 1), (0, 2), (2, 2)];
591    let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
592    assert!(!rpolygon_is_xmonotone(&pointset2));
593}
594
595#[test]
596fn test_rpolygon_is_ymonotone() {
597    // Test y-monotone polygon
598    let coords = [(0, 0), (0, 1), (0, 2), (1, 2), (1, 1), (1, 0)];
599    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
600    assert!(rpolygon_is_ymonotone(&pointset));
601
602    // Test non-y-monotone polygon
603    let coords2 = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)];
604    let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
605    assert!(!rpolygon_is_ymonotone(&pointset2));
606}
607
608#[test]
609fn test_rpolygon_is_convex() {
610    // Test rectilinearly convex polygon
611    let coords = [(0, 0), (0, 2), (2, 2), (2, 0)];
612    let pointset: Vec<Point<i32, i32>> = coords.iter().map(|(x, y)| Point::new(*x, *y)).collect();
613    assert!(rpolygon_is_convex(&pointset));
614
615    // Test non-convex polygon
616    let coords2 = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)];
617    let pointset2: Vec<Point<i32, i32>> = coords2.iter().map(|(x, y)| Point::new(*x, *y)).collect();
618    assert!(!rpolygon_is_convex(&pointset2));
619}
620
621#[test]
622fn test_rpolygon_vertices() {
623    let pt1 = Point::new(0, 0);
624    let pt2 = Point::new(1, 0);
625    let pt3 = Point::new(1, 1);
626    let poly = RPolygon::new(&[pt1, pt2, pt3]);
627    let vertices = poly.vertices();
628    assert_eq!(vertices.len(), 3);
629    assert_eq!(vertices[0], pt1);
630    assert_eq!(vertices[1], pt2);
631    assert_eq!(vertices[2], pt3);
632}
633
634#[test]
635fn test_rpolygon_bounding_box() {
636    let pt1 = Point::new(0, 0);
637    let pt2 = Point::new(2, 0);
638    let pt3 = Point::new(2, 2);
639    let pt4 = Point::new(0, 2);
640    let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
641    let (min, max) = poly.bounding_box();
642    assert_eq!(min, Point::new(0, 0));
643    assert_eq!(max, Point::new(2, 2));
644}
645
646#[test]
647fn test_rpolygon_add_assign() {
648    let pt1 = Point::new(0, 0);
649    let pt2 = Point::new(1, 0);
650    let pt3 = Point::new(1, 1);
651    let mut poly = RPolygon::new(&[pt1, pt2, pt3]);
652    poly.add_assign(Vector2::new(1, 1));
653    let vertices = poly.vertices();
654    assert_eq!(vertices[0], Point::new(1, 1));
655}
656
657#[test]
658fn test_rpolygon_sub_assign() {
659    let pt1 = Point::new(1, 1);
660    let pt2 = Point::new(2, 1);
661    let pt3 = Point::new(2, 2);
662    let mut poly = RPolygon::new(&[pt1, pt2, pt3]);
663    poly.sub_assign(Vector2::new(1, 1));
664    let vertices = poly.vertices();
665    assert_eq!(vertices[0], Point::new(0, 0));
666}
667
668#[test]
669fn test_rpolygon_is_rectilinear() {
670    let pt1 = Point::new(0, 0);
671    let pt2 = Point::new(1, 0);
672    let pt3 = Point::new(1, 1);
673    let pt4 = Point::new(0, 1);
674    let poly = RPolygon::new(&[pt1, pt2, pt3, pt4]);
675    assert!(poly.is_rectilinear());
676}
677
678#[test]
679fn test_rpolygon_from_origin_and_vectors() {
680    let origin = Point::new(0, 0);
681    let vecs = vec![Vector2::new(1, 0), Vector2::new(1, 1), Vector2::new(0, 1)];
682    let poly = RPolygon::from_origin_and_vectors(origin, vecs);
683    assert_eq!(poly.origin, Point::new(0, 0));
684    let vertices = poly.vertices();
685    assert_eq!(vertices.len(), 4);
686}
687
688#[test]
689fn test_rpolygon_from_pointset() {
690    let pointset = [Point::new(0, 0), Point::new(1, 0), Point::new(1, 1)];
691    let poly = RPolygon::from_pointset(&pointset);
692    assert_eq!(poly.origin, Point::new(0, 0));
693}
694
695#[test]
696fn test_rpolygon_partial_eq() {
697    let pt1 = Point::new(0, 0);
698    let pt2 = Point::new(1, 0);
699    let pt3 = Point::new(1, 1);
700    let poly1 = RPolygon::new(&[pt1, pt2, pt3]);
701    let poly2 = RPolygon::new(&[pt1, pt2, pt3]);
702    assert_eq!(poly1, poly2);
703
704    let poly3 = RPolygon::new(&[pt1, pt2, Point::new(0, 1)]);
705    assert_ne!(poly1, poly3);
706}
707
708#[test]
709fn test_rpolygon_is_anticlockwise_standalone() {
710    let pointset = [Point::new(0, 0), Point::new(1, 0), Point::new(1, 1)];
711    assert!(rpolygon_is_anticlockwise(&pointset));
712}
713
714#[test]
715fn test_rpolygon_default() {
716    let poly: RPolygon<i32> = RPolygon::default();
717    assert_eq!(poly.origin, Point::new(0, 0));
718}