Skip to content

Commit 8ebdcca

Browse files
author
cookiedan42
committed
faster upper bound for line-rect intersection
1 parent 70df134 commit 8ebdcca

File tree

3 files changed

+144
-12
lines changed

3 files changed

+144
-12
lines changed

geo-benches/src/intersection.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,46 @@ use criterion::{Criterion, criterion_group, criterion_main};
22
use geo::intersects::Intersects;
33
use geo::{MultiPolygon, Triangle};
44

5+
fn line_rect_intersection(c: &mut Criterion) {
6+
use geo::{Line, Rect, coord};
7+
8+
c.bench_function("Line intersects Rect (end intersect)", |bencher| {
9+
let line = Line::new(coord! {x:0., y:0.}, coord! {x:10., y:10.});
10+
let rect = Rect::new(coord! {x:0., y:0.}, coord! {x:10., y:10.});
11+
12+
bencher.iter(|| {
13+
assert!(criterion::black_box(&line).intersects(criterion::black_box(&rect)));
14+
});
15+
});
16+
17+
c.bench_function("Line intersects Rect cross", |bencher| {
18+
let line = Line::new(coord! {x:-1., y:5.}, coord! {x:11., y:16.});
19+
let rect = Rect::new(coord! {x:0., y:0.}, coord! {x:10., y:10.});
20+
21+
bencher.iter(|| {
22+
assert!(criterion::black_box(&line).intersects(criterion::black_box(&rect)));
23+
});
24+
});
25+
26+
c.bench_function("Line disjoint Rect bbox disjoint", |bencher| {
27+
let line = Line::new(coord! {x:0., y:0.}, coord! {x:10., y:10.});
28+
let rect = Rect::new(coord! {x:11., y:11.}, coord! {x:12., y:12.});
29+
30+
bencher.iter(|| {
31+
assert!(!criterion::black_box(&line).intersects(criterion::black_box(&rect)));
32+
});
33+
});
34+
35+
c.bench_function("Line disjoint Rect bbox overlap", |bencher| {
36+
let line = Line::new(coord! {x:0., y:0.}, coord! {x:10., y:10.});
37+
let rect = Rect::new(coord! {x:6., y:4.}, coord! {x:10., y:0.});
38+
39+
bencher.iter(|| {
40+
assert!(!criterion::black_box(&line).intersects(criterion::black_box(&rect)));
41+
});
42+
});
43+
}
44+
545
fn multi_polygon_intersection(c: &mut Criterion) {
646
let plot_polygons: MultiPolygon = geo_test_fixtures::nl_plots_wgs84();
747
let zone_polygons: MultiPolygon = geo_test_fixtures::nl_zones();
@@ -253,8 +293,10 @@ criterion_group! {
253293
}
254294

255295
criterion_group! { bench_linestring_poly,linestring_polygon_intersection}
296+
criterion_group! { bench_line_rect_intersection,line_rect_intersection}
256297

257298
criterion_main!(
299+
bench_line_rect_intersection,
258300
bench_linestring_poly,
259301
bench_multi_polygons,
260302
bench_point_rect,

geo/src/algorithm/intersects/line_string.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,19 @@ where
6363
}
6464
}
6565

66-
intersects_line_string_impl!(area: Rect<T>);
66+
impl<T> Intersects<Rect<T>> for LineString<T>
67+
where
68+
T: GeoNum,
69+
{
70+
fn intersects(&self, rhs: &Rect<T>) -> bool {
71+
if has_disjoint_bboxes(self, rhs) {
72+
return false;
73+
}
74+
// splitting into `Line` intersects `Rect`
75+
self.lines_iter().any(|ln| ln.intersects(rhs))
76+
}
77+
}
78+
6779
intersects_line_string_impl!(area: Triangle<T>);
6880

6981
// Blanket implementation from LineString<T>

