1use crate::primitives::{cross2d, point_on_segment};
4
5pub fn point_strictly_inside_convex(px: i64, py: i64, ring: &[[i64; 2]]) -> bool {
13 let n = ring.len();
14 if n < 3 {
15 return false;
16 }
17 let mut all_pos = true;
18 let mut all_neg = true;
19 for i in 0..n {
20 let j = (i + 1) % n;
21 let cp = cross2d(ring[i][0], ring[i][1], ring[j][0], ring[j][1], px, py);
22 if cp <= 0 {
23 all_pos = false;
24 }
25 if cp >= 0 {
26 all_neg = false;
27 }
28 }
29 all_pos || all_neg
30}
31
32pub fn point_on_polygon_boundary(px: i64, py: i64, ring: &[[i64; 2]]) -> bool {
34 let n = ring.len();
35 for i in 0..n {
36 let j = (i + 1) % n;
37 if point_on_segment(px, py, ring[i][0], ring[i][1], ring[j][0], ring[j][1]) {
38 return true;
39 }
40 }
41 false
42}
43
44fn point_inside_polygon_ray_cast(px: i64, py: i64, ring: &[[i64; 2]]) -> bool {
48 let n = ring.len();
49 if n < 3 {
50 return false;
51 }
52 let mut crossings = 0i32;
53 for i in 0..n {
54 let j = (i + 1) % n;
55 let ax = ring[i][0];
56 let ay = ring[i][1];
57 let bx = ring[j][0];
58 let by = ring[j][1];
59 if (ay > py) != (by > py) {
61 let lhs = (py as i128 - ay as i128) * (bx as i128 - ax as i128);
66 let rhs = (px as i128 - ax as i128) * (by as i128 - ay as i128);
67 let to_right = if by > ay { lhs > rhs } else { lhs < rhs };
68 if to_right {
69 crossings += 1;
70 }
71 }
72 }
73 crossings % 2 == 1
74}
75
76pub fn point_inside_or_on_boundary(px: i64, py: i64, ring: &[[i64; 2]]) -> bool {
78 point_on_polygon_boundary(px, py, ring) || point_inside_polygon_ray_cast(px, py, ring)
79}
80
81pub fn collinear_segments_overlap_area(
91 a1x: i64,
92 a1y: i64,
93 a2x: i64,
94 a2y: i64,
95 b1x: i64,
96 b1y: i64,
97 b2x: i64,
98 b2y: i64,
99 a_ring: &[[i64; 2]], b_ring: &[[i64; 2]], ) -> bool {
102 let dax = (a2x as i128) - (a1x as i128);
103 let day = (a2y as i128) - (a1y as i128);
104 let dbx = (b2x as i128) - (b1x as i128);
105 let dby = (b2y as i128) - (b1y as i128);
106
107 if dax * dby != day * dbx {
109 return false;
110 }
111
112 let collinear_check = cross2d(a1x, a1y, a2x, a2y, b1x, b1y);
114 if collinear_check != 0 {
115 return false;
116 }
117
118 let has_overlap = if dax != 0 || dbx != 0 {
120 let (a_lo, a_hi) = (a1x.min(a2x), a1x.max(a2x));
122 let (b_lo, b_hi) = (b1x.min(b2x), b1x.max(b2x));
123 a_lo.max(b_lo) < a_hi.min(b_hi)
124 } else {
125 let (a_lo, a_hi) = (a1y.min(a2y), a1y.max(a2y));
127 let (b_lo, b_hi) = (b1y.min(b2y), b1y.max(b2y));
128 a_lo.max(b_lo) < a_hi.min(b_hi)
129 };
130
131 if !has_overlap {
132 return false;
133 }
134
135 let mut side_a: i128 = 0;
137 for point in a_ring {
138 let cp = cross2d(a1x, a1y, a2x, a2y, point[0], point[1]);
139 if cp != 0 {
140 side_a = cp;
141 break;
142 }
143 }
144
145 let mut side_b: i128 = 0;
146 for point in b_ring {
147 let cp = cross2d(a1x, a1y, a2x, a2y, point[0], point[1]);
148 if cp != 0 {
149 side_b = cp;
150 break;
151 }
152 }
153
154 if side_a == 0 || side_b == 0 {
155 return false;
156 } (side_a > 0) == (side_b > 0)
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 const M: i64 = 1_000_000;
168
169 fn square() -> Vec<[i64; 2]> {
170 vec![[0, 0], [M, 0], [M, M], [0, M]]
171 }
172
173 fn rhombus() -> Vec<[i64; 2]> {
174 vec![[0, 4 * M], [2 * M, 0], [0, -4 * M], [-2 * M, 0]]
175 }
176
177 #[test]
178 fn point_strictly_inside() {
179 assert!(point_strictly_inside_convex(M / 2, M / 2, &square()));
180 }
181
182 #[test]
183 fn point_strictly_inside_convex_rhombus_centroid_and_edge_neighbors() {
184 let ring = rhombus();
185
186 assert!(point_strictly_inside_convex(0, 0, &ring));
187
188 let edge_mid_x = M;
189 let edge_mid_y = 2 * M;
190 assert!(point_strictly_inside_convex(
191 edge_mid_x - 2,
192 edge_mid_y - 1,
193 &ring
194 ));
195 assert!(!point_strictly_inside_convex(
196 edge_mid_x + 2,
197 edge_mid_y + 1,
198 &ring
199 ));
200 }
201
202 #[test]
203 fn point_on_boundary_not_strictly_inside() {
204 assert!(!point_strictly_inside_convex(M / 2, 0, &square()));
206 }
207
208 #[test]
209 fn point_at_vertex_not_strictly_inside() {
210 assert!(!point_strictly_inside_convex(0, 0, &square()));
211 }
212
213 #[test]
214 fn point_outside() {
215 assert!(!point_strictly_inside_convex(2 * M, 2 * M, &square()));
216 }
217
218 #[test]
219 fn point_inside_or_on_boundary_interior() {
220 assert!(point_inside_or_on_boundary(M / 2, M / 2, &square()));
221 }
222
223 #[test]
224 fn point_inside_or_on_boundary_edge() {
225 assert!(point_inside_or_on_boundary(M / 2, 0, &square()));
226 }
227
228 #[test]
229 fn point_inside_or_on_boundary_vertex() {
230 assert!(point_inside_or_on_boundary(0, 0, &square()));
231 }
232
233 #[test]
234 fn point_inside_or_on_boundary_outside() {
235 assert!(!point_inside_or_on_boundary(2 * M, 0, &square()));
236 }
237
238 #[test]
239 fn point_on_polygon_boundary_edge_midpoint_and_off_edge() {
240 let ring = rhombus();
241
242 assert!(point_on_polygon_boundary(M, 2 * M, &ring));
243 assert!(!point_on_polygon_boundary(M + 2, 2 * M + 1, &ring));
244 }
245
246 #[test]
247 fn point_inside_or_on_boundary_inclusive_cases() {
248 let ring = rhombus();
249
250 assert!(point_inside_or_on_boundary(0, 4 * M, &ring));
251 assert!(point_inside_or_on_boundary(M, 2 * M, &ring));
252 assert!(point_inside_or_on_boundary(0, 0, &ring));
253 assert!(!point_inside_or_on_boundary(3 * M, 3 * M, &ring));
254 }
255
256 fn l_shape() -> Vec<[i64; 2]> {
258 vec![
259 [0, 0], [60 * M, 0], [60 * M, 40 * M], [30 * M, 40 * M],
260 [30 * M, 80 * M], [0, 80 * M],
261 ]
262 }
263
264 #[test]
265 fn point_inside_or_on_boundary_non_convex_l_shape_interior() {
266 let ring = l_shape();
267 assert!(point_inside_or_on_boundary(15 * M, 20 * M, &ring));
269 assert!(point_inside_or_on_boundary(15 * M, 60 * M, &ring));
271 assert!(!point_inside_or_on_boundary(45 * M, 60 * M, &ring));
273 }
274
275 #[test]
276 fn ray_cast_non_convex_star_interior() {
277 let arrow: Vec<[i64; 2]> = vec![
279 [0, 30 * M], [60 * M, 30 * M], [60 * M, 0],
280 [100 * M, 50 * M], [60 * M, 100 * M], [60 * M, 70 * M], [0, 70 * M],
281 ];
282 assert!(point_inside_or_on_boundary(30 * M, 50 * M, &arrow));
284 assert!(!point_inside_or_on_boundary(30 * M, 90 * M, &arrow));
286 }
287
288 #[test]
289 fn collinear_overlap_same_side_returns_true() {
290 let a_ring = vec![[0, 0], [2 * M, 0], [2 * M, M], [0, M]];
296 let b_ring = vec![[M, 0], [3 * M, 0], [3 * M, M], [M, M]];
297
298 let result = collinear_segments_overlap_area(
299 0,
300 0,
301 2 * M,
302 0, M,
304 0,
305 3 * M,
306 0, &a_ring,
308 &b_ring,
309 );
310 assert!(result);
312 }
313
314 #[test]
315 fn adjacent_polygons_opposite_sides_no_overlap() {
316 let a_ring = vec![[0, 0], [2 * M, 0], [2 * M, M], [0, M]]; let b_ring = vec![[0, 0], [2 * M, 0], [2 * M, -M], [0, -M]]; let result =
321 collinear_segments_overlap_area(0, 0, 2 * M, 0, 0, 0, 2 * M, 0, &a_ring, &b_ring);
322 assert!(!result);
324 }
325
326 #[test]
327 fn non_collinear_segments_return_false() {
328 let a_ring = vec![[0, 0], [M, 0], [M, M], [0, M]]; let b_ring = vec![[2 * M, 0], [3 * M, 0], [3 * M, M], [2 * M, M]]; let result = collinear_segments_overlap_area(
333 0,
334 0,
335 M,
336 0, 2 * M,
338 0,
339 2 * M,
340 M, &a_ring,
342 &b_ring,
343 );
344 assert!(!result);
345 }
346}