Skip to main content

doc_quad/geom/
validate.rs

1// src/geom/validate.rs
2use glam::Vec2;
3use geo_types::LineString;
4
5pub struct GeometryValidator;
6
7impl GeometryValidator {
8    /// 校验四边形是否近似为凸多边形并计算面积。
9    ///
10    /// # 凸性容差
11    /// 允许最多 1 个叉积符号与主方向相反(容忍轻微透视变形),
12    /// 但不允许超过 1 个,防止严重凹入四边形通过。
13    pub fn validate_and_score(poly: &LineString<f32>) -> Option<(f32, [Vec2; 4])> {
14        if poly.0.len() != 5 {
15            return None;
16        }
17
18        let pts = [
19            Vec2::new(poly.0[0].x, poly.0[0].y),
20            Vec2::new(poly.0[1].x, poly.0[1].y),
21            Vec2::new(poly.0[2].x, poly.0[2].y),
22            Vec2::new(poly.0[3].x, poly.0[3].y),
23        ];
24
25        // 鞋带公式
26        let mut area = 0.0f32;
27        for i in 0..4 {
28            let j = (i + 1) % 4;
29            area += pts[i].x * pts[j].y;
30            area -= pts[j].x * pts[i].y;
31        }
32        area = area.abs() * 0.5;
33
34        if area < f32::EPSILON {
35            return None;
36        }
37
38        // 计算各边叉积
39        let mut crosses = [0.0f32; 4];
40        for i in 0..4 {
41            let v1 = pts[(i + 1) % 4] - pts[i];
42            let v2 = pts[(i + 2) % 4] - pts[(i + 1) % 4];
43            crosses[i] = v1.x * v2.y - v1.y * v2.x;
44        }
45
46        // 过滤接近零的叉积(共线边)
47        let non_zero: Vec<f32> = crosses
48            .iter()
49            .filter(|&&c| c.abs() > area * 0.01) // 相对阈值:叉积绝对值 > 面积的 1%
50            .copied()
51            .collect();
52
53        if non_zero.is_empty() {
54            log::warn!("[Geom::Validate] - Quadrilateral is fully degenerate.");
55            return None;
56        }
57
58        // 确定主方向(多数票)
59        let positive_count = non_zero.iter().filter(|&&c| c > 0.0).count();
60        let negative_count = non_zero.len() - positive_count;
61        let minority_count = positive_count.min(negative_count);
62
63        // 允许最多 1 个叉积与主方向相反(容忍轻微变形)
64        if minority_count > 1 {
65            log::warn!(
66                "[Geom::Validate] - Quadrilateral failed convexity test ({} minority crosses).",
67                minority_count
68            );
69            return None;
70        }
71
72        Some((area, pts))
73    }
74}