geo/src/algorithm/intersects/rect.rs

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,53 @@ where
2323
T: GeoNum,
2424
{
2525
fn intersects(&self, rhs: &Line<T>) -> bool {
26-
let lb = self.min();
27-
let rt = self.max();
28-
let lt = Coord::from((lb.x, rt.y));
29-
let rb = Coord::from((rt.x, lb.y));
30-
// If either rhs.{start,end} lies inside Rect, then true
31-
self.intersects(&rhs.start)
32-
|| self.intersects(&rhs.end)
33-
|| Line::new(lt, rt).intersects(rhs)
34-
|| Line::new(rt, rb).intersects(rhs)
35-
|| Line::new(lb, rb).intersects(rhs)
36-
|| Line::new(lt, lb).intersects(rhs)
26+
// adds overhead to worst case
27+
// but short circuits if a terminal intersects the rectangle
28+
29+
// if self.intersects(&rhs.start)|| self.intersects(&rhs.end) {
30+
// return true;
31+
// }
32+
33+
if !self.intersects(&rhs.bounding_rect()) {
34+
return false;
35+
}
36+
37+
/*
38+
o3 o2
39+
| /
40+
| /
41+
o0--o1
42+
If `rhs` line extended to infinity crosses any of these three lines,
43+
and bounding boxes intersect,
44+
then `rhs` (Line) intersects `self` (Rect).
45+
*/
46+
47+
let c0 = self.min();
48+
let c1 = coord! {x: self.max().x, y: self.min().y};
49+
let o0 = T::Ker::orient2d(rhs.start, rhs.end, c0);
50+
let o1 = T::Ker::orient2d(rhs.start, rhs.end, c1);
51+
if o0 != o1 {
52+
return true;
53+
}
54+
55+
let c2 = self.max();
56+
let o2 = T::Ker::orient2d(rhs.start, rhs.end, c2);
57+
if o0 != o2 {
58+
return true;
59+
}
60+
61+
let c3 = coord! {x: self.min().x, y: self.max().y};
62+
let o3 = T::Ker::orient2d(rhs.start, rhs.end, c3);
63+
if o0 != o3 {
64+
return true;
65+
}
66+
67+
// At this point we know all the orientations are equal and that the bounding boxes overlap.
68+
// The only ways there could be an intersection is if
69+
// 1. `self` (Rect) has degenerated to a line, and `rhs` (Line) is on that line.
70+
// 2. `self` (Rect) has degenerated to a point, and lies on `rhs` (Line).
71+
// 3. `rhs` (Line) is degenerated to a point.
72+
o0 == Orientation::Collinear
3773
}
3874
}
3975

@@ -117,10 +153,52 @@ where
117153
}
118154
}
119155

156+
#[cfg(test)]
157+
mod test_line {
158+
use super::*;
159+
use crate::wkt;
160+
161+
#[test]
162+
fn test_overlap_bbox_no_overlap() {
163+
let rect = wkt! {RECT(6 4, 10 0)};
164+
let line = wkt! {LINE(0 0, 10 10)};
165+
166+
assert!(!rect.intersects(&line));
167+
}
168+
169+
#[test]
170+
fn test_degen_line() {
171+
let rect = wkt! {RECT(0 0, 10 10)};
172+
let line = wkt! {LINE(0 0, 0 0)};
173+
174+
assert!(rect.intersects(&line));
175+
}
176+
177+
#[test]
178+
fn test_degen_rect() {
179+
let rect_pt = wkt! {RECT(0 0, 0 10)};
180+
let rect_line1 = wkt! {RECT(0 0, 0 10)};
181+
let rect_line2 = wkt! {RECT(0 0, 10 0)};
182+
let line = wkt! {LINE(0 0, 10 0)};
183+
184+
assert!(rect_pt.intersects(&line));
185+
assert!(rect_line1.intersects(&line));
186+
assert!(rect_line2.intersects(&line));
187+
}
188+
}
189+
120190
#[cfg(test)]
121191
mod test_triangle {
122192
use super::*;
123193

194+
#[test]
195+
fn test_rhs_degenerate_line() {
196+
let s: Rect<f64> = Rect::new((0, 0), (0, 0)).convert();
197+
let l = Line::new(coord! {x: 0.0, y: 0.0}, coord! { x: 0.0, y: 0.0 });
198+
199+
assert!(s.intersects(&l));
200+
}
201+
124202
#[test]
125203
fn test_disjoint() {
126204
let rect: Rect<f64> = Rect::new((0, 0), (10, 10)).convert();

0 commit comments

Comments
 (0)