use crate::aabb::Aabb;
use crate::spatial::point_inside_or_on_boundary;
pub fn point_inside_any_part(parts: &[Vec<[i64; 2]>], x: i64, y: i64) -> bool {
for part in parts {
if point_inside_or_on_boundary(x, y, part) {
return true;
}
}
false
}
pub fn contains_polygon(outer_parts: &[Vec<[i64; 2]>], inner_parts: &[Vec<[i64; 2]>]) -> bool {
if outer_parts.is_empty() || inner_parts.is_empty() {
return false;
}
let outer_aabb = compute_multi_part_aabb(outer_parts);
let inner_aabb = compute_multi_part_aabb(inner_parts);
if inner_aabb.min_x < outer_aabb.min_x
|| inner_aabb.max_x > outer_aabb.max_x
|| inner_aabb.min_y < outer_aabb.min_y
|| inner_aabb.max_y > outer_aabb.max_y
{
return false;
}
for part in inner_parts {
if !part_is_inside(outer_parts, part) {
return false;
}
}
true
}
fn part_is_inside(outer_parts: &[Vec<[i64; 2]>], inner_part: &[[i64; 2]]) -> bool {
for &v in inner_part {
if !point_inside_any_part(outer_parts, v[0], v[1]) {
return false;
}
}
if outer_parts.len() > 1 {
let n = inner_part.len();
for i in 0..n {
let j = (i + 1) % n;
let x1 = inner_part[i][0];
let y1 = inner_part[i][1];
let x2 = inner_part[j][0];
let y2 = inner_part[j][1];
let sx1 = (2 * x1 + x2) / 3;
let sy1 = (2 * y1 + y2) / 3;
if !point_inside_any_part(outer_parts, sx1, sy1) {
return false;
}
let sx2 = (x1 + 2 * x2) / 3;
let sy2 = (y1 + 2 * y2) / 3;
if !point_inside_any_part(outer_parts, sx2, sy2) {
return false;
}
}
}
true
}
fn compute_multi_part_aabb(parts: &[Vec<[i64; 2]>]) -> Aabb {
let ring: Vec<[i64; 2]> = parts.iter().flat_map(|p| p.iter().copied()).collect();
Aabb::from_ring(&ring)
}
#[cfg(test)]
mod tests {
use super::*;
const M: i64 = 1_000_000;
fn square(ox: i64, oy: i64, size: i64) -> Vec<[i64; 2]> {
vec![
[ox, oy],
[ox + size, oy],
[ox + size, oy + size],
[ox, oy + size],
]
}
#[test]
fn small_square_inside_large_square() {
let outer = vec![square(0, 0, 10 * M)];
let inner = vec![square(2 * M, 2 * M, 3 * M)];
assert!(contains_polygon(&outer, &inner));
}
#[test]
fn touching_outer_boundary_is_not_contained() {
let outer = vec![square(0, 0, 5 * M), square(7 * M, 0, 5 * M)];
let inner = vec![square(5 * M, 2 * M, 4 * M)];
assert!(!contains_polygon(&outer, &inner));
}
#[test]
fn partially_outside_returns_false() {
let outer = vec![square(0, 0, 10 * M)];
let inner = vec![square(8 * M, 8 * M, 5 * M)]; assert!(!contains_polygon(&outer, &inner));
}
#[test]
fn completely_outside_returns_false() {
let outer = vec![square(0, 0, M)];
let inner = vec![square(3 * M, 3 * M, M)];
assert!(!contains_polygon(&outer, &inner));
}
#[test]
fn same_polygon_contains_itself() {
let polygon = vec![square(0, 0, 10 * M)];
assert!(contains_polygon(&polygon, &polygon));
}
#[test]
fn multipart_outer_contains_inner() {
let outer = vec![square(0, 0, 10 * M), square(10 * M, 0, 10 * M)];
let inner = vec![square(12 * M, 2 * M, 5 * M)];
assert!(contains_polygon(&outer, &inner));
}
#[test]
fn multipart_outer_inner_bridges_gap_returns_false() {
let outer = vec![square(0, 0, 5 * M), square(7 * M, 0, 5 * M)];
let inner = vec![square(4 * M, 2 * M, 4 * M)];
assert!(!contains_polygon(&outer, &inner));
}
#[test]
fn edge_sampling_catches_concave_bridge() {
let outer = vec![
vec![[0, 0], [10 * M, 0], [10 * M, 5 * M], [0, 5 * M]],
vec![
[5 * M, 5 * M],
[10 * M, 5 * M],
[10 * M, 10 * M],
[5 * M, 10 * M],
],
];
let inner = vec![vec![[2 * M, 4 * M], [6 * M, 6 * M], [6 * M, 4 * M]]];
assert!(!contains_polygon(&outer, &inner));
}
#[test]
fn empty_outer_returns_false() {
let inner = vec![square(0, 0, M)];
assert!(!contains_polygon(&[], &inner));
}
#[test]
fn empty_inner_returns_false() {
let outer = vec![square(0, 0, M)];
assert!(!contains_polygon(&outer, &[]));
}
#[test]
fn point_inside_any_part_works() {
let parts = vec![
square(0, 0, 10 * M),
square(12 * M, 0, 10 * M),
square(24 * M, 0, 10 * M),
];
assert!(point_inside_any_part(&parts, 5 * M, 5 * M));
assert!(point_inside_any_part(&parts, 17 * M, 5 * M));
assert!(point_inside_any_part(&parts, 12 * M, 5 * M));
assert!(!point_inside_any_part(&parts, 35 * M, 5 * M));
}
#[test]
fn multipart_inner_fully_contained() {
let outer = vec![square(0, 0, 20 * M)];
let inner = vec![square(1 * M, 1 * M, 3 * M), square(5 * M, 5 * M, 3 * M)];
assert!(contains_polygon(&outer, &inner));
}
#[test]
fn multipart_inner_one_part_outside() {
let outer = vec![square(0, 0, 10 * M)];
let inner = vec![square(1 * M, 1 * M, 3 * M), square(15 * M, 15 * M, 3 * M)];
assert!(!contains_polygon(&outer, &inner));
}
}