Skip to content

Commit 0ee6b34

Browse files
authored
Merge pull request #1192 from michel-kraemer/main
Polygon in Rect performance improvements
2 parents af7b2bd + 2af5f5b commit 0ee6b34

File tree

4 files changed

+243
-3
lines changed

4 files changed

+243
-3
lines changed

geo/CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* <https://github.com/georust/geo/pull/1159>
77
* Fix issue in Debug impl for AffineTransform where yoff is shown instead of xoff
88
* <https://github.com/georust/geo/pull/1191>
9+
* `Polygon` in `Rect` performance improvements.
10+
* <https://github.com/georust/geo/pull/1192>
911

1012
## 0.28.0
1113

geo/benches/contains.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
22
use geo::contains::Contains;
3-
use geo::{point, polygon, Line, Point, Polygon, Triangle};
3+
use geo::{coord, point, polygon, Line, Point, Polygon, Rect, Triangle};
44

55
fn criterion_benchmark(c: &mut Criterion) {
66
c.bench_function("point in simple polygon", |bencher| {
@@ -128,6 +128,23 @@ fn criterion_benchmark(c: &mut Criterion) {
128128
assert!(!criterion::black_box(&triangle).contains(criterion::black_box(&point)));
129129
});
130130
});
131+
132+
c.bench_function("Rect contains polygon", |bencher| {
133+
let polygon = polygon![
134+
(x: 150., y: 350.),
135+
(x: 100., y: 350.),
136+
(x: 210., y: 160.),
137+
(x: 290., y: 350.),
138+
(x: 250., y: 350.),
139+
(x: 200., y: 250.),
140+
(x: 150., y: 350.),
141+
];
142+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
143+
144+
bencher.iter(|| {
145+
assert!(criterion::black_box(&rect).contains(criterion::black_box(&polygon)));
146+
});
147+
});
131148
}
132149

133150
criterion_group!(benches, criterion_benchmark);

geo/src/algorithm/contains/mod.rs

+183
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub(crate) use impl_contains_geometry_for;
9595
mod test {
9696
use crate::line_string;
9797
use crate::Contains;
98+
use crate::Relate;
9899
use crate::{coord, Coord, Line, LineString, MultiPolygon, Point, Polygon, Rect, Triangle};
99100

100101
#[test]
@@ -555,4 +556,186 @@ mod test {
555556
let pt: Point = (0.5, 0.5).into();
556557
assert!(!tri.contains(&pt));
557558
}
559+
560+
#[test]
561+
fn rect_contains_polygon() {
562+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
563+
let poly = Polygon::new(
564+
line_string![
565+
(x: 150., y: 350.),
566+
(x: 100., y: 350.),
567+
(x: 210., y: 160.),
568+
(x: 290., y: 350.),
569+
(x: 250., y: 350.),
570+
(x: 200., y: 250.),
571+
(x: 150., y: 350.),
572+
],
573+
vec![],
574+
);
575+
assert_eq!(rect.contains(&poly), rect.relate(&poly).is_contains());
576+
}
577+
578+
#[test]
579+
fn rect_contains_touching_polygon() {
580+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
581+
let touching_poly = Polygon::new(
582+
line_string![
583+
(x: 150., y: 350.),
584+
(x: 90., y: 350.),
585+
(x: 210., y: 160.),
586+
(x: 290., y: 350.),
587+
(x: 250., y: 350.),
588+
(x: 200., y: 250.),
589+
(x: 150., y: 350.),
590+
],
591+
vec![],
592+
);
593+
assert_eq!(
594+
rect.contains(&touching_poly),
595+
rect.relate(&touching_poly).is_contains()
596+
);
597+
598+
let touching_rect = Rect::new(coord! { x: 90., y: 200. }, coord! { x: 200., y: 300. });
599+
assert_eq!(
600+
rect.contains(&touching_rect),
601+
rect.relate(&touching_rect).is_contains()
602+
);
603+
}
604+
605+
#[test]
606+
fn rect_contains_empty_polygon() {
607+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
608+
let empty_poly = Polygon::new(line_string![], vec![]);
609+
assert_eq!(
610+
rect.contains(&empty_poly),
611+
rect.relate(&empty_poly).is_contains()
612+
);
613+
}
614+
615+
#[test]
616+
fn rect_contains_polygon_empty_area() {
617+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
618+
let empty_poly = Polygon::new(
619+
line_string![
620+
(x: 100., y: 200.),
621+
(x: 100., y: 200.),
622+
(x: 100., y: 200.),
623+
(x: 100., y: 200.),
624+
],
625+
vec![],
626+
);
627+
assert_eq!(
628+
rect.contains(&empty_poly),
629+
rect.relate(&empty_poly).is_contains()
630+
);
631+
}
632+
633+
#[test]
634+
fn rect_contains_rect_polygon() {
635+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
636+
let rect_poly = rect.to_polygon();
637+
assert_eq!(
638+
rect.contains(&rect_poly),
639+
rect.relate(&rect_poly).is_contains()
640+
);
641+
}
642+
643+
#[test]
644+
fn rect_contains_polygon_in_boundary() {
645+
let rect = Rect::new(coord! { x: 90. , y: 150. }, coord! { x: 300., y: 360. });
646+
let poly_one_border =
647+
Rect::new(coord! { x: 90. , y: 150. }, coord! { x: 90., y: 360. }).to_polygon();
648+
assert_eq!(
649+
rect.contains(&poly_one_border),
650+
rect.relate(&poly_one_border).is_contains()
651+
);
652+
653+
let poly_two_borders = Polygon::new(
654+
line_string![
655+
(x: 90., y: 150.),
656+
(x: 300., y: 150.),
657+
(x: 90., y: 150.),
658+
(x: 90., y: 360.),
659+
(x: 90., y: 150.),
660+
],
661+
vec![],
662+
);
663+
assert_eq!(
664+
rect.contains(&poly_two_borders),
665+
rect.relate(&poly_two_borders).is_contains()
666+
);
667+
668+
let poly_two_borders_triangle = Polygon::new(
669+
line_string![
670+
(x: 90., y: 150.),
671+
(x: 300., y: 150.),
672+
(x: 90., y: 360.),
673+
(x: 90., y: 150.),
674+
],
675+
vec![],
676+
);
677+
assert_eq!(
678+
rect.contains(&poly_two_borders_triangle),
679+
rect.relate(&poly_two_borders_triangle).is_contains()
680+
);
681+
}
682+
683+
#[test]
684+
fn rect_contains_polygon_in_boundary_with_hole() {
685+
let rect = Rect::new(coord! { x: 90. , y: 150. }, coord! { x: 300., y: 360. });
686+
let poly_two_borders_triangle_with_hole = Polygon::new(
687+
line_string![
688+
(x: 90., y: 150.),
689+
(x: 300., y: 150.),
690+
(x: 90., y: 360.),
691+
(x: 90., y: 150.),
692+
],
693+
vec![line_string![
694+
(x: 90., y: 150.),
695+
(x: 300., y: 150.),
696+
(x: 90., y: 360.),
697+
(x: 90., y: 150.),
698+
]],
699+
);
700+
assert_eq!(
701+
rect.contains(&poly_two_borders_triangle_with_hole),
702+
rect.relate(&poly_two_borders_triangle_with_hole)
703+
.is_contains()
704+
);
705+
}
706+
707+
#[test]
708+
fn rect_empty_contains_polygon() {
709+
let rect = Rect::new(coord! { x: 90. , y: 150. }, coord! { x: 90., y: 150. });
710+
let poly = Polygon::new(
711+
line_string![
712+
(x: 150., y: 350.),
713+
(x: 100., y: 350.),
714+
(x: 210., y: 160.),
715+
(x: 290., y: 350.),
716+
(x: 250., y: 350.),
717+
(x: 200., y: 250.),
718+
(x: 150., y: 350.),
719+
],
720+
vec![],
721+
);
722+
assert_eq!(rect.contains(&poly), rect.relate(&poly).is_contains());
723+
724+
let rect_poly = rect.to_polygon();
725+
assert_eq!(
726+
rect.contains(&rect_poly),
727+
rect.relate(&rect_poly).is_contains()
728+
);
729+
}
730+
731+
#[test]
732+
fn rect_contains_point() {
733+
let rect = Rect::new(coord! { x: 90., y: 150. }, coord! { x: 300., y: 360. });
734+
735+
let point1 = Point::new(100., 200.);
736+
assert_eq!(rect.contains(&point1), rect.relate(&point1).is_contains());
737+
738+
let point2 = Point::new(90., 200.);
739+
assert_eq!(rect.contains(&point2), rect.relate(&point2).is_contains());
740+
}
558741
}

