flow_gate_core/gate/
polygon.rs1use crate::error::FlowGateError;
2use crate::traits::{Gate, GateId, ParameterName};
3use crate::transform::TransformKind;
4
5#[derive(Debug, Clone)]
6pub struct PolygonDimension {
7 pub parameter: ParameterName,
8 pub transform: Option<TransformKind>,
9}
10
11#[derive(Debug, Clone)]
12pub struct PolygonGate {
13 id: GateId,
14 parent_id: Option<GateId>,
15 x_dim: PolygonDimension,
16 y_dim: PolygonDimension,
17 dim_names: [ParameterName; 2],
18 vertices: Vec<(f64, f64)>,
19 aabb_x: (f64, f64),
20 aabb_y: (f64, f64),
21}
22
23impl PolygonGate {
24 pub fn new(
25 id: GateId,
26 parent_id: Option<GateId>,
27 x_dim: PolygonDimension,
28 y_dim: PolygonDimension,
29 vertices: Vec<(f64, f64)>,
30 ) -> Result<Self, FlowGateError> {
31 if vertices.len() < 3 {
32 return Err(FlowGateError::InvalidGate(
33 "PolygonGate requires at least 3 vertices".to_string(),
34 ));
35 }
36 let aabb_x = (
37 vertices
38 .iter()
39 .map(|(x, _)| *x)
40 .fold(f64::INFINITY, f64::min),
41 vertices
42 .iter()
43 .map(|(x, _)| *x)
44 .fold(f64::NEG_INFINITY, f64::max),
45 );
46 let aabb_y = (
47 vertices
48 .iter()
49 .map(|(_, y)| *y)
50 .fold(f64::INFINITY, f64::min),
51 vertices
52 .iter()
53 .map(|(_, y)| *y)
54 .fold(f64::NEG_INFINITY, f64::max),
55 );
56 let dim_names = [x_dim.parameter.clone(), y_dim.parameter.clone()];
57 Ok(Self {
58 id,
59 parent_id,
60 x_dim,
61 y_dim,
62 dim_names,
63 vertices,
64 aabb_x,
65 aabb_y,
66 })
67 }
68
69 pub fn x_dim(&self) -> &PolygonDimension {
70 &self.x_dim
71 }
72
73 pub fn y_dim(&self) -> &PolygonDimension {
74 &self.y_dim
75 }
76
77 pub fn vertices(&self) -> &[(f64, f64)] {
78 &self.vertices
79 }
80}
81
82impl Gate for PolygonGate {
83 fn dimensions(&self) -> &[ParameterName] {
84 &self.dim_names
85 }
86
87 fn contains(&self, coords: &[f64]) -> bool {
88 if coords.len() != 2 {
89 return false;
90 }
91 let (px, py) = (coords[0], coords[1]);
92 if !px.is_finite() || !py.is_finite() {
93 return false;
94 }
95 if px < self.aabb_x.0 || px > self.aabb_x.1 || py < self.aabb_y.0 || py > self.aabb_y.1 {
96 return false;
97 }
98
99 for i in 0..self.vertices.len() {
100 let (x0, y0) = self.vertices[i];
101 let (x1, y1) = self.vertices[(i + 1) % self.vertices.len()];
102 if point_on_segment(px, py, x0, y0, x1, y1) {
103 return true;
104 }
105 }
106
107 winding_number(px, py, &self.vertices).abs() % 2 == 1
110 }
111
112 fn gate_id(&self) -> &GateId {
113 &self.id
114 }
115
116 fn parent_id(&self) -> Option<&GateId> {
117 self.parent_id.as_ref()
118 }
119}
120
121#[inline]
122pub fn is_left(x0: f64, y0: f64, x1: f64, y1: f64, px: f64, py: f64) -> f64 {
123 (x1 - x0) * (py - y0) - (px - x0) * (y1 - y0)
124}
125
126const POLYGON_EDGE_EPS: f64 = 1e-9;
132
133pub fn winding_number(px: f64, py: f64, vertices: &[(f64, f64)]) -> i32 {
134 let n = vertices.len();
135 let mut wn = 0_i32;
136 for i in 0..n {
137 let (x0, y0) = vertices[i];
138 let (x1, y1) = vertices[(i + 1) % n];
139
140 let cp = is_left(x0, y0, x1, y1, px, py);
143 if cp.abs() < POLYGON_EDGE_EPS {
144 return 1;
145 }
146
147 if y0 <= py {
148 if y1 > py && cp > 0.0 {
149 wn += 1;
150 }
151 } else if y1 <= py && cp < 0.0 {
152 wn -= 1;
153 }
154 }
155 wn
156}
157
158fn point_on_segment(px: f64, py: f64, x0: f64, y0: f64, x1: f64, y1: f64) -> bool {
160 let cross = is_left(x0, y0, x1, y1, px, py).abs();
161 if cross > POLYGON_EDGE_EPS {
162 return false;
163 }
164 let min_x = x0.min(x1) - POLYGON_EDGE_EPS;
165 let max_x = x0.max(x1) + POLYGON_EDGE_EPS;
166 let min_y = y0.min(y1) - POLYGON_EDGE_EPS;
167 let max_y = y0.max(y1) + POLYGON_EDGE_EPS;
168 px >= min_x && px <= max_x && py >= min_y && py <= max_y
169}