i_shape/int/
despike.rs

1use alloc::vec;
2use alloc::vec::Vec;
3use i_float::int::point::IntPoint;
4use crate::int::shape::{IntContour, IntShape, IntShapes};
5
6/// A trait for removing spike artifacts from polygon contours.
7pub trait DeSpike {
8    /// Removes spikes from the contour in-place.
9    ///
10    /// # Returns
11    ///
12    /// - `true` if spikes were found and removed.
13    /// - `false` if the contour was already clean.
14    fn remove_spikes(&mut self) -> bool;
15}
16
17pub trait DeSpikeContour {
18    /// Checks whether the contour has no spikes.
19    ///
20    /// A contour with no spikes is considered clean and valid
21    /// for most geometric operations.
22    ///
23    /// # Returns
24    ///
25    /// - `true` if the contour has no spike patterns.
26    /// - `false` if any spike-like edge reversal is detected.
27    fn has_no_spikes(&self) -> bool;
28
29    /// Returns a copy of the contour with spikes removed.
30    ///
31    /// # Returns
32    ///
33    /// - `Some(IntContour)` if a valid, despiked contour can be produced.
34    /// - `None` if the contour is degenerate after spike removal.
35    fn despiked_contour(&self) -> Option<IntContour>;
36}
37
38pub trait DeSpikeShape {
39    /// Checks whether the shape has no spikes.
40    ///
41    /// A contour with no spikes is considered clean and valid
42    /// for most geometric operations.
43    ///
44    /// # Returns
45    ///
46    /// - `true` if the contour has no spike patterns.
47    /// - `false` if any spike-like edge reversal is detected.
48    fn has_no_spikes(&self) -> bool;
49
50    /// Returns an optional simplified version of the shape.
51    ///
52    /// # Returns
53    ///
54    /// - `Some(IntShape)` containing the simplified shape if simplification is possible.
55    /// - `None` if the shape is degenerate or empty.
56    fn despiked_shape(&self) -> Option<IntShape>;
57}
58
59pub trait DeSpikeShapes {
60    /// Checks whether the shapes have no spikes.
61    ///
62    /// A contour with no spikes is considered clean and valid
63    /// for most geometric operations.
64    ///
65    /// # Returns
66    ///
67    /// - `true` if the contour has no spike patterns.
68    /// - `false` if any spike-like edge reversal is detected.
69    fn has_no_spikes(&self) -> bool;
70
71    /// Returns an optional simplified version of the collection.
72    ///
73    /// # Returns
74    ///
75    /// - `IntShapes` the simplified shapes.
76    fn despiked_shapes(&self) -> IntShapes;
77}
78
79impl DeSpike for IntContour {
80    fn remove_spikes(&mut self) -> bool {
81        if self.has_no_spikes() {
82            return false;
83        }
84        if let Some(contour) = self.despiked_contour() {
85            *self = contour;
86        } else {
87            self.clear()
88        }
89        true
90    }
91}
92
93impl DeSpikeContour for IntContour {
94    fn has_no_spikes(&self) -> bool {
95        let count = self.len();
96
97        if count < 3 { return false; }
98
99        let mut p0 = self[count - 2];
100        let p1 = self[count - 1];
101
102        let mut v0 = p1.subtract(p0);
103        p0 = p1;
104
105        for &pi in self.iter() {
106            let vi = pi.subtract(p0);
107            let cross = vi.cross_product(v0);
108            let dot = vi.dot_product(v0);
109            if cross == 0 && dot < 0 {
110                return false;
111            }
112            v0 = vi;
113            p0 = pi;
114        }
115
116        true
117    }
118
119    fn despiked_contour(&self) -> Option<IntContour> {
120        if self.len() < 3 {
121            return None;
122        }
123
124        let mut n = self.len();
125        let mut nodes: Vec<Node> = vec![Node { next: 0, index: 0, prev: 0 }; n];
126        let mut validated: Vec<bool> = vec![false; n];
127
128        let mut i0 = n - 2;
129        let mut i1 = n - 1;
130        for i2 in 0..n {
131            nodes[i1] = Node { next: i2, index: i1, prev: i0 };
132            i0 = i1;
133            i1 = i2;
134        }
135
136        let mut first: usize = 0;
137        let mut node = nodes[first];
138        let mut i = 0;
139        while i < n {
140            if validated[node.index] {
141                node = nodes[node.next];
142                continue;
143            }
144
145            let p0 = self[node.prev];
146            let p1 = self[node.index];
147            let p2 = self[node.next];
148
149            let v10 = p1.subtract(p0);
150            let v21 = p2.subtract(p1);
151            let cross = v10.cross_product(v21);
152            let dot = v10.dot_product(v21);
153
154            if cross == 0 && dot < 0 {
155                n -= 1;
156                if n < 3 {
157                    return None;
158                }
159
160                // remove node
161                nodes[node.prev].next = node.next;
162                nodes[node.next].prev = node.prev;
163
164                if node.index == first {
165                    first = node.next
166                }
167
168                node = nodes[node.prev];
169
170                if validated[node.prev] {
171                    i -= 1;
172                    validated[node.prev] = false
173                }
174
175                if validated[node.next] {
176                    i -= 1;
177                    validated[node.next] = false
178                }
179
180                if validated[node.index] {
181                    i -= 1;
182                    validated[node.index] = false
183                }
184            } else {
185                validated[node.index] = true;
186                i += 1;
187                node = nodes[node.next];
188            }
189        }
190
191        let mut buffer = vec![IntPoint::ZERO; n];
192        node = nodes[first];
193
194        for item in buffer.iter_mut().take(n) {
195            *item = self[node.index];
196            node = nodes[node.next];
197        }
198
199        Some(buffer)
200    }
201}
202
203impl DeSpike for IntShape {
204    fn remove_spikes(&mut self) -> bool {
205        let mut any_simplified = false;
206        let mut any_empty = false;
207
208        for (index, contour) in self.iter_mut().enumerate() {
209            if contour.has_no_spikes() { continue; }
210            any_simplified = true;
211
212            if let Some(simple_contour) = contour.despiked_contour() {
213                *contour = simple_contour;
214            } else if index == 0 {
215                // early out main contour is empty
216                self.clear();
217                return true;
218            } else {
219                contour.clear();
220                any_empty = true;
221            }
222        }
223
224        if any_empty {
225            self.retain(|contour| !contour.is_empty());
226        }
227
228        any_simplified
229    }
230}
231
232impl DeSpikeShape for IntShape {
233    fn has_no_spikes(&self) -> bool {
234        for contour in self.iter() {
235            if !contour.has_no_spikes() {
236                return false;
237            }
238        }
239        true
240    }
241
242    fn despiked_shape(&self) -> Option<IntShape> {
243        let mut contours = Vec::with_capacity(self.len());
244        for (i, contour) in self.iter().enumerate() {
245            if contour.has_no_spikes() {
246                contours.push(contour.clone());
247            } else if let Some(simple) = contour.despiked_contour() {
248                contours.push(simple);
249            } else if i == 0 {
250                return None;
251            }
252        }
253
254        Some(contours)
255    }
256}
257
258impl DeSpike for IntShapes {
259    fn remove_spikes(&mut self) -> bool {
260        let mut any_simplified = false;
261        let mut any_empty = false;
262
263        for shape in self.iter_mut() {
264            if shape.has_no_spikes() { continue; }
265            any_simplified = true;
266            if let Some(simple_shape) = shape.despiked_shape() {
267                *shape = simple_shape;
268            } else {
269                shape.clear();
270                any_empty = true;
271            }
272        }
273
274        if any_empty {
275            self.retain(|contour| !contour.is_empty());
276        }
277
278        any_simplified
279    }
280}
281
282impl DeSpikeShapes for IntShapes {
283    fn has_no_spikes(&self) -> bool {
284        for shape in self.iter() {
285            if !shape.has_no_spikes() {
286                return false;
287            }
288        }
289        true
290    }
291
292    fn despiked_shapes(&self) -> IntShapes {
293        let mut shapes = Vec::with_capacity(self.len());
294        for shape in self.iter() {
295            if shape.has_no_spikes() {
296                shapes.push(shape.clone());
297            } else if let Some(simple) = shape.despiked_shape() {
298                shapes.push(simple);
299            }
300        }
301
302        shapes
303    }
304}
305
306#[derive(Clone, Copy)]
307struct Node {
308    next: usize,
309    index: usize,
310    prev: usize,
311}
312
313#[cfg(test)]
314mod tests {
315    use alloc::vec;
316    use i_float::int::point::IntPoint;
317    use crate::int::despike::DeSpike;
318
319    #[test]
320    fn test_0() {
321        let mut contour =
322            vec![
323                IntPoint::new(0, 0),
324                IntPoint::new(1, 0),
325                IntPoint::new(1, 1),
326                IntPoint::new(0, 1),
327            ];
328
329        let modified = contour.remove_spikes();
330
331        assert_eq!(contour.len(), 4);
332        assert_eq!(modified, false);
333    }
334
335    #[test]
336    fn test_1() {
337        let mut contour =
338            vec![
339                IntPoint::new(0, -1),
340                IntPoint::new(0, 1),
341                IntPoint::new(1, 1),
342                IntPoint::new(1, 0),
343                IntPoint::new(0, 0),
344            ];
345
346        let modified = contour.remove_spikes();
347
348        assert_eq!(contour.len(), 4);
349        assert_eq!(modified, true);
350    }
351
352    #[test]
353    fn test_2() {
354        let mut contour =
355            vec![
356                IntPoint::new(0, -1),
357                IntPoint::new(0, 1),
358                IntPoint::new(1, 1),
359                IntPoint::new(1, 0),
360                IntPoint::new(0, 0),
361            ];
362
363        let modified = contour.remove_spikes();
364
365        assert_eq!(contour.len(), 4);
366        assert_eq!(modified, true);
367    }
368
369    #[test]
370    fn test_3() {
371        let mut contour =
372            vec![
373                IntPoint::new(0, 0),
374                IntPoint::new(0, 2),
375                IntPoint::new(1, 2),
376                IntPoint::new(3, 2),
377                IntPoint::new(4, 2),
378                IntPoint::new(2, 2),
379                IntPoint::new(2, 0),
380            ];
381
382        let modified = contour.remove_spikes();
383
384        assert_eq!(contour.len(), 5);
385        assert_eq!(modified, true);
386    }
387
388    #[test]
389    fn test_4() {
390        let mut contour =
391            vec![
392                IntPoint::new(0, 0),
393                IntPoint::new(0, 2),
394                IntPoint::new(1, 2),
395                IntPoint::new(4, 2),
396                IntPoint::new(3, 2),
397                IntPoint::new(2, 2),
398                IntPoint::new(2, 0),
399            ];
400
401        let modified = contour.remove_spikes();
402
403        assert_eq!(contour.len(), 5);
404        assert_eq!(modified, true);
405    }
406
407    #[test]
408    fn test_5() {
409        let mut contour =
410            vec![
411                IntPoint::new(-10, 10),
412                IntPoint::new(-10, 0),
413                IntPoint::new(-10, -10),
414                IntPoint::new(0, -10),
415                IntPoint::new(10, -10),
416                IntPoint::new(10, 0),
417                IntPoint::new(10, 10),
418                IntPoint::new(0, 10),
419            ];
420
421        let modified = contour.remove_spikes();
422
423        assert_eq!(contour.len(), 8);
424        assert_eq!(modified, false);
425    }
426}