1use crate::aabb::Aabb;
10use crate::spatial::point_inside_or_on_boundary;
11
12pub fn point_inside_any_part(parts: &[Vec<[i64; 2]>], x: i64, y: i64) -> bool {
14 for part in parts {
15 if point_inside_or_on_boundary(x, y, part) {
16 return true;
17 }
18 }
19 false
20}
21
22pub fn contains_polygon(outer_parts: &[Vec<[i64; 2]>], inner_parts: &[Vec<[i64; 2]>]) -> bool {
34 if outer_parts.is_empty() || inner_parts.is_empty() {
35 return false;
36 }
37
38 let outer_aabb = compute_multi_part_aabb(outer_parts);
40 let inner_aabb = compute_multi_part_aabb(inner_parts);
41
42 if inner_aabb.min_x < outer_aabb.min_x
43 || inner_aabb.max_x > outer_aabb.max_x
44 || inner_aabb.min_y < outer_aabb.min_y
45 || inner_aabb.max_y > outer_aabb.max_y
46 {
47 return false;
48 }
49
50 for part in inner_parts {
52 if !part_is_inside(outer_parts, part) {
53 return false;
54 }
55 }
56
57 true
58}
59
60fn part_is_inside(outer_parts: &[Vec<[i64; 2]>], inner_part: &[[i64; 2]]) -> bool {
63 for &v in inner_part {
65 if !point_inside_any_part(outer_parts, v[0], v[1]) {
66 return false;
67 }
68 }
69
70 if outer_parts.len() > 1 {
73 let n = inner_part.len();
74 for i in 0..n {
75 let j = (i + 1) % n;
76 let x1 = inner_part[i][0];
77 let y1 = inner_part[i][1];
78 let x2 = inner_part[j][0];
79 let y2 = inner_part[j][1];
80
81 let sx1 = (2 * x1 + x2) / 3;
83 let sy1 = (2 * y1 + y2) / 3;
84 if !point_inside_any_part(outer_parts, sx1, sy1) {
85 return false;
86 }
87
88 let sx2 = (x1 + 2 * x2) / 3;
90 let sy2 = (y1 + 2 * y2) / 3;
91 if !point_inside_any_part(outer_parts, sx2, sy2) {
92 return false;
93 }
94 }
95 }
96
97 true
98}
99
100fn compute_multi_part_aabb(parts: &[Vec<[i64; 2]>]) -> Aabb {
102 let ring: Vec<[i64; 2]> = parts.iter().flat_map(|p| p.iter().copied()).collect();
103 Aabb::from_ring(&ring)
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 const M: i64 = 1_000_000;
111
112 fn square(ox: i64, oy: i64, size: i64) -> Vec<[i64; 2]> {
113 vec![
114 [ox, oy],
115 [ox + size, oy],
116 [ox + size, oy + size],
117 [ox, oy + size],
118 ]
119 }
120
121 #[test]
122 fn small_square_inside_large_square() {
123 let outer = vec![square(0, 0, 10 * M)];
124 let inner = vec![square(2 * M, 2 * M, 3 * M)];
125 assert!(contains_polygon(&outer, &inner));
126 }
127
128 #[test]
129 fn touching_outer_boundary_is_not_contained() {
130 let outer = vec![square(0, 0, 5 * M), square(7 * M, 0, 5 * M)];
131 let inner = vec![square(5 * M, 2 * M, 4 * M)];
132
133 assert!(!contains_polygon(&outer, &inner));
134 }
135
136 #[test]
137 fn partially_outside_returns_false() {
138 let outer = vec![square(0, 0, 10 * M)];
139 let inner = vec![square(8 * M, 8 * M, 5 * M)]; assert!(!contains_polygon(&outer, &inner));
141 }
142
143 #[test]
144 fn completely_outside_returns_false() {
145 let outer = vec![square(0, 0, M)];
146 let inner = vec![square(3 * M, 3 * M, M)];
147 assert!(!contains_polygon(&outer, &inner));
148 }
149
150 #[test]
151 fn same_polygon_contains_itself() {
152 let polygon = vec![square(0, 0, 10 * M)];
153 assert!(contains_polygon(&polygon, &polygon));
154 }
155
156 #[test]
157 fn multipart_outer_contains_inner() {
158 let outer = vec![square(0, 0, 10 * M), square(10 * M, 0, 10 * M)];
160 let inner = vec![square(12 * M, 2 * M, 5 * M)];
162 assert!(contains_polygon(&outer, &inner));
163 }
164
165 #[test]
166 fn multipart_outer_inner_bridges_gap_returns_false() {
167 let outer = vec![square(0, 0, 5 * M), square(7 * M, 0, 5 * M)];
171 let inner = vec![square(4 * M, 2 * M, 4 * M)];
173 assert!(!contains_polygon(&outer, &inner));
174 }
175
176 #[test]
177 fn edge_sampling_catches_concave_bridge() {
178 let outer = vec![
182 vec![[0, 0], [10 * M, 0], [10 * M, 5 * M], [0, 5 * M]],
183 vec![
184 [5 * M, 5 * M],
185 [10 * M, 5 * M],
186 [10 * M, 10 * M],
187 [5 * M, 10 * M],
188 ],
189 ];
190 let inner = vec![vec![[2 * M, 4 * M], [6 * M, 6 * M], [6 * M, 4 * M]]];
193 assert!(!contains_polygon(&outer, &inner));
194 }
195
196 #[test]
197 fn empty_outer_returns_false() {
198 let inner = vec![square(0, 0, M)];
199 assert!(!contains_polygon(&[], &inner));
200 }
201
202 #[test]
203 fn empty_inner_returns_false() {
204 let outer = vec![square(0, 0, M)];
205 assert!(!contains_polygon(&outer, &[]));
206 }
207
208 #[test]
209 fn point_inside_any_part_works() {
210 let parts = vec![
211 square(0, 0, 10 * M),
212 square(12 * M, 0, 10 * M),
213 square(24 * M, 0, 10 * M),
214 ];
215 assert!(point_inside_any_part(&parts, 5 * M, 5 * M));
216 assert!(point_inside_any_part(&parts, 17 * M, 5 * M));
217 assert!(point_inside_any_part(&parts, 12 * M, 5 * M));
218 assert!(!point_inside_any_part(&parts, 35 * M, 5 * M));
219 }
220
221 #[test]
222 fn multipart_inner_fully_contained() {
223 let outer = vec![square(0, 0, 20 * M)];
224 let inner = vec![square(1 * M, 1 * M, 3 * M), square(5 * M, 5 * M, 3 * M)];
225 assert!(contains_polygon(&outer, &inner));
226 }
227
228 #[test]
229 fn multipart_inner_one_part_outside() {
230 let outer = vec![square(0, 0, 10 * M)];
231 let inner = vec![square(1 * M, 1 * M, 3 * M), square(15 * M, 15 * M, 3 * M)];
232 assert!(!contains_polygon(&outer, &inner));
233 }
234}