1use alloc::vec::Vec;
2use i_float::int::point::IntPoint;
3use i_shape::int::path::ContourExtension;
4use i_shape::int::shape::IntContour;
5use i_shape::util::reserve::Reserve;
6
7pub(super) trait Split {
8 fn split_loops(
9 self,
10 min_area: u64,
11 contour_buffer: &mut IntContour,
12 bin_store: &mut BinStore,
13 ) -> Vec<Self>
14 where
15 Self: Sized;
16}
17
18impl Split for IntContour {
19 fn split_loops(
20 self,
21 min_area: u64,
22 contour_buffer: &mut IntContour,
23 bin_store: &mut BinStore,
24 ) -> Vec<Self> {
25 if self.is_empty() {
26 return Vec::new();
27 }
28 contour_buffer.reserve_capacity(self.len());
29 contour_buffer.clear();
30
31 bin_store.init(&self);
32
33 let mut result: Vec<IntContour> = Vec::with_capacity(16);
34
35 for point in self {
36 let next_pos = contour_buffer.len() + 1;
37 let pos = bin_store.insert_if_not_exist(point, next_pos);
38 if pos < contour_buffer.len() {
39 let tail_len = contour_buffer.len() - pos;
41 if tail_len < 2 {
42 contour_buffer.truncate(pos);
44 } else {
45 let mut tail = contour_buffer.split_off(pos);
46 tail.push(point);
47 if tail.validate_area(min_area) {
48 result.push(tail);
49 }
50 }
51 } else {
52 contour_buffer.push(point);
53 }
54 }
55
56 if contour_buffer.len() > 2 {
57 result.push(contour_buffer.as_slice().to_vec());
58 }
59
60 result
61 }
62}
63
64#[derive(Debug, Clone, Copy)]
65struct PointItem {
66 point: IntPoint,
67 pos: usize,
68}
69
70#[derive(Debug, Clone, Copy)]
71struct Bin {
72 offset: usize,
73 data: usize,
74}
75
76pub(super) struct BinStore {
77 mask: u32,
78 bins: Vec<Bin>,
79 items: Vec<PointItem>,
80}
81
82impl BinStore {
83 pub(super) fn new() -> Self {
84 Self {
85 mask: 0,
86 bins: Vec::new(),
87 items: Vec::new(),
88 }
89 }
90
91 fn init(&mut self, contour: &IntContour) {
92 let log = contour.len().ilog2().saturating_sub(4).clamp(1, 30);
93 let bins_count = (1 << log) as usize;
94
95 self.bins.clear();
96 self.bins.resize(bins_count, Bin { offset: 0, data: 0 });
97
98 self.items.clear();
99 self.items.resize(
100 contour.len(),
101 PointItem {
102 point: IntPoint::EMPTY,
103 pos: 0,
104 },
105 );
106
107 self.mask = bins_count.wrapping_sub(1) as u32;
108
109 for &p in contour.iter() {
110 let index = self.bin_index(p);
111 unsafe {
112 self.bins.get_unchecked_mut(index).data += 1
114 };
115 }
116
117 let mut offset = 0;
118 for bin in self.bins.iter_mut() {
119 let next_offset = offset + bin.data;
120 *bin = Bin {
121 offset,
122 data: offset,
123 };
124 offset = next_offset;
125 }
126 }
127
128 #[inline]
129 fn insert_if_not_exist(&mut self, point: IntPoint, pos: usize) -> usize {
130 let index = self.bin_index(point);
131 let bin = unsafe {
132 self.bins.get_unchecked_mut(index)
134 };
135 let start = bin.offset;
136 let end = bin.data;
137 for i in start..end {
138 let item = unsafe {
139 self.items.get_unchecked_mut(i)
141 };
142 if item.point == point {
143 return item.pos;
144 }
145 }
146 bin.data = end + 1;
147 unsafe {
148 *self.items.get_unchecked_mut(end) = PointItem { point, pos }
150 }
151
152 usize::MAX
153 }
154
155 #[inline]
156 fn bin_index(&self, p: IntPoint) -> usize {
157 let x = p.x.unsigned_abs();
158 let y = p.y.unsigned_abs();
159 let hash = x.wrapping_mul(31) ^ y.wrapping_mul(17);
160 (hash & self.mask) as usize
161 }
162}
163
164trait ValidateArea {
165 fn validate_area(&self, min_area: u64) -> bool;
166}
167
168impl ValidateArea for IntContour {
169 #[inline]
170 fn validate_area(&self, min_area: u64) -> bool {
171 if min_area == 0 {
172 return true;
173 }
174 let abs_area = self.unsafe_area().unsigned_abs() >> 1;
175 abs_area < min_area
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::i_shape::int::path::IntPath;
183 use alloc::vec;
184
185 #[test]
186 fn test_empty_path() {
187 let path: IntPath = vec![];
188 let mut contour: IntContour = Vec::new();
189 let mut bin_store = BinStore::new();
190 let result = path.split_loops(0, &mut contour, &mut bin_store);
191 assert_eq!(result, vec![] as Vec<IntPath>);
192 }
193
194 #[test]
195 fn test_single_point() {
196 let path = vec![IntPoint::new(0, 0)];
197 let mut contour: IntContour = Vec::new();
198 let mut bin_store = BinStore::new();
199 let result = path.split_loops(0, &mut contour, &mut bin_store);
200 assert!(result.is_empty());
201 }
202
203 #[test]
204 fn test_two_points() {
205 let path = vec![IntPoint::new(0, 0), IntPoint::new(1, 1)];
206 let mut contour: IntContour = Vec::new();
207 let mut bin_store = BinStore::new();
208 let result = path.split_loops(0, &mut contour, &mut bin_store);
209 assert!(result.is_empty());
210 }
211
212 #[test]
213 fn test_no_repeated_points() {
214 let path = vec![
215 IntPoint::new(0, 0),
216 IntPoint::new(0, 1),
217 IntPoint::new(1, 1),
218 IntPoint::new(1, 0),
219 ];
220
221 let mut contour: IntContour = Vec::new();
222 let mut bin_store = BinStore::new();
223 let result = path.clone().split_loops(0, &mut contour, &mut bin_store);
224 assert_eq!(result, vec![path]);
225 }
226
227 #[test]
228 fn test_2_loops_0() {
229 let path = vec![
230 IntPoint::new(0, 0),
231 IntPoint::new(1, 1),
232 IntPoint::new(2, 0),
233 IntPoint::new(3, 1),
234 IntPoint::new(4, 0),
235 IntPoint::new(3, -1),
236 IntPoint::new(2, 0), IntPoint::new(1, -1),
238 ];
239
240 let mut contour: IntContour = Vec::new();
241 let mut bin_store = BinStore::new();
242 let result = path.split_loops(0, &mut contour, &mut bin_store);
243 assert_eq!(result.len(), 2);
244 assert_eq!(
245 result[0],
246 [
247 IntPoint::new(3, 1),
248 IntPoint::new(4, 0),
249 IntPoint::new(3, -1),
250 IntPoint::new(2, 0),
251 ]
252 .to_vec()
253 );
254 assert_eq!(
255 result[1],
256 [
257 IntPoint::new(0, 0),
258 IntPoint::new(1, 1),
259 IntPoint::new(2, 0),
260 IntPoint::new(1, -1),
261 ]
262 .to_vec()
263 );
264 }
265
266 #[test]
267 fn test_2_loops_1() {
268 let path = vec![
269 IntPoint::new(0, 0),
270 IntPoint::new(1, 1),
271 IntPoint::new(2, 0),
272 IntPoint::new(3, 1),
273 IntPoint::new(3, -1),
274 IntPoint::new(2, 0), IntPoint::new(1, -1),
276 ];
277
278 let mut contour: IntContour = Vec::new();
279 let mut bin_store = BinStore::new();
280 let result = path.split_loops(0, &mut contour, &mut bin_store);
281 assert_eq!(result.len(), 2);
282 assert_eq!(
283 result[0],
284 [
285 IntPoint::new(3, 1),
286 IntPoint::new(3, -1),
287 IntPoint::new(2, 0),
288 ]
289 .to_vec()
290 );
291 assert_eq!(
292 result[1],
293 [
294 IntPoint::new(0, 0),
295 IntPoint::new(1, 1),
296 IntPoint::new(2, 0),
297 IntPoint::new(1, -1),
298 ]
299 .to_vec()
300 );
301 }
302
303 #[test]
304 fn test_2_loops_with_tails() {
305 let path = vec![
306 IntPoint::new(-1, 0),
307 IntPoint::new(0, 0),
308 IntPoint::new(1, 1),
309 IntPoint::new(2, 0),
310 IntPoint::new(3, 1),
311 IntPoint::new(4, 0),
312 IntPoint::new(5, 0),
313 IntPoint::new(4, 0),
314 IntPoint::new(3, -1),
315 IntPoint::new(2, 0), IntPoint::new(1, -1),
317 IntPoint::new(0, 0),
318 ];
319
320 let mut contour: IntContour = Vec::new();
321 let mut bin_store = BinStore::new();
322 let result = path.split_loops(0, &mut contour, &mut bin_store);
323 assert_eq!(result.len(), 2);
324 assert_eq!(
325 result[0],
326 [
327 IntPoint::new(3, 1),
328 IntPoint::new(4, 0),
329 IntPoint::new(3, -1),
330 IntPoint::new(2, 0),
331 ]
332 .to_vec()
333 );
334 assert_eq!(
335 result[1],
336 [
337 IntPoint::new(1, 1),
338 IntPoint::new(2, 0),
339 IntPoint::new(1, -1),
340 IntPoint::new(0, 0),
341 ]
342 .to_vec()
343 );
344 }
345
346 #[test]
347 fn test_single_loop() {
348 let path = vec![
349 IntPoint::new(0, 0),
350 IntPoint::new(1, 1),
351 IntPoint::new(2, 0),
352 IntPoint::new(0, 0), ];
354
355 let mut contour: IntContour = Vec::new();
356 let mut bin_store = BinStore::new();
357 let result = path.split_loops(0, &mut contour, &mut bin_store);
358 assert_eq!(result.len(), 1);
359 assert_eq!(
360 result[0],
361 [
362 IntPoint::new(1, 1),
363 IntPoint::new(2, 0),
364 IntPoint::new(0, 0),
365 ]
366 .to_vec()
367 );
368 }
369
370 #[test]
371 fn test_cross() {
372 let path = vec![
373 IntPoint::new(-2, -1),
374 IntPoint::new(-2, 1),
375 IntPoint::new(0, 0),
376 IntPoint::new(-1, 2),
377 IntPoint::new(1, 2),
378 IntPoint::new(0, 0), IntPoint::new(2, 1),
380 IntPoint::new(2, -1),
381 IntPoint::new(0, 0), IntPoint::new(1, -2),
383 IntPoint::new(-1, -2),
384 IntPoint::new(0, 0), ];
386
387 let mut contour: IntContour = Vec::new();
388 let mut bin_store = BinStore::new();
389 let result = path.split_loops(0, &mut contour, &mut bin_store);
390 assert_eq!(result.len(), 4);
391 assert_eq!(
392 result[0],
393 [
394 IntPoint::new(-1, 2),
395 IntPoint::new(1, 2),
396 IntPoint::new(0, 0),
397 ]
398 .to_vec()
399 );
400 assert_eq!(
401 result[1],
402 [
403 IntPoint::new(2, 1),
404 IntPoint::new(2, -1),
405 IntPoint::new(0, 0),
406 ]
407 .to_vec()
408 );
409 assert_eq!(
410 result[2],
411 [
412 IntPoint::new(1, -2),
413 IntPoint::new(-1, -2),
414 IntPoint::new(0, 0),
415 ]
416 .to_vec()
417 );
418 assert_eq!(
419 result[3],
420 [
421 IntPoint::new(-2, -1),
422 IntPoint::new(-2, 1),
423 IntPoint::new(0, 0),
424 ]
425 .to_vec()
426 );
427 }
428}