Skip to main content

doc_quad/topology/
contour.rs

1// src/topology/contour.rs
2use glam::Vec2;
3use crate::topology::chain::{find_next_pixel, reverse_dir};
4
5pub struct ContourExtractor;
6
7impl ContourExtractor {
8    /// 提取二值图像中的外轮廓。
9    pub fn extract(edges: &[u8], width: u32, height: u32) -> Vec<Vec<Vec2>> {
10        let mut visited = vec![false; (width * height) as usize];
11        let mut contours = Vec::new();
12
13        for y in 0..height {
14            for x in 0..width {
15                let idx = (y * width + x) as usize;
16                
17                if edges[idx] == 0 || visited[idx] {
18                    continue;
19                }
20
21                let contour = Self::trace_contour(edges, &mut visited, width, height, x, y);
22                
23                if contour.len() >= 4 {
24                    contours.push(contour);
25                }
26            }
27        }
28
29        contours
30    }
31
32    fn trace_contour(
33        edges: &[u8],
34        visited: &mut [bool],
35        width: u32,
36        height: u32,
37        start_x: u32,
38        start_y: u32,
39    ) -> Vec<Vec2> {
40        let mut contour = Vec::with_capacity(16);
41        let mut cx = start_x;
42        let mut cy = start_y;
43        
44        let mut search_dir = 7; 
45
46        contour.push(Vec2::new(cx as f32, cy as f32));
47        visited[(cy * width + cx) as usize] = true;
48
49        let mut prev_dir: Option<u8> = None;
50        let max_pts = width * height; 
51
52        for _ in 0..max_pts {
53            if let Some((nx, ny, found_dir)) = find_next_pixel(edges, width, height, cx, cy, search_dir) {
54                visited[(ny * width + nx) as usize] = true;
55
56                if Some(found_dir) == prev_dir && contour.len() > 1 {
57                    let last_idx = contour.len() - 1;
58                    contour[last_idx] = Vec2::new(nx as f32, ny as f32);
59                } else {
60                    contour.push(Vec2::new(nx as f32, ny as f32));
61                }
62
63                prev_dir = Some(found_dir);
64
65                if nx == start_x && ny == start_y {
66                    break; 
67                }
68
69                cx = nx;
70                cy = ny;
71                
72                search_dir = (reverse_dir(found_dir) + 1) % 8;
73            } else {
74                break;
75            }
76        }
77
78        contour
79    }
80}