mapping_algorithms/polygons/
point_in_polygon.rs1use nalgebra::{Point2, RealField, Vector2};
25use num_traits::{AsPrimitive, Bounded};
26
27use crate::Vec;
28
29use super::calculate_polygon_extents;
30
31#[inline]
32#[cfg_attr(
33 feature = "tracing",
34 tracing::instrument("Does Ray Intersect Polygon", skip_all, level = "trace")
35)]
36fn does_ray_intersect_polygon_segment<T>(
37 point: &Vector2<T>,
38 vertex1: Point2<T>,
39 vertex2: Point2<T>,
40) -> bool
41where
42 T: Copy + RealField,
43 f32: AsPrimitive<T>,
44{
45 if point.y > vertex1.y.min(vertex2.y)
46 && point.y <= vertex1.y.max(vertex2.y)
47 && point.x <= vertex1.x.max(vertex2.x)
48 {
49 let origin_x = (vertex1.y != vertex2.y)
50 .then(|| {
51 (point.y - vertex1.y) * (vertex2.x - vertex1.x) / (vertex2.y - vertex1.y)
52 + vertex1.x
53 })
54 .unwrap_or(point.x);
55
56 if vertex1.x == vertex2.x || point.x <= origin_x {
57 return true;
58 }
59 }
60
61 false
62}
63
64#[cfg_attr(
76 feature = "tracing",
77 tracing::instrument("Is Point In Polygon", skip_all, level = "debug")
78)]
79pub fn is_single_point_in_polygon<T>(point: &Point2<T>, polygon: &[Point2<T>]) -> bool
80where
81 T: Copy + RealField,
82 f32: AsPrimitive<T>,
83{
84 let polygon_len = polygon.len();
85 (0..polygon_len)
86 .filter_map(|current_vertex_idx| {
87 let current_vertex = polygon[current_vertex_idx];
88 let next_vertex = polygon[(current_vertex_idx + 1) % polygon_len];
89
90 does_ray_intersect_polygon_segment(&point.coords, current_vertex, next_vertex)
91 .then_some(1)
92 })
93 .sum::<usize>()
94 % 2
95 == 1 }
97
98#[cfg_attr(
111 feature = "tracing",
112 tracing::instrument("Are Points In Polygon", skip_all, level = "info")
113)]
114pub fn are_multiple_points_in_polygon<T>(
115 points: &[Point2<T>],
116 polygon: &[Point2<T>],
117) -> Option<Vec<bool>>
118where
119 T: Bounded + Copy + RealField,
120 f32: AsPrimitive<T>,
121{
122 if polygon.len() < 3 {
123 return None;
124 }
125 let polygon_extents = calculate_polygon_extents(polygon)?;
126
127 Some(
128 points
129 .iter()
130 .map(|current_point| {
131 polygon_extents
133 .iter()
134 .zip(current_point.coords.iter())
135 .fold(
136 true,
137 |is_in_extents, (extent_for_dimension, vertex_coord)| {
138 is_in_extents && extent_for_dimension.contains(vertex_coord)
139 },
140 )
141 && is_single_point_in_polygon(current_point, polygon)
142 })
143 .collect(),
144 )
145}
146
147#[cfg(feature = "pregenerated")]
148macro_rules! impl_p_i_p_algorithm {
149 ($prec:expr, doc $doc:tt) => {
150 ::paste::paste! {
151 pub(super) mod [<$doc _precision>] {
152 use nalgebra::Point2;
153 use crate::Vec;
154
155 #[doc = "A premade variant of the single point-in-polygon algorithm function, made for " $doc " precision floating-point arithmetic."]
156 pub fn is_single_point_in_polygon(point: &Point2<$prec>, polygon: &[Point2<$prec>]) -> bool {
157 super::is_single_point_in_polygon(point, polygon)
158 }
159
160 #[doc = "A premade variant of the multiple point-in-polygon algorithm function, made for " $doc " precision floating-point arithmetic."]
161 pub fn are_multiple_points_in_polygon(
162 points: &[Point2<$prec>],
163 polygon: &[Point2<$prec>],
164 ) -> Option<Vec<bool>> {
165 super::are_multiple_points_in_polygon(points, polygon)
166 }
167 }
168 }
169 };
170}
171
172#[cfg(feature = "pregenerated")]
173impl_p_i_p_algorithm!(f32, doc single);
174#[cfg(feature = "pregenerated")]
175impl_p_i_p_algorithm!(f64, doc double);
176
177#[cfg(test)]
178mod tests {
179 use nalgebra::{Point2, Vector2};
180
181 use crate::Vec;
182
183 use super::*;
184
185 fn get_polygon_for_tests() -> Vec<Point2<f32>> {
186 Vec::from([
187 Point2::from([0.0, 0.0]),
188 Point2::from([1.0, 1.2]),
189 Point2::from([1.4, 1.2]),
190 Point2::from([1.4, 2.0]),
191 Point2::from([0.5, 1.8]),
192 Point2::from([-2.0, 0.2]),
193 Point2::from([-1.2, -0.4]),
194 Point2::from([-0.3, -0.4]),
195 ])
196 }
197
198 #[test]
199 fn test_does_ray_intersect() {
200 let point_a = Vector2::new(0.5, -1.5);
201 let vertex_a1 = Point2::new(5.0, 0.0);
202 let vertex_a2 = Point2::new(1.0, -4.0);
203 assert!(does_ray_intersect_polygon_segment(
204 &point_a, vertex_a1, vertex_a2
205 ));
206
207 let point_b = Vector2::new(-0.5, -1.5);
208 let vertex_b1 = Point2::new(0.0, 0.0);
209 let vertex_b2 = Point2::new(1.0, 5.0);
210 assert!(!does_ray_intersect_polygon_segment(
211 &point_b, vertex_b1, vertex_b2
212 ));
213 }
214
215 #[test]
216 fn test_is_single_points_in_polygon() {
217 let polygon = get_polygon_for_tests();
218
219 let point = Point2::from([0.5, 1.5]);
220
221 assert!(is_single_point_in_polygon(&point, &polygon));
222 }
223
224 #[test]
225 fn test_multiple_points_in_polygon_clockwise() {
226 let polygon = get_polygon_for_tests();
227
228 let points = &[
230 Point2::from([0.5, 1.5]), Point2::from([1.5, 1.5]), ];
233
234 let result = are_multiple_points_in_polygon(points, &polygon);
235
236 assert_eq!(result, Some(Vec::from([true, false])));
238 }
239
240 #[test]
241 fn test_multiple_points_in_polygon_counter_clockwise() {
242 let polygon = get_polygon_for_tests();
243
244 let points = &[
246 Point2::from([0.5, 1.5]), Point2::from([1.5, 1.5]), ];
249
250 let result = are_multiple_points_in_polygon(points, &polygon);
251
252 assert_eq!(result, Some(Vec::from([true, false])));
254 }
255}