Skip to main content

exact_poly/
overlap.rs

1//! Convex parts area overlap detection.
2
3/// True if two convex polygons share area (not just edges).
4/// Adjacent parts that share an edge have opposite interior sides → return false.
5/// Overlapping parts share area → return true.
6///
7pub fn convex_parts_overlap(a: &[[i64; 2]], b: &[[i64; 2]]) -> bool {
8    crate::sat::sat_overlaps(a, b)
9}
10
11/// Find all overlapping part pairs between two sets of parts.
12/// Returns list of (index_from_a, index_from_b) pairs.
13pub fn find_overlapping_parts(
14    a_parts: &[Vec<[i64; 2]>],
15    b_parts: &[Vec<[i64; 2]>],
16) -> Vec<(usize, usize)> {
17    let mut overlaps = Vec::new();
18
19    for (i, a) in a_parts.iter().enumerate() {
20        for (j, b) in b_parts.iter().enumerate() {
21            if convex_parts_overlap(a, b) {
22                overlaps.push((i, j));
23            }
24        }
25    }
26
27    overlaps
28}
29
30/// True if any part from `a_parts` overlaps any part from `b_parts`.
31pub fn parts_overlap(a_parts: &[Vec<[i64; 2]>], b_parts: &[Vec<[i64; 2]>]) -> bool {
32    for a in a_parts {
33        for b in b_parts {
34            if convex_parts_overlap(a, b) {
35                return true;
36            }
37        }
38    }
39    false
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    const M: i64 = 1_000_000;
47
48    fn square(ox: i64, oy: i64, size: i64) -> Vec<[i64; 2]> {
49        vec![
50            [ox, oy],
51            [ox + size, oy],
52            [ox + size, oy + size],
53            [ox, oy + size],
54        ]
55    }
56
57    #[test]
58    fn adjacent_squares_no_overlap() {
59        // Adjacent squares sharing edge at x=M — NOT overlapping
60        let a = square(0, 0, M);
61        let b = square(M, 0, M);
62        assert!(
63            !convex_parts_overlap(&a, &b),
64            "adjacent parts sharing an edge must NOT overlap"
65        );
66    }
67
68    #[test]
69    fn one_pixel_penetration_is_overlap() {
70        let a = square(0, 0, M);
71        let b = square(M - 1, 0, M);
72
73        assert!(
74            convex_parts_overlap(&a, &b),
75            "a 1-unit interior intersection must count as overlap"
76        );
77    }
78
79    #[test]
80    fn overlapping_squares_overlap() {
81        // Squares overlapping by 1M in each direction
82        let a = square(0, 0, 3 * M);
83        let b = square(2 * M, 2 * M, 3 * M);
84        assert!(convex_parts_overlap(&a, &b));
85    }
86
87    #[test]
88    fn separated_squares_no_overlap() {
89        let a = square(0, 0, M);
90        let b = square(3 * M, 0, M);
91        assert!(!convex_parts_overlap(&a, &b));
92    }
93
94    #[test]
95    fn identical_squares_overlap() {
96        let square = square(0, 0, M);
97        assert!(convex_parts_overlap(&square, &square));
98    }
99
100    #[test]
101    fn contained_square_overlaps() {
102        let outer = square(0, 0, 10 * M);
103        let inner = square(2 * M, 2 * M, 3 * M);
104        assert!(convex_parts_overlap(&outer, &inner));
105    }
106
107    #[test]
108    fn parts_overlap_multipart() {
109        let a_parts = vec![vec![[0, 0], [10 * M, 0], [10 * M, 10 * M], [0, 10 * M]]];
110        let b_parts_overlap = vec![vec![
111            [5 * M, 5 * M],
112            [15 * M, 5 * M],
113            [15 * M, 15 * M],
114            [5 * M, 15 * M],
115        ]];
116        let b_parts_adjacent = vec![vec![
117            [10 * M, 0],
118            [20 * M, 0],
119            [20 * M, 10 * M],
120            [10 * M, 10 * M],
121        ]];
122        assert!(parts_overlap(&a_parts, &b_parts_overlap));
123        assert!(!parts_overlap(&a_parts, &b_parts_adjacent));
124    }
125
126    #[test]
127    fn find_overlapping_parts_returns_empty_when_disjoint() {
128        let a_parts = vec![
129            vec![[0, 0], [M, 0], [M, M], [0, M]],
130            vec![[3 * M, 0], [4 * M, 0], [4 * M, M], [3 * M, M]],
131            vec![[6 * M, 0], [7 * M, 0], [7 * M, M], [6 * M, M]],
132        ];
133        let b_parts = vec![
134            vec![[0, 3 * M], [M, 3 * M], [M, 4 * M], [0, 4 * M]],
135            vec![
136                [3 * M, 3 * M],
137                [4 * M, 3 * M],
138                [4 * M, 4 * M],
139                [3 * M, 4 * M],
140            ],
141            vec![
142                [6 * M, 3 * M],
143                [7 * M, 3 * M],
144                [7 * M, 4 * M],
145                [6 * M, 4 * M],
146            ],
147        ];
148
149        assert!(find_overlapping_parts(&a_parts, &b_parts).is_empty());
150    }
151
152    #[test]
153    fn find_overlapping_parts_returns_matching_indices() {
154        let a_parts = vec![
155            vec![[0, 0], [M, 0], [M, M], [0, M]],
156            vec![[3 * M, 0], [4 * M, 0], [4 * M, M], [3 * M, M]],
157            vec![[6 * M, 0], [7 * M, 0], [7 * M, M], [6 * M, M]],
158        ];
159        let b_parts = vec![
160            vec![[0, 3 * M], [M, 3 * M], [M, 4 * M], [0, 4 * M]],
161            vec![
162                [3 * M, 3 * M],
163                [4 * M, 3 * M],
164                [4 * M, 4 * M],
165                [3 * M, 4 * M],
166            ],
167            vec![
168                [6 * M - 1, 0],
169                [7 * M - 1, 0],
170                [7 * M - 1, M],
171                [6 * M - 1, M],
172            ],
173        ];
174
175        assert_eq!(find_overlapping_parts(&a_parts, &b_parts), vec![(2, 2)]);
176    }
177
178    #[test]
179    fn parts_overlap_false_when_all_pairs_separated() {
180        let a_parts = vec![
181            vec![[0, 0], [M, 0], [M, M], [0, M]],
182            vec![[3 * M, 0], [4 * M, 0], [4 * M, M], [3 * M, M]],
183        ];
184        let b_parts = vec![
185            vec![[0, 3 * M], [M, 3 * M], [M, 4 * M], [0, 4 * M]],
186            vec![
187                [3 * M, 3 * M],
188                [4 * M, 3 * M],
189                [4 * M, 4 * M],
190                [3 * M, 4 * M],
191            ],
192        ];
193
194        assert!(!parts_overlap(&a_parts, &b_parts));
195    }
196
197    #[test]
198    fn parts_overlap_true_when_one_pair_overlaps() {
199        let a_parts = vec![
200            vec![[0, 0], [M, 0], [M, M], [0, M]],
201            vec![[3 * M, 0], [4 * M, 0], [4 * M, M], [3 * M, M]],
202        ];
203        let b_parts = vec![
204            vec![[0, 3 * M], [M, 3 * M], [M, 4 * M], [0, 4 * M]],
205            vec![[M - 1, 0], [2 * M - 1, 0], [2 * M - 1, M], [M - 1, M]],
206        ];
207
208        assert!(parts_overlap(&a_parts, &b_parts));
209    }
210}