Skip to main content

doc_quad/geom/
validate.rs

1// src/geom/validate.rs
2use glam::Vec2;
3
4pub struct GeometryValidator;
5
6impl GeometryValidator {
7    pub fn validate_and_score(simplified: &(Vec<Vec2>, bool)) -> Option<(f32, [Vec2; 4])> {
8        let pts = &simplified.0;
9        let is_closed = simplified.1;
10
11        if !is_closed || pts.len() != 5 {
12            log::debug!(
13                "[Geom::Validate] - Failed base check: is_closed={}, len={} (expected closed, len=5).",
14                is_closed, pts.len()
15            );
16            return None;
17        }
18
19        let p = [pts[0], pts[1], pts[2], pts[3]];
20
21        let cross = [
22            Self::cross_product(p[0], p[1], p[2]),
23            Self::cross_product(p[1], p[2], p[3]),
24            Self::cross_product(p[2], p[3], p[0]),
25            Self::cross_product(p[3], p[0], p[1]),
26        ];
27
28        if cross.iter().any(|&c| c.abs() < 1.0) {
29            log::warn!(
30                "[Geom::Validate] - Quadrilateral failed collinear test (degenerate polygon). Cross products: {:?}",
31                cross
32            );
33            return None;
34        }
35
36        let non_zero: Vec<f32> = cross.iter().copied().filter(|&c| c != 0.0).collect();
37        if non_zero.is_empty() {
38            log::warn!("[Geom::Validate] - Quadrilateral completely degenerate (all cross products zero).");
39            return None;
40        }
41
42        let positive_count = non_zero.iter().filter(|&&c| c > 0.0).count();
43        let negative_count = non_zero.len() - positive_count;
44        let minority_count = positive_count.min(negative_count);
45
46        if minority_count > 0 {
47            log::warn!(
48                "[Geom::Validate] - Quadrilateral failed convexity test (concave or self-intersecting detected). \
49                 Positive crosses: {}, Negative crosses: {}",
50                positive_count, negative_count
51            );
52            return None;
53        }
54
55        let area = Self::polygon_area(&p);
56        Some((area, p))
57    }
58
59    #[inline]
60    fn cross_product(p0: Vec2, p1: Vec2, p2: Vec2) -> f32 {
61        (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x)
62    }
63
64    #[inline]
65    fn polygon_area(pts: &[Vec2; 4]) -> f32 {
66        let mut area = 0.0;
67        for i in 0..4 {
68            let j = (i + 1) % 4;
69            area += pts[i].x * pts[j].y - pts[j].x * pts[i].y;
70        }
71        (area / 2.0).abs()
72    }
73}