geo/src/algorithm/contains/rect.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use geo_types::CoordFloat;
2+
13
use super::{impl_contains_from_relate, impl_contains_geometry_for, Contains};
2-
use crate::geometry::*;
4+
use crate::{geometry::*, Area, CoordsIter, HasDimensions, Intersects};
35
use crate::{CoordNum, GeoFloat};
46

57
// ┌──────────────────────────┐
@@ -41,5 +43,41 @@ where
4143
}
4244
}
4345

44-
impl_contains_from_relate!(Rect<T>, [Line<T>, LineString<T>, Polygon<T>, MultiPoint<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Triangle<T>]);
46+
impl<T> Contains<Polygon<T>> for Rect<T>
47+
where
48+
T: CoordFloat,
49+
{
50+
fn contains(&self, rhs: &Polygon<T>) -> bool {
51+
// the polygon must not be empty
52+
if rhs.is_empty() {
53+
return false;
54+
}
55+
56+
// none of the polygon's points may lie outside the rectangle
57+
let mut points_inside = 0;
58+
for c in rhs.exterior_coords_iter() {
59+
if !self.intersects(&c) {
60+
return false;
61+
}
62+
if self.contains(&c) {
63+
points_inside += 1;
64+
}
65+
}
66+
67+
// The polygon must not lie completely inside the rectangle's boundary.
68+
// In other words: at least one point of the interior of the polygon
69+
// must lie in the interior of the rectangle. Since we know that the
70+
// rectangle is convex, we just need make sure that either at least
71+
// one point of the polygon lies inside the rectangle's interior or
72+
// that the polygon's interior is not empty, in which case it will
73+
// definitely intersect with the rectangle's interior.
74+
if points_inside == 0 && rhs.signed_area().is_zero() {
75+
return false;
76+
}
77+
78+
true
79+
}
80+
}
81+
82+
impl_contains_from_relate!(Rect<T>, [Line<T>, LineString<T>, MultiPoint<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Triangle<T>]);
4583
impl_contains_geometry_for!(Rect<T>);

0 commit comments

Comments
 (0)