1use super::error::{GateError, Result};
2use super::traits::*;
3use super::types::GateNode;
4
5#[derive(Debug, Clone)]
6pub struct PolygonGateGeometry {
7 pub nodes: Vec<GateNode>,
8 pub closed: bool,
9}
10
11impl GateCenter for PolygonGateGeometry {
12 fn calculate_center(&self, x_param: &str, y_param: &str) -> Result<(f32, f32)> {
13 let (sum_x, sum_y, count) = self
14 .nodes
15 .iter()
16 .filter_map(|node| {
17 let x = node.get_coordinate(x_param)?;
18 let y = node.get_coordinate(y_param)?;
19 Some((x, y))
20 })
21 .fold((0.0, 0.0, 0), |(sx, sy, c), (x, y)| (sx + x, sy + y, c + 1));
22
23 if count > 0 {
24 Ok((sum_x / count as f32, sum_y / count as f32))
25 } else {
26 Err(GateError::invalid_geometry(
27 "Polygon has no valid coordinates",
28 ))
29 }
30 }
31}
32
33impl GateContainment for PolygonGateGeometry {
34 fn contains_point(&self, x: f32, y: f32, x_param: &str, y_param: &str) -> Result<bool> {
35 if !self.closed {
36 return Ok(false);
37 }
38
39 let coords: Vec<(f32, f32)> = self
41 .nodes
42 .iter()
43 .filter_map(|node| {
44 let x_coord = node.get_coordinate(x_param)?;
45 let y_coord = node.get_coordinate(y_param)?;
46 Some((x_coord, y_coord))
47 })
48 .collect();
49
50 if coords.len() < 3 {
51 return Ok(false);
52 }
53
54 Ok(point_in_polygon(x, y, &coords))
56 }
57}
58
59impl GateBounds for PolygonGateGeometry {
60 fn bounding_box(&self, x_param: &str, y_param: &str) -> Result<(f32, f32, f32, f32)> {
61 let coords: Vec<(f32, f32)> = self
62 .nodes
63 .iter()
64 .filter_map(|node| {
65 let x_coord = node.get_coordinate(x_param)?;
66 let y_coord = node.get_coordinate(y_param)?;
67 Some((x_coord, y_coord))
68 })
69 .collect();
70
71 if coords.is_empty() {
72 return Err(GateError::invalid_geometry(
73 "No valid coordinates for bounding box",
74 ));
75 }
76
77 let min_x = coords
78 .iter()
79 .map(|(x, _)| x)
80 .fold(f32::INFINITY, |a, &b| a.min(b));
81 let max_x = coords
82 .iter()
83 .map(|(x, _)| x)
84 .fold(f32::NEG_INFINITY, |a, &b| a.max(b));
85 let min_y = coords
86 .iter()
87 .map(|(_, y)| y)
88 .fold(f32::INFINITY, |a, &b| a.min(b));
89 let max_y = coords
90 .iter()
91 .map(|(_, y)| y)
92 .fold(f32::NEG_INFINITY, |a, &b| a.max(b));
93
94 Ok((min_x, min_y, max_x, max_y))
95 }
96}
97
98impl GateValidation for PolygonGateGeometry {
99 fn is_valid(&self, x_param: &str, y_param: &str) -> Result<bool> {
100 if self.nodes.len() < 3 {
102 return Ok(false);
103 }
104
105 let valid_coords = self.nodes.iter().all(|node| {
107 node.get_coordinate(x_param).is_some() && node.get_coordinate(y_param).is_some()
108 });
109
110 Ok(valid_coords)
111 }
112}
113
114impl GateGeometryOps for PolygonGateGeometry {
115 fn gate_type_name(&self) -> &'static str {
116 "Polygon"
117 }
118}
119
120fn point_in_polygon(x: f32, y: f32, polygon: &[(f32, f32)]) -> bool {
122 let mut inside = false;
123 let n = polygon.len();
124
125 for i in 0..n {
126 let (x1, y1) = polygon[i];
127 let (x2, y2) = polygon[(i + 1) % n];
128
129 if ((y1 > y) != (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1) {
130 inside = !inside;
131 }
132 }
133
134 inside
135}