1pub use self::builder::*;
4use super::*;
5use crate::ConvexHull;
6use crate::Intersects;
7use itertools::Itertools;
8use serde::{Deserialize, Serialize};
9
10mod builder;
11
12#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
20pub struct Polygon {
21 vertices: Vec<Point>,
23}
24
25impl Polygon {
26 pub fn try_new(vertices: Vec<Point>) -> Result<Self, ()> {
36 const MINIMUM_VERTICES_IN_EUCLIDEAN_GEOMETRY: usize = 3;
37
38 if vertices.len() >= MINIMUM_VERTICES_IN_EUCLIDEAN_GEOMETRY
39 && vertices.iter().all(is_finite)
40 && is_convex_polygon(&vertices)
41 {
42 Ok(Self { vertices })
43 } else {
44 Err(())
45 }
46 }
47
48 pub fn vertices(&self) -> &[Point] {
50 &self.vertices
51 }
52
53 pub fn translate(&self, translation: Point) -> Self {
56 let translated_vertices = self
57 .vertices
58 .iter()
59 .map(|&vertex| vertex + translation)
60 .collect();
61
62 Polygon {
63 vertices: translated_vertices,
64 }
65 }
66
67 pub fn rotate_around_point(&self, rotation: Radians, point: Point) -> Self {
69 let rotation = rotation.value();
70 let rotated_vertices = self
71 .vertices
72 .iter()
73 .map(|&vertex| {
74 let delta = vertex - point;
76 let (rotation_sin, rotation_cos) = rotation.sin_cos();
77 let rotated_x = rotation_cos * delta.x + rotation_sin * delta.y + point.x;
78 let rotated_y = -rotation_sin * delta.x + rotation_cos * delta.y + point.y;
79 Point {
80 x: rotated_x,
81 y: rotated_y,
82 }
83 })
84 .collect();
85 Self {
86 vertices: rotated_vertices,
87 }
88 }
89
90 pub fn contains_point(&self, point: Point) -> bool {
92 let vector_to_point: Vector = point.into();
93 let vector_to_last_point: Vector = (*self.vertices.last().unwrap()).into();
96 let vector_to_first_point: Vector = (*self.vertices.first().unwrap()).into();
97 let reference_side =
98 calculate_facing_side(vector_to_last_point, vector_to_first_point, vector_to_point);
99
100 self.vertices
103 .iter()
104 .tuple_windows()
105 .map(|(&vector_to_point_a, &vector_to_point_b)| {
106 calculate_facing_side(
107 vector_to_point_a.into(),
108 vector_to_point_b.into(),
109 vector_to_point,
110 )
111 })
112 .all(|side| side == reference_side || side == Side::OnTheLine)
113 }
114
115 pub fn aabb(&self) -> Aabb {
122 let mut vertices = self.vertices.clone();
123
124 vertices.sort_unstable_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
126 let min_x = vertices.first().unwrap().x;
128 let max_x = vertices.last().unwrap().x;
129
130 vertices.sort_unstable_by(|a, b| a.y.partial_cmp(&b.y).unwrap());
131 let min_y = vertices.first().unwrap().y;
132 let max_y = vertices.last().unwrap().y;
133
134 Aabb::try_new((min_x, min_y), (max_x, max_y)).unwrap()
136 }
137
138 pub fn edges(&self) -> impl Iterator<Item = Vector> + '_ {
140 let vertices = self.vertices();
141 let shifted_vertices = vertices.iter().cycle().skip(1).take(vertices.len());
142 vertices
143 .iter()
144 .zip(shifted_vertices)
145 .map(|(&first_vertex, &second_vertex)| second_vertex - first_vertex)
146 .map(Vector::from)
147 }
148
149 fn scalar_project_onto_unit_vector(&self, axis: Vector) -> (f64, f64) {
150 let projection: Vec<_> = self
151 .vertices()
152 .iter()
153 .cloned()
154 .map(Vector::from)
155 .map(|vector| vector.dot_product(axis))
156 .collect();
157 (
158 *projection
159 .iter()
160 .min_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap())
161 .unwrap(),
162 *projection
163 .iter()
164 .max_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap())
165 .unwrap(),
166 )
167 }
168}
169
170impl Intersects for Polygon {
171 fn intersects(&self, other: &Polygon) -> bool {
173 self.edges()
179 .chain(other.edges())
180 .map(Vector::normal)
183 .map(Vector::unit)
187 .all(|axis| {
188 let (own_min, own_max) = self.scalar_project_onto_unit_vector(axis);
191 let (other_min, other_max) = other.scalar_project_onto_unit_vector(axis);
192
193 own_min.max(other_min) <= own_max.min(other_max)
196 })
197 }
198}
199
200impl From<Aabb> for Polygon {
201 fn from(aabb: Aabb) -> Self {
202 Polygon {
203 vertices: vec![
204 Point {
205 x: aabb.upper_left.x,
206 y: aabb.upper_left.y,
207 },
208 Point {
209 x: aabb.upper_left.x,
210 y: aabb.lower_right.y,
211 },
212 Point {
213 x: aabb.lower_right.x,
214 y: aabb.upper_left.y,
215 },
216 Point {
217 x: aabb.lower_right.x,
218 y: aabb.lower_right.y,
219 },
220 ],
221 }
222 }
223}
224
225fn calculate_facing_side(a: Vector, b: Vector, point: Vector) -> Side {
228 let side_vector = b - a;
229 let vector_from_a_to_point = point - a;
230 let cross_product = side_vector.cross_product(vector_from_a_to_point);
231
232 const EPSILON: f64 = 0.000_001;
235 if cross_product < -EPSILON {
236 Side::Left
237 } else if cross_product > EPSILON {
238 Side::Right
239 } else {
240 Side::OnTheLine
241 }
242}
243
244fn is_convex_polygon(vertices: &[Point]) -> bool {
245 let convex_hull_vertice_count = ConvexHull::try_new(vertices).unwrap().count();
246 convex_hull_vertice_count == vertices.len()
247}
248
249fn is_finite(vertice: &Point) -> bool {
250 vertice.x.is_finite() && vertice.y.is_finite()
251}
252
253#[derive(Eq, PartialEq, Debug)]
256enum Side {
257 Right,
259 Left,
261 OnTheLine,
263}
264
265#[cfg(test)]
266mod tests {
267 use self::builder::PolygonBuilder;
268 use super::*;
269 use std::f64::consts::PI;
270
271 fn polygon() -> Polygon {
272 PolygonBuilder::default()
273 .vertex(-10.0, -10.0)
274 .vertex(10.0, -10.0)
275 .vertex(10.0, 10.0)
276 .vertex(-10.0, 10.0)
277 .build()
278 .unwrap()
279 }
280
281 fn translation() -> Point {
282 Point { x: 30.0, y: 40.0 }
283 }
284
285 #[test]
286 fn translates() {
287 let polygon = polygon();
288 assert_eq!(
289 Polygon {
290 vertices: vec![
291 Point { x: 20.0, y: 30.0 },
292 Point { x: 40.0, y: 30.0 },
293 Point { x: 40.0, y: 50.0 },
294 Point { x: 20.0, y: 50.0 },
295 ],
296 },
297 polygon.translate(translation())
298 );
299 }
300
301 #[test]
302 fn rotates_by_pi() {
303 let polygon = polygon();
304
305 const FLOATING_POINT_INACCURACY: f64 = 0.000_000_000_000_002;
306 assert_eq!(
307 Polygon {
308 vertices: vec![
309 Point {
310 x: 10.0 - FLOATING_POINT_INACCURACY,
311 y: 10.0 + FLOATING_POINT_INACCURACY,
312 },
313 Point {
314 x: -10.0 - FLOATING_POINT_INACCURACY,
315 y: 10.0 - FLOATING_POINT_INACCURACY,
316 },
317 Point {
318 x: -10.0 + FLOATING_POINT_INACCURACY,
319 y: -10.0 - FLOATING_POINT_INACCURACY,
320 },
321 Point {
322 x: 10.0 + FLOATING_POINT_INACCURACY,
323 y: -10.0 + FLOATING_POINT_INACCURACY,
324 },
325 ],
326 },
327 polygon.rotate_around_point(Radians::try_new(PI).unwrap(), Point::default())
328 );
329 }
330
331 #[test]
332 fn rotates_by_arbitrary_orientation() {
333 let polygon = polygon();
334
335 const ROTATION_A: f64 = 8.488_724_885_405_782;
336 const ROTATION_B: f64 = 11.311_125_046_603_125;
337
338 assert_eq!(
339 Polygon {
340 vertices: vec![
341 Point {
342 x: ROTATION_A,
343 y: ROTATION_B,
344 },
345 Point {
346 x: -ROTATION_B,
347 y: ROTATION_A,
348 },
349 Point {
350 x: -ROTATION_A,
351 y: -ROTATION_B,
352 },
353 Point {
354 x: ROTATION_B,
355 y: -ROTATION_A,
356 },
357 ],
358 },
359 polygon.rotate_around_point(Radians::try_new(3.0).unwrap(), Point::default())
360 );
361 }
362
363 #[test]
364 fn translates_and_rotates() {
365 let polygon = polygon();
366 let translation = translation();
367 let translated_polygon = polygon.translate(translation);
368
369 assert_eq!(
370 Polygon {
371 vertices: vec![
372 Point {
373 x: 38.488_724_885_405_78,
374 y: 51.311_125_046_603_124,
375 },
376 Point {
377 x: 18.688_874_953_396_876,
378 y: 48.488_724_885_405_78,
379 },
380 Point {
381 x: 21.511_275_114_594_22,
382 y: 28.688_874_953_396_876,
383 },
384 Point {
385 x: 41.311_125_046_603_124,
386 y: 31.511_275_114_594_22,
387 },
388 ],
389 },
390 translated_polygon.rotate_around_point(Radians::try_new(3.0).unwrap(), translation)
391 );
392 }
393
394 #[test]
395 fn contains_point_when_point_is_positive() {
396 let translation = Point { x: 10.43, y: 20.1 };
397 let polygon = polygon().translate(translation);
398 let point = Point { x: 12.0, y: 18.0 };
399 assert!(polygon.contains_point(point));
400 }
401
402 #[test]
403 fn contains_point_when_point_is_negative() {
404 let translation = Point { x: -20.0, y: -5.0 };
405 let polygon = polygon().translate(translation);
406 let point = Point { x: -21.70, y: -2.3 };
407 assert!(polygon.contains_point(point));
408 }
409
410 #[test]
411 fn contains_point_when_point_is_at_zero() {
412 let polygon = polygon();
413 let point = Point::default();
414 assert!(polygon.contains_point(point));
415 }
416
417 #[test]
418 fn contains_point_at_border() {
419 let polygon = polygon();
420 let point = Point { x: 10.0, y: -10.0 };
421 assert!(polygon.contains_point(point));
422 }
423
424 #[test]
425 fn does_not_contain_point_barely_outside_polygon() {
426 let polygon = polygon();
427 let point = Point { x: 10.1, y: -10.1 };
428 assert!(!polygon.contains_point(point));
429 }
430
431 #[test]
432 fn does_not_contain_point_way_outside_polygon() {
433 let polygon = polygon();
434 let point = Point {
435 x: -9000.0,
436 y: -9000.0,
437 };
438 assert!(!polygon.contains_point(point));
439 }
440
441 #[test]
442 fn does_not_contain_point_when_point_is_at_zero() {
443 let translation = Point { x: 11.0, y: 11.0 };
444 let polygon = polygon().translate(translation);
445 let point = Point::default();
446 assert!(!polygon.contains_point(point));
447 }
448
449 #[test]
450 #[should_panic]
451 fn aabb_panics_when_polygon_has_zero_vertices() {
452 let polygon = Polygon::default();
453 let _aabb = polygon.aabb();
454 }
455
456 #[test]
457 fn aabb_returns_works_with_three_vertices() {
458 let polygon = Polygon {
459 vertices: vec![
460 Point { x: -5.0, y: -5.0 },
461 Point { x: 5.0, y: 0.0 },
462 Point { x: 5.0, y: 5.0 },
463 ],
464 };
465 let expected_aabb = Aabb::try_new((-5.0, -5.0), (5.0, 5.0)).unwrap();
466
467 assert_eq!(expected_aabb, polygon.aabb());
468 }
469
470 #[test]
471 fn aabb_returns_works_with_four_vertices() {
472 let polygon = Polygon {
473 vertices: vec![
474 Point { x: 5.0, y: 0.0 },
475 Point { x: 0.0, y: 5.0 },
476 Point { x: -5.0, y: -5.0 },
477 Point { x: 5.0, y: 5.0 },
478 ],
479 };
480 let expected_aabb = Aabb::try_new((-5.0, -5.0), (5.0, 5.0)).unwrap();
481
482 assert_eq!(expected_aabb, polygon.aabb());
483 }
484
485 #[test]
486 fn try_new_errors_for_zero_vertices() {
487 assert!(Polygon::try_new(Vec::new()).is_err());
488 }
489
490 #[test]
491 fn try_new_errors_for_one_vertex() {
492 assert!(Polygon::try_new(vec![Point { x: 0.0, y: 0.0 }]).is_err());
493 }
494
495 #[test]
496 fn try_new_errors_for_two_vertices() {
497 assert!(
498 Polygon::try_new(vec![Point { x: 0.0, y: 0.0 }, Point { x: 1.0, y: 0.0 }]).is_err()
499 );
500 }
501
502 #[test]
503 fn try_new_works_for_three_vertices() {
504 assert_eq!(
505 Ok(Polygon {
506 vertices: vec![
507 Point { x: 0.0, y: 0.0 },
508 Point { x: 1.0, y: 0.0 },
509 Point { x: 0.0, y: 1.0 },
510 ]
511 }),
512 Polygon::try_new(vec![
513 Point { x: 0.0, y: 0.0 },
514 Point { x: 1.0, y: 0.0 },
515 Point { x: 0.0, y: 1.0 },
516 ])
517 );
518 }
519
520 #[test]
521 fn try_new_works_for_four_vertices() {
522 assert_eq!(
523 Ok(Polygon {
524 vertices: vec![
525 Point { x: 0.0, y: 0.0 },
526 Point { x: 1.0, y: 0.0 },
527 Point { x: 0.0, y: 1.0 },
528 Point { x: 1.0, y: 1.0 },
529 ]
530 }),
531 Polygon::try_new(vec![
532 Point { x: 0.0, y: 0.0 },
533 Point { x: 1.0, y: 0.0 },
534 Point { x: 0.0, y: 1.0 },
535 Point { x: 1.0, y: 1.0 },
536 ])
537 );
538 }
539
540 #[test]
541 fn try_new_does_not_work_with_concave_polygon() {
542 assert!(Polygon::try_new(vec![
543 Point { x: 10.0, y: 10.0 },
544 Point { x: 5.0, y: 5.0 },
545 Point { x: 10.0, y: 5.0 },
546 Point { x: 15.0, y: 0.0 },
547 Point { x: 10.0, y: 0.0 },
548 ])
549 .is_err());
550 }
551
552 #[test]
553 fn try_new_works_with_convex_polygon() {
554 let vertices = vec![
555 Point { x: 10.0, y: 10.0 },
556 Point { x: 5.0, y: 5.0 },
557 Point { x: 20.0, y: 5.0 },
558 Point { x: 15.0, y: 0.0 },
559 Point { x: 10.0, y: 0.0 },
560 ];
561
562 assert_eq!(
563 Ok(Polygon {
564 vertices: vertices.clone(),
565 }),
566 Polygon::try_new(vertices)
567 );
568 }
569
570 #[test]
571 fn try_new_does_not_work_when_x_value_of_vertice_is_positive_infinity() {
572 let vertices = vec![
573 Point {
574 x: f64::INFINITY,
575 y: 0.0,
576 },
577 Point { x: 1.0, y: 0.0 },
578 Point { x: 0.0, y: 1.0 },
579 ];
580 assert!(Polygon::try_new(vertices).is_err());
581 }
582
583 #[test]
584 fn try_new_does_not_work_when_x_value_of_vertice_is_negative_infinity() {
585 let vertices = vec![
586 Point {
587 x: f64::NEG_INFINITY,
588 y: 0.0,
589 },
590 Point { x: 1.0, y: 0.0 },
591 Point { x: 0.0, y: 1.0 },
592 ];
593 assert!(Polygon::try_new(vertices).is_err());
594 }
595
596 #[test]
597 fn try_new_does_not_work_when_x_value_of_vertice_is_nan() {
598 let vertices = vec![
599 Point {
600 x: f64::NAN,
601 y: 0.0,
602 },
603 Point { x: 1.0, y: 0.0 },
604 Point { x: 0.0, y: 1.0 },
605 ];
606 assert!(Polygon::try_new(vertices).is_err());
607 }
608
609 #[test]
610 fn try_new_does_not_work_when_y_value_of_vertice_is_positive_infinity() {
611 let vertices = vec![
612 Point {
613 x: 0.0,
614 y: f64::INFINITY,
615 },
616 Point { x: 1.0, y: 0.0 },
617 Point { x: 0.0, y: 1.0 },
618 ];
619 assert!(Polygon::try_new(vertices).is_err());
620 }
621
622 #[test]
623 fn try_new_does_not_work_when_y_value_of_vertice_is_negative_infinity() {
624 let vertices = vec![
625 Point {
626 x: 0.0,
627 y: f64::NEG_INFINITY,
628 },
629 Point { x: 1.0, y: 0.0 },
630 Point { x: 0.0, y: 1.0 },
631 ];
632 assert!(Polygon::try_new(vertices).is_err());
633 }
634
635 #[test]
636 fn try_new_does_not_work_when_y_value_of_vertice_is_nan() {
637 let vertices = vec![
638 Point {
639 x: 0.0,
640 y: f64::NAN,
641 },
642 Point { x: 1.0, y: 0.0 },
643 Point { x: 0.0, y: 1.0 },
644 ];
645 assert!(Polygon::try_new(vertices).is_err());
646 }
647
648 #[test]
649 fn can_be_created_from_aabb() {
650 let aabb = Aabb::try_new(Point { x: 10.0, y: 15.0 }, Point { x: 20.0, y: 30.0 }).unwrap();
651 let expected_polygon = Polygon::try_new(vec![
652 Point { x: 10.0, y: 15.0 },
653 Point { x: 10.0, y: 30.0 },
654 Point { x: 20.0, y: 15.0 },
655 Point { x: 20.0, y: 30.0 },
656 ])
657 .unwrap();
658
659 assert_eq!(expected_polygon, Polygon::from(aabb));
660 }
661
662 #[test]
663 fn edges_are_reported_correctly() {
664 let polygon = Polygon::try_new(vec![
665 Point { x: 10.0, y: 15.0 },
666 Point { x: 10.0, y: 30.0 },
667 Point { x: 20.0, y: 15.0 },
668 Point { x: 20.0, y: 30.0 },
669 ])
670 .unwrap();
671
672 let expected_edges = vec![
673 Vector { x: 0.0, y: 15.0 },
674 Vector { x: 10.0, y: -15.0 },
675 Vector { x: 0.0, y: 15.0 },
676 Vector { x: -10.0, y: -15.0 },
677 ];
678
679 let edges: Vec<_> = polygon.edges().collect();
680 assert_eq!(expected_edges, edges);
681 }
682
683 #[test]
684 fn intersects_self() {
685 let polygon = Polygon::try_new(vec![
686 Point { x: 0.0, y: 0.0 },
687 Point { x: 10.0, y: 0.0 },
688 Point { x: 10.0, y: 10.0 },
689 ])
690 .unwrap();
691 assert!(polygon.intersects(&polygon));
692 }
693
694 #[test]
695 fn intersects_contained() {
696 let bigger_polygon = Polygon::try_new(vec![
697 Point { x: 0.0, y: 0.0 },
698 Point { x: 10.0, y: 0.0 },
699 Point { x: 10.0, y: 10.0 },
700 ])
701 .unwrap();
702 let smaller_polygon = Polygon::try_new(vec![
703 Point { x: 2.0, y: 2.0 },
704 Point { x: 8.0, y: 2.0 },
705 Point { x: 8.0, y: 8.0 },
706 ])
707 .unwrap();
708 assert!(bigger_polygon.intersects(&smaller_polygon));
709 assert!(smaller_polygon.intersects(&bigger_polygon));
710 }
711
712 #[test]
713 fn intersects_touching_line() {
714 let left_polygon = Polygon::try_new(vec![
715 Point { x: 0.0, y: 0.0 },
716 Point { x: 10.0, y: 0.0 },
717 Point { x: 10.0, y: 10.0 },
718 ])
719 .unwrap();
720 let right_polygon = Polygon::try_new(vec![
721 Point { x: 10.0, y: 0.0 },
722 Point { x: 5.0, y: 5.0 },
723 Point { x: 20.0, y: 10.0 },
724 ])
725 .unwrap();
726 assert!(left_polygon.intersects(&right_polygon));
727 assert!(right_polygon.intersects(&left_polygon));
728 }
729
730 #[test]
731 fn intersects_touching_point() {
732 let left_polygon = Polygon::try_new(vec![
733 Point { x: 0.0, y: 0.0 },
734 Point { x: 10.0, y: 0.0 },
735 Point { x: 10.0, y: 10.0 },
736 ])
737 .unwrap();
738 let right_polygon = Polygon::try_new(vec![
739 Point { x: 10.0, y: 10.0 },
740 Point { x: 20.0, y: 10.0 },
741 Point { x: 20.0, y: 11.0 },
742 ])
743 .unwrap();
744 assert!(left_polygon.intersects(&right_polygon));
745 assert!(right_polygon.intersects(&left_polygon));
746 }
747
748 #[test]
749 fn intersects_intersecting() {
750 let first_polygon = Polygon::try_new(vec![
751 Point { x: 0.0, y: 0.0 },
752 Point { x: 10.0, y: 0.0 },
753 Point { x: 10.0, y: 10.0 },
754 ])
755 .unwrap();
756 let second_polygon = Polygon::try_new(vec![
757 Point { x: 8.0, y: 8.0 },
758 Point { x: 20.0, y: 8.0 },
759 Point { x: 20.0, y: 20.0 },
760 ])
761 .unwrap();
762 assert!(first_polygon.intersects(&second_polygon));
763 assert!(second_polygon.intersects(&first_polygon));
764 }
765
766 #[test]
767 fn intersects_intersecting_when_negative() {
768 let first_polygon = Polygon::try_new(vec![
769 Point { x: -10.0, y: -10.0 },
770 Point { x: -5.0, y: -10.0 },
771 Point { x: -5.0, y: -5.0 },
772 ])
773 .unwrap();
774 let second_polygon = Polygon::try_new(vec![
775 Point { x: -8.0, y: -20.0 },
776 Point { x: -3.0, y: -20.0 },
777 Point { x: -3.0, y: -3.0 },
778 ])
779 .unwrap();
780 assert!(first_polygon.intersects(&second_polygon));
781 assert!(second_polygon.intersects(&first_polygon));
782 }
783
784 #[test]
785 fn intersects_intersecting_when_negative_and_positive() {
786 let first_polygon = Polygon::try_new(vec![
787 Point { x: -5.0, y: -5.0 },
788 Point { x: 5.0, y: -5.0 },
789 Point { x: 5.0, y: 5.0 },
790 ])
791 .unwrap();
792 let second_polygon = Polygon::try_new(vec![
793 Point { x: -6.0, y: -20.0 },
794 Point { x: 0.0, y: -20.0 },
795 Point { x: 0.0, y: 2.0 },
796 ])
797 .unwrap();
798 assert!(first_polygon.intersects(&second_polygon));
799 assert!(second_polygon.intersects(&first_polygon));
800 }
801
802 #[test]
803 fn does_not_intersect_when_apart() {
804 let first_polygon = Polygon::try_new(vec![
805 Point { x: 0.0, y: 0.0 },
806 Point { x: 10.0, y: 0.0 },
807 Point { x: 10.0, y: 10.0 },
808 ])
809 .unwrap();
810 let second_polygon = Polygon::try_new(vec![
811 Point { x: 20.0, y: 0.0 },
812 Point { x: 21.0, y: 0.0 },
813 Point { x: 21.0, y: 20.0 },
814 ])
815 .unwrap();
816 assert!(!first_polygon.intersects(&second_polygon));
817 assert!(!second_polygon.intersects(&first_polygon));
818 }
819}