1use crate::Point;
2
3pub fn perimeter(poly: &[Point]) -> f32 {
4 let mut p = 0.0;
5 for i in 0..poly.len() {
6 let j = (i + 1) % poly.len();
7 let dx = poly[i].x - poly[j].x;
8 let dy = poly[i].y - poly[j].y;
9 p += (dx * dx + dy * dy).sqrt();
10 }
11 p
12}
13
14pub fn polygon_area(poly: &[Point]) -> f32 {
15 let mut area = 0.0;
16 let len = poly.len();
17 for i in 0..len {
18 let j = (i + 1) % len;
19 area += poly[i].x * poly[j].y;
20 area -= poly[j].x * poly[i].y;
21 }
22 area.abs() / 2.0
23}
24
25pub fn min_edge_length(poly: &[Point]) -> f32 {
26 let mut min = f32::MAX;
27 for i in 0..poly.len() {
28 let j = (i + 1) % poly.len();
29 let dx = poly[i].x - poly[j].x;
30 let dy = poly[i].y - poly[j].y;
31 let d = dx * dx + dy * dy;
32 if d < min {
33 min = d;
34 }
35 }
36 min.sqrt()
37}
38
39pub fn is_contour_convex(poly: &[Point]) -> bool {
40 let len = poly.len();
41 if len < 3 { return false; }
42
43 let mut prev_pt = poly[len - 1];
44 let mut cur_pt = poly[0];
45
46 let mut dx0 = cur_pt.x - prev_pt.x;
47 let mut dy0 = cur_pt.y - prev_pt.y;
48
49 let mut orientation = 0;
50
51 for i in 0..len {
52 let next_idx = (i + 1) % len;
53 prev_pt = cur_pt;
54 cur_pt = poly[next_idx];
55
56 let dx = cur_pt.x - prev_pt.x;
57 let dy = cur_pt.y - prev_pt.y;
58
59 let dxdy0 = dx * dy0;
60 let dydx0 = dy * dx0;
61
62 if dydx0 > dxdy0 {
63 orientation |= 1;
64 } else if dydx0 < dxdy0 {
65 orientation |= 2;
66 }
67
68 if orientation == 3 {
69 return false;
70 }
71
72 dx0 = dx;
73 dy0 = dy;
74 }
75
76 true
77}
78
79pub fn approx_poly_dp(contour: &[Point], epsilon: f32) -> Vec<Point> {
83 let len = contour.len();
84 if len <= 2 {
85 return contour.to_vec();
86 }
87
88 let epsilon_sq = epsilon * epsilon;
89
90 let mut right_start = 0usize;
92 let mut k = 0usize;
93 let mut max_dist_phase1: f32 = 0.0;
94
95 for _ in 0..3 {
96 let mut local_max: f32 = 0.0;
97 k = (k + right_start) % len;
98 let start_pt = contour[k];
99 k = (k + 1) % len;
100
101 for _j in 1..len {
102 let pt = contour[k];
103 k = (k + 1) % len;
104
105 let dx = pt.x - start_pt.x;
106 let dy = pt.y - start_pt.y;
107 let dist = dx * dx + dy * dy;
108
109 if dist > local_max {
110 local_max = dist;
111 right_start = _j;
112 }
113 }
114 max_dist_phase1 = local_max;
115 }
116
117 if max_dist_phase1 <= epsilon_sq {
118 let start_pt = contour[k % len];
119 return vec![Point { x: start_pt.x, y: start_pt.y }];
120 }
121
122 let slice_start = k;
124 let slice_end = right_start + slice_start;
125
126 let right_slice_start = if right_start + slice_start >= len {
127 right_start + slice_start - len
128 } else {
129 right_start + slice_start
130 };
131 let right_slice_end = if slice_start < right_slice_start {
132 slice_start + len
133 } else {
134 slice_start
135 };
136
137 let mut poly = Vec::new();
138 let mut stack: Vec<(usize, usize)> = Vec::new();
139
140 stack.push((right_slice_start, right_slice_end));
141 stack.push((slice_start, slice_end));
142
143 while let Some((s_start, s_end)) = stack.pop() {
144 let end_pt = contour[s_end % len];
145 let mut kk = s_start % len;
146 let start_pt = contour[kk];
147 kk = (kk + 1) % len;
148
149 if s_end <= s_start + 1 {
150 poly.push(Point { x: start_pt.x, y: start_pt.y });
151 } else {
152 let mut max_dist: f32 = 0.0;
153 let mut max_idx = 0;
154
155 let dx = end_pt.x - start_pt.x;
156 let dy = end_pt.y - start_pt.y;
157
158 for i in (s_start + 1)..s_end {
159 let pt = contour[kk];
160 kk = (kk + 1) % len;
161
162 let dist = ((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy).abs();
163
164 if dist > max_dist {
165 max_dist = dist;
166 max_idx = i;
167 }
168 }
169
170 let le_eps = max_dist * max_dist <= epsilon_sq * (dx * dx + dy * dy);
171
172 if le_eps {
173 poly.push(Point { x: start_pt.x, y: start_pt.y });
174 } else {
175 stack.push((max_idx, s_end));
177 stack.push((s_start, max_idx));
178 }
179 }
180 }
181
182 poly
183}
184
185pub fn clockwise_corners(candidates: &mut Vec<Vec<Point>>) {
188 for c in candidates.iter_mut() {
189 if c.len() != 4 { continue; }
190
191 let dx1 = c[1].x - c[0].x;
192 let dy1 = c[1].y - c[0].y;
193 let dx2 = c[2].x - c[0].x;
194 let dy2 = c[2].y - c[0].y;
195
196 if (dx1 * dy2 - dy1 * dx2) < 0.0 {
197 c.swap(1, 3);
198 }
199 }
200}
201
202pub fn not_too_near(candidates: &[Vec<Point>], min_dist: f32) -> Vec<Vec<Point>> {
205 let len = candidates.len();
206 let mut too_near = vec![false; len];
207
208 for i in 0..len {
209 if too_near[i] { continue; }
210 for j in (i + 1)..len {
211 if too_near[j] { continue; }
212
213 let mut dist: f32 = 0.0;
214 for k in 0..4 {
215 let dx = candidates[i][k].x - candidates[j][k].x;
216 let dy = candidates[i][k].y - candidates[j][k].y;
217 dist += dx * dx + dy * dy;
218 }
219
220 if (dist / 4.0) < (min_dist * min_dist) {
221 if perimeter(&candidates[i]) < perimeter(&candidates[j]) {
222 too_near[i] = true;
223 } else {
224 too_near[j] = true;
225 }
226 }
227 }
228 }
229
230 candidates.iter().enumerate()
231 .filter(|(i, _)| !too_near[*i])
232 .map(|(_, c)| c.clone())
233 .collect()
234}
235
236pub fn count_non_zero(data: &[u8], img_width: usize, x: usize, y: usize, w: usize, h: usize) -> usize {
239 let mut nz = 0;
240 for row in y..(y + h) {
241 for col in x..(x + w) {
242 if data[row * img_width + col] != 0 {
243 nz += 1;
244 }
245 }
246 }
247 nz
248}
249
250pub fn refine_corner(data: &[u8], width: usize, height: usize, p: Point, window: i32) -> Point {
253 let x = p.x.floor() as i32;
254 let y = p.y.floor() as i32;
255
256 let mut min_i = 255u8;
258 let mut max_i = 0u8;
259
260 for dy in -window..=window {
261 for dx in -window..=window {
262 let px = x + dx;
263 let py = y + dy;
264 if px >= 0 && px < width as i32 && py >= 0 && py < height as i32 {
265 let v = data[py as usize * width + px as usize];
266 if v < min_i { min_i = v; }
267 if v > max_i { max_i = v; }
268 }
269 }
270 }
271
272 if (max_i as i32 - min_i as i32) < 30 {
274 return p;
275 }
276
277 let mut s_ix2 = 0.0;
279 let mut s_iy2 = 0.0;
280 let mut s_ixy = 0.0;
281 let mut s_ix2x = 0.0;
282 let mut s_iy2y = 0.0;
283
284 for dy in -window..=window {
285 for dx in -window..=window {
286 let px = x + dx;
287 let py = y + dy;
288
289 if px > 0 && px < (width as i32 - 1) && py > 0 && py < (height as i32 - 1) {
291 let px_idx = py as usize * width + px as usize;
292
293 let ix = (data[px_idx + 1] as f32 - data[px_idx - 1] as f32) / 2.0;
295 let iy = (data[px_idx + width] as f32 - data[px_idx - width] as f32) / 2.0;
296
297 s_ix2 += ix * ix;
298 s_iy2 += iy * iy;
299 s_ixy += ix * iy;
300 s_ix2x += ix * ix * px as f32 + ix * iy * py as f32;
301 s_iy2y += iy * iy * py as f32 + ix * iy * px as f32;
302 }
303 }
304 }
305
306 let det = s_ix2 * s_iy2 - s_ixy * s_ixy;
307 if det.abs() < 0.0001 {
308 return p;
309 }
310
311 let nx = (s_ix2x * s_iy2 - s_ixy * s_iy2y) / det;
312 let ny = (s_ix2 * s_iy2y - s_ix2x * s_ixy) / det;
313
314 let dist_sq = (nx - p.x) * (nx - p.x) + (ny - p.y) * (ny - p.y);
316 if dist_sq > 9.0 {
317 return p;
318 }
319
320 Point { x: nx, y: ny }
321}
322
323pub fn get_extreme_corner(corners: &[Point], mode_x_max: bool, mode_y_max: bool) -> Point {
328 let mut best_score = f32::NEG_INFINITY;
329 let mut best_idx = 0;
330
331 for (i, p) in corners.iter().enumerate() {
332 let x_val = if mode_x_max { p.x } else { -p.x };
333 let y_val = if mode_y_max { p.y } else { -p.y };
334 let score = x_val + y_val;
335
336 if score > best_score {
337 best_score = score;
338 best_idx = i;
339 }
340 }
341 corners[best_idx]
342}