Skip to content
Merged
4 changes: 4 additions & 0 deletions geo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ wkt = "0.10.1"
name = "area"
harness = false

[[bench]]
name = "coordinate_position"
harness = false

[[bench]]
name = "contains"
harness = false
Expand Down
20 changes: 19 additions & 1 deletion geo/benches/contains.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{criterion_group, criterion_main, Criterion};
use geo::contains::Contains;
use geo::{point, polygon, Line, Point, Polygon};
use geo::{point, polygon, Line, Point, Polygon, Triangle};

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("point in simple polygon", |bencher| {
Expand Down Expand Up @@ -110,6 +110,24 @@ fn criterion_benchmark(c: &mut Criterion) {
);
});
});

c.bench_function("Triangle contains point", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (5., 10.)]);
let point = Point::new(5., 5.);

bencher.iter(|| {
assert!(criterion::black_box(&triangle).contains(criterion::black_box(&point)));
});
});

c.bench_function("Triangle contains point on edge", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (6., 10.)]);
let point = Point::new(3., 5.);

bencher.iter(|| {
assert!(!criterion::black_box(&triangle).contains(criterion::black_box(&point)));
});
});
}

criterion_group!(benches, criterion_benchmark);
Expand Down
69 changes: 69 additions & 0 deletions geo/benches/coordinate_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#[macro_use]
extern crate criterion;
extern crate geo;

use geo::{
coordinate_position::CoordPos, BoundingRect, Centroid, CoordinatePosition, Point, Rect,
Triangle,
};

use criterion::Criterion;

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("Point position to rect", |bencher| {
let plot_centroids: Vec<Point> = geo_test_fixtures::nl_plots()
.iter()
.map(|plot| plot.centroid().unwrap())
.collect();
let zone_bbox: Vec<Rect> = geo_test_fixtures::nl_zones()
.iter()
.map(|plot| plot.bounding_rect().unwrap())
.collect();
bencher.iter(|| {
let mut inside = 0;
let mut outsied = 0;
let mut boundary = 0;

for a in &plot_centroids {
for b in &zone_bbox {
match criterion::black_box(b).coordinate_position(criterion::black_box(&a.0)) {
CoordPos::OnBoundary => boundary += 1,
CoordPos::Inside => inside += 1,
CoordPos::Outside => outsied += 1,
}
}
}

assert_eq!(inside, 2246);
assert_eq!(outsied, 26510);
assert_eq!(boundary, 0);
});
});

c.bench_function("Point in triangle", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (5., 10.)]);
let point = Point::new(5., 5.);

bencher.iter(|| {
assert!(
criterion::black_box(&triangle).coordinate_position(criterion::black_box(&point.0))
!= CoordPos::Outside
);
});
});

c.bench_function("Point on triangle boundary", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (6., 10.)]);
let point = Point::new(3., 5.);

bencher.iter(|| {
assert!(
criterion::black_box(&triangle).coordinate_position(criterion::black_box(&point.0))
== CoordPos::OnBoundary
);
});
});
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
101 changes: 100 additions & 1 deletion geo/benches/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,110 @@ fn rect_intersection(c: &mut Criterion) {
});
}

fn point_rect_intersection(c: &mut Criterion) {
use geo::algorithm::{BoundingRect, Centroid};
use geo::geometry::{Point, Rect};
let plot_centroids: Vec<Point> = geo_test_fixtures::nl_plots()
.iter()
.map(|plot| plot.centroid().unwrap())
.collect();
let zone_bbox: Vec<Rect> = geo_test_fixtures::nl_zones()
.iter()
.map(|plot| plot.bounding_rect().unwrap())
.collect();

c.bench_function("Point intersects rect", |bencher| {
bencher.iter(|| {
let mut intersects = 0;
let mut non_intersects = 0;

for a in &plot_centroids {
for b in &zone_bbox {
if criterion::black_box(a.intersects(b)) {
intersects += 1;
} else {
non_intersects += 1;
}
}
}

assert_eq!(intersects, 2246);
assert_eq!(non_intersects, 26510);
});
});
}

fn point_triangle_intersection(c: &mut Criterion) {
use geo::{Centroid, TriangulateEarcut};
use geo_types::{Point, Triangle};
let plot_centroids: Vec<Point> = geo_test_fixtures::nl_plots()
.iter()
.map(|plot| plot.centroid().unwrap())
.collect();
let zone_triangles: Vec<Triangle> = geo_test_fixtures::nl_zones()
.iter()
.flat_map(|plot| plot.earcut_triangles_iter())
.collect();

c.bench_function("Point intersects triangle", |bencher| {
bencher.iter(|| {
let mut intersects = 0;
let mut non_intersects = 0;

for a in &plot_centroids {
for b in &zone_triangles {
if criterion::black_box(a.intersects(b)) {
intersects += 1;
} else {
non_intersects += 1;
}
}
}

assert_eq!(intersects, 533);
assert_eq!(non_intersects, 5450151);
});
});

c.bench_function("Triangle intersects point", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (5., 10.)]);
let point = Point::new(5., 5.);

bencher.iter(|| {
assert!(criterion::black_box(&triangle).intersects(criterion::black_box(&point)));
});
});

c.bench_function("Triangle intersects point on edge", |bencher| {
let triangle = Triangle::from([(0., 0.), (10., 0.), (6., 10.)]);
let point = Point::new(3., 5.);

bencher.iter(|| {
assert!(criterion::black_box(&triangle).intersects(criterion::black_box(&point)));
});
});
}

criterion_group! {
name = bench_multi_polygons;
config = Criterion::default().sample_size(10);
targets = multi_polygon_intersection
}
criterion_group!(bench_rects, rect_intersection);
criterion_group! {
name = bench_point_rect;
config = Criterion::default().sample_size(50);
targets = point_rect_intersection
}
criterion_group! {
name = bench_point_triangle;
config = Criterion::default().sample_size(50);
targets = point_triangle_intersection
}

criterion_main!(bench_multi_polygons, bench_rects);
criterion_main!(
bench_multi_polygons,
bench_rects,
bench_point_rect,
bench_point_triangle
);
30 changes: 27 additions & 3 deletions geo/src/algorithm/contains/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,33 @@ where
T: GeoNum,
{
fn contains(&self, coord: &Coord<T>) -> bool {
let ls = LineString::new(vec![self.0, self.1, self.2, self.0]);
use crate::utils::{coord_pos_relative_to_ring, CoordPos};
coord_pos_relative_to_ring(*coord, &ls) == CoordPos::Inside
// leverageing robust predicates
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with this impl. if it's correct, rather than the non-robust one.

For quick intro. to robust, you may want to refer this example that explains why it's important.

// use crate::algorithm::kernels::Kernel;
// use crate::Orientation
// self.to_lines()
// .map(|l| T::Ker::orient2d(l.start, l.end, *coord))
// .windows(2)
// .all(|win| win[0] == win[1] && win[0] != Orientation::Collinear)

// neglecting robust prdicates, hence faster
let p0x = self.0.x.to_f64().unwrap();
let p0y = self.0.y.to_f64().unwrap();
let p1x = self.1.x.to_f64().unwrap();
let p1y = self.1.y.to_f64().unwrap();
let p2x = self.2.x.to_f64().unwrap();
let p2y = self.2.y.to_f64().unwrap();

let px = coord.x.to_f64().unwrap();
let py = coord.y.to_f64().unwrap();

let a = 0.5 * (-p1y * p2x + p0y * (-p1x + p2x) + p0x * (p1y - p2y) + p1x * p2y);

let sign = a.signum();

let s = (p0y * p2x - p0x * p2y + (p2y - p0y) * px + (p0x - p2x) * py) * sign;
let t = (p0x * p1y - p0y * p1x + (p0y - p1y) * px + (p1x - p0x) * py) * sign;

s > 0. && t > 0. && (s + t) < 2. * a * sign
}
}

Expand Down
57 changes: 50 additions & 7 deletions geo/src/algorithm/coordinate_position.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::cmp::Ordering;

use crate::geometry::*;
use crate::intersects::value_in_between;
use crate::intersects::{point_in_rect, value_in_between};
use crate::kernels::*;
use crate::{BoundingRect, HasDimensions, Intersects};
use crate::{GeoNum, GeometryCow};
Expand Down Expand Up @@ -184,9 +186,20 @@ where
is_inside: &mut bool,
boundary_count: &mut usize,
) {
// PERF TODO: I'm sure there's a better way to calculate than converting to a polygon
self.to_polygon()
.calculate_coordinate_position(coord, is_inside, boundary_count);
*is_inside = self
.to_lines()
.map(|l| {
let orientation = T::Ker::orient2d(l.start, l.end, *coord);
if orientation == Orientation::Collinear
&& point_in_rect(*coord, l.start, l.end)
&& coord.x != l.end.x
{
*boundary_count += 1;
}
orientation
})
.windows(2)
.all(|win| win[0] == win[1] && win[0] != Orientation::Collinear);
}
}

Expand All @@ -201,9 +214,39 @@ where
is_inside: &mut bool,
boundary_count: &mut usize,
) {
// PERF TODO: I'm sure there's a better way to calculate than converting to a polygon
self.to_polygon()
.calculate_coordinate_position(coord, is_inside, boundary_count);
let mut boundary = false;

let min = self.min();

match coord.x.partial_cmp(&min.x).unwrap() {
Ordering::Less => return,
Ordering::Equal => boundary = true,
Ordering::Greater => {}
}
match coord.y.partial_cmp(&min.y).unwrap() {
Ordering::Less => return,
Ordering::Equal => boundary = true,
Ordering::Greater => {}
}

let max = self.max();

match max.x.partial_cmp(&coord.x).unwrap() {
Ordering::Less => return,
Ordering::Equal => boundary = true,
Ordering::Greater => {}
}
match max.y.partial_cmp(&coord.y).unwrap() {
Ordering::Less => return,
Ordering::Equal => boundary = true,
Ordering::Greater => {}
}

if boundary {
*boundary_count += 1;
} else {
*is_inside = true;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions geo/src/algorithm/intersects/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ where
symmetric_intersects_impl!(Coord<T>, Geometry<T>);
symmetric_intersects_impl!(Line<T>, Geometry<T>);
symmetric_intersects_impl!(Rect<T>, Geometry<T>);
symmetric_intersects_impl!(Triangle<T>, Geometry<T>);
symmetric_intersects_impl!(Polygon<T>, Geometry<T>);

impl<T, G> Intersects<G> for GeometryCollection<T>
Expand All @@ -43,4 +44,5 @@ where
symmetric_intersects_impl!(Coord<T>, GeometryCollection<T>);
symmetric_intersects_impl!(Line<T>, GeometryCollection<T>);
symmetric_intersects_impl!(Rect<T>, GeometryCollection<T>);
symmetric_intersects_impl!(Triangle<T>, GeometryCollection<T>);
symmetric_intersects_impl!(Polygon<T>, GeometryCollection<T>);
10 changes: 10 additions & 0 deletions geo/src/algorithm/intersects/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ where
}
}
}

impl<T> Intersects<Triangle<T>> for Line<T>
where
T: GeoNum,
{
fn intersects(&self, rhs: &Triangle<T>) -> bool {
self.intersects(&rhs.to_polygon())
}
}
symmetric_intersects_impl!(Triangle<T>, Line<T>);
2 changes: 2 additions & 0 deletions geo/src/algorithm/intersects/line_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ where
symmetric_intersects_impl!(Coord<T>, LineString<T>);
symmetric_intersects_impl!(Line<T>, LineString<T>);
symmetric_intersects_impl!(Rect<T>, LineString<T>);
symmetric_intersects_impl!(Triangle<T>, LineString<T>);

// Blanket implementation from LineString<T>
impl<T, G> Intersects<G> for MultiLineString<T>
Expand All @@ -38,3 +39,4 @@ where
symmetric_intersects_impl!(Point<T>, MultiLineString<T>);
symmetric_intersects_impl!(Line<T>, MultiLineString<T>);
symmetric_intersects_impl!(Rect<T>, MultiLineString<T>);
symmetric_intersects_impl!(Triangle<T>, MultiLineString<T>);
1 change: 1 addition & 0 deletions geo/src/algorithm/intersects/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ where

symmetric_intersects_impl!(Coord<T>, MultiPoint<T>);
symmetric_intersects_impl!(Line<T>, MultiPoint<T>);
symmetric_intersects_impl!(Triangle<T>, MultiPoint<T>);
symmetric_intersects_impl!(Polygon<T>, MultiPoint<T>);
Loading