1use crate::error::{GateError, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct GateNode {
24 pub id: Arc<str>,
26 #[serde(with = "arc_str_hashmap")]
31 pub coordinates: HashMap<Arc<str>, f32>,
32}
33
34impl GateNode {
35 pub fn new(id: impl Into<Arc<str>>) -> Self {
40 Self {
41 id: id.into(),
42 coordinates: HashMap::new(),
43 }
44 }
45
46 pub fn with_coordinate(mut self, channel: impl Into<Arc<str>>, value: f32) -> Self {
50 self.coordinates.insert(channel.into(), value);
51 self
52 }
53
54 pub fn get_coordinate(&self, channel: &str) -> Option<f32> {
58 self.coordinates.get(channel).copied()
59 }
60
61 pub fn set_coordinate(&mut self, channel: impl Into<Arc<str>>, value: f32) {
62 self.coordinates.insert(channel.into(), value);
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(tag = "type")]
96pub enum GateGeometry {
97 Polygon {
98 nodes: Vec<GateNode>,
99 closed: bool,
100 },
101 Rectangle {
102 min: GateNode,
103 max: GateNode,
104 },
105 Ellipse {
106 center: GateNode,
107 radius_x: f32,
108 radius_y: f32,
109 angle: f32, },
111}
112
113impl GateGeometry {
114 pub fn bounding_box(&self, x_param: &str, y_param: &str) -> Option<(f32, f32, f32, f32)> {
116 match self {
117 GateGeometry::Polygon { nodes, .. } => {
118 let mut min_x = f32::MAX;
119 let mut min_y = f32::MAX;
120 let mut max_x = f32::MIN;
121 let mut max_y = f32::MIN;
122
123 for node in nodes {
124 if let (Some(x), Some(y)) =
125 (node.get_coordinate(x_param), node.get_coordinate(y_param))
126 {
127 min_x = min_x.min(x);
128 min_y = min_y.min(y);
129 max_x = max_x.max(x);
130 max_y = max_y.max(y);
131 }
132 }
133
134 if min_x < max_x && min_y < max_y {
135 Some((min_x, min_y, max_x, max_y))
136 } else {
137 None
138 }
139 }
140 GateGeometry::Rectangle { min, max } => {
141 if let (Some(min_x), Some(min_y), Some(max_x), Some(max_y)) = (
142 min.get_coordinate(x_param),
143 min.get_coordinate(y_param),
144 max.get_coordinate(x_param),
145 max.get_coordinate(y_param),
146 ) {
147 Some((min_x, min_y, max_x, max_y))
148 } else {
149 None
150 }
151 }
152 GateGeometry::Ellipse {
153 center,
154 radius_x,
155 radius_y,
156 angle,
157 } => {
158 if let (Some(cx), Some(cy)) = (
159 center.get_coordinate(x_param),
160 center.get_coordinate(y_param),
161 ) {
162 let cos_a = angle.cos();
163 let sin_a = angle.sin();
164
165 let extent_x = ((radius_x * cos_a).powi(2) + (radius_y * sin_a).powi(2)).sqrt();
167 let extent_y = ((radius_x * sin_a).powi(2) + (radius_y * cos_a).powi(2)).sqrt();
168
169 Some((cx - extent_x, cy - extent_y, cx + extent_x, cy + extent_y))
170 } else {
171 None
172 }
173 }
174 }
175 }
176
177 pub fn calculate_center(&self, x_param: &str, y_param: &str) -> Result<(f32, f32)> {
179 match self {
180 GateGeometry::Polygon { nodes, .. } => {
181 let (sum_x, sum_y, count) = nodes
182 .iter()
183 .filter_map(|node| {
184 let x = node.get_coordinate(x_param)?;
185 let y = node.get_coordinate(y_param)?;
186 Some((x, y))
187 })
188 .fold((0.0, 0.0, 0), |(sx, sy, c), (x, y)| (sx + x, sy + y, c + 1));
189
190 if count > 0 {
191 Ok((sum_x / count as f32, sum_y / count as f32))
192 } else {
193 Err(GateError::invalid_geometry(
194 "Polygon has no valid coordinates",
195 ))
196 }
197 }
198 GateGeometry::Rectangle { min, max } => {
199 let min_x = min
200 .get_coordinate(x_param)
201 .ok_or_else(|| GateError::missing_parameter(x_param, "rectangle min"))?;
202 let min_y = min
203 .get_coordinate(y_param)
204 .ok_or_else(|| GateError::missing_parameter(y_param, "rectangle min"))?;
205 let max_x = max
206 .get_coordinate(x_param)
207 .ok_or_else(|| GateError::missing_parameter(x_param, "rectangle max"))?;
208 let max_y = max
209 .get_coordinate(y_param)
210 .ok_or_else(|| GateError::missing_parameter(y_param, "rectangle max"))?;
211
212 Ok(((min_x + max_x) / 2.0, (min_y + max_y) / 2.0))
213 }
214 GateGeometry::Ellipse { center, .. } => {
215 let cx = center
216 .get_coordinate(x_param)
217 .ok_or_else(|| GateError::missing_parameter(x_param, "ellipse center"))?;
218 let cy = center
219 .get_coordinate(y_param)
220 .ok_or_else(|| GateError::missing_parameter(y_param, "ellipse center"))?;
221
222 Ok((cx, cy))
223 }
224 }
225 }
226
227 pub fn contains_point(&self, x: f32, y: f32, x_param: &str, y_param: &str) -> Result<bool> {
229 match self {
230 GateGeometry::Polygon { nodes, closed } => {
231 if !closed {
232 return Ok(false);
233 }
234
235 let coords: Vec<(f32, f32)> = nodes
237 .iter()
238 .filter_map(|node| {
239 Some((node.get_coordinate(x_param)?, node.get_coordinate(y_param)?))
240 })
241 .collect();
242
243 if coords.len() < 3 {
244 return Ok(false);
245 }
246
247 Ok(point_in_polygon(x, y, &coords))
249 }
250 GateGeometry::Rectangle { min, max } => {
251 let min_x = min
252 .get_coordinate(x_param)
253 .ok_or_else(|| GateError::missing_parameter(x_param, "rectangle min"))?;
254 let min_y = min
255 .get_coordinate(y_param)
256 .ok_or_else(|| GateError::missing_parameter(y_param, "rectangle min"))?;
257 let max_x = max
258 .get_coordinate(x_param)
259 .ok_or_else(|| GateError::missing_parameter(x_param, "rectangle max"))?;
260 let max_y = max
261 .get_coordinate(y_param)
262 .ok_or_else(|| GateError::missing_parameter(y_param, "rectangle max"))?;
263
264 Ok(x >= min_x && x <= max_x && y >= min_y && y <= max_y)
265 }
266 GateGeometry::Ellipse {
267 center,
268 radius_x,
269 radius_y,
270 angle,
271 } => {
272 let cx = center
273 .get_coordinate(x_param)
274 .ok_or_else(|| GateError::missing_parameter(x_param, "ellipse center"))?;
275 let cy = center
276 .get_coordinate(y_param)
277 .ok_or_else(|| GateError::missing_parameter(y_param, "ellipse center"))?;
278
279 let cos_a = angle.cos();
281 let sin_a = angle.sin();
282 let dx = x - cx;
283 let dy = y - cy;
284 let rotated_x = dx * cos_a + dy * sin_a;
285 let rotated_y = -dx * sin_a + dy * cos_a;
286
287 let normalized = (rotated_x / radius_x).powi(2) + (rotated_y / radius_y).powi(2);
289 Ok(normalized <= 1.0)
290 }
291 }
292 }
293
294 pub fn is_valid(&self, x_param: &str, y_param: &str) -> Result<bool> {
296 match self {
297 GateGeometry::Polygon { nodes, .. } => {
298 if nodes.len() < 3 {
300 return Ok(false);
301 }
302
303 let valid_coords = nodes.iter().all(|node| {
305 node.get_coordinate(x_param).is_some() && node.get_coordinate(y_param).is_some()
306 });
307
308 Ok(valid_coords)
309 }
310 GateGeometry::Rectangle { min, max } => {
311 if min.get_coordinate(x_param).is_none()
313 || min.get_coordinate(y_param).is_none()
314 || max.get_coordinate(x_param).is_none()
315 || max.get_coordinate(y_param).is_none()
316 {
317 return Ok(false);
318 }
319
320 let min_x = min.get_coordinate(x_param).unwrap();
322 let min_y = min.get_coordinate(y_param).unwrap();
323 let max_x = max.get_coordinate(x_param).unwrap();
324 let max_y = max.get_coordinate(y_param).unwrap();
325
326 Ok(min_x < max_x && min_y < max_y)
327 }
328 GateGeometry::Ellipse {
329 center,
330 radius_x,
331 radius_y,
332 ..
333 } => {
334 if center.get_coordinate(x_param).is_none()
336 || center.get_coordinate(y_param).is_none()
337 {
338 return Ok(false);
339 }
340
341 Ok(radius_x > &0.0 && radius_y > &0.0)
343 }
344 }
345 }
346
347 pub fn gate_type_name(&self) -> &'static str {
349 match self {
350 GateGeometry::Polygon { .. } => "Polygon",
351 GateGeometry::Rectangle { .. } => "Rectangle",
352 GateGeometry::Ellipse { .. } => "Ellipse",
353 }
354 }
355}
356
357use crate::traits::*;
359
360impl GateCenter for GateGeometry {
361 fn calculate_center(&self, x_param: &str, y_param: &str) -> Result<(f32, f32)> {
362 match self {
363 GateGeometry::Polygon { nodes, closed } => {
364 let poly = crate::polygon::PolygonGateGeometry {
365 nodes: nodes.clone(),
366 closed: *closed,
367 };
368 poly.calculate_center(x_param, y_param)
369 }
370 GateGeometry::Rectangle { min, max } => {
371 let rect = crate::rectangle::RectangleGateGeometry {
372 min: min.clone(),
373 max: max.clone(),
374 };
375 rect.calculate_center(x_param, y_param)
376 }
377 GateGeometry::Ellipse {
378 center,
379 radius_x,
380 radius_y,
381 angle,
382 } => {
383 let ellipse = crate::ellipse::EllipseGateGeometry {
384 center: center.clone(),
385 radius_x: *radius_x,
386 radius_y: *radius_y,
387 angle: *angle,
388 };
389 ellipse.calculate_center(x_param, y_param)
390 }
391 }
392 }
393}
394
395impl GateContainment for GateGeometry {
396 fn contains_point(&self, x: f32, y: f32, x_param: &str, y_param: &str) -> Result<bool> {
397 match self {
398 GateGeometry::Polygon { nodes, closed } => {
399 let poly = crate::polygon::PolygonGateGeometry {
400 nodes: nodes.clone(),
401 closed: *closed,
402 };
403 poly.contains_point(x, y, x_param, y_param)
404 }
405 GateGeometry::Rectangle { min, max } => {
406 let rect = crate::rectangle::RectangleGateGeometry {
407 min: min.clone(),
408 max: max.clone(),
409 };
410 rect.contains_point(x, y, x_param, y_param)
411 }
412 GateGeometry::Ellipse {
413 center,
414 radius_x,
415 radius_y,
416 angle,
417 } => {
418 let ellipse = crate::ellipse::EllipseGateGeometry {
419 center: center.clone(),
420 radius_x: *radius_x,
421 radius_y: *radius_y,
422 angle: *angle,
423 };
424 ellipse.contains_point(x, y, x_param, y_param)
425 }
426 }
427 }
428}
429
430impl GateBounds for GateGeometry {
431 fn bounding_box(&self, x_param: &str, y_param: &str) -> Result<(f32, f32, f32, f32)> {
432 match self {
433 GateGeometry::Polygon { nodes, closed } => {
434 let poly = crate::polygon::PolygonGateGeometry {
435 nodes: nodes.clone(),
436 closed: *closed,
437 };
438 poly.bounding_box(x_param, y_param)
439 }
440 GateGeometry::Rectangle { min, max } => {
441 let rect = crate::rectangle::RectangleGateGeometry {
442 min: min.clone(),
443 max: max.clone(),
444 };
445 rect.bounding_box(x_param, y_param)
446 }
447 GateGeometry::Ellipse {
448 center,
449 radius_x,
450 radius_y,
451 angle,
452 } => {
453 let ellipse = crate::ellipse::EllipseGateGeometry {
454 center: center.clone(),
455 radius_x: *radius_x,
456 radius_y: *radius_y,
457 angle: *angle,
458 };
459 ellipse.bounding_box(x_param, y_param)
460 }
461 }
462 }
463}
464
465impl GateValidation for GateGeometry {
466 fn is_valid(&self, x_param: &str, y_param: &str) -> Result<bool> {
467 match self {
468 GateGeometry::Polygon { nodes, closed } => {
469 let poly = crate::polygon::PolygonGateGeometry {
470 nodes: nodes.clone(),
471 closed: *closed,
472 };
473 poly.is_valid(x_param, y_param)
474 }
475 GateGeometry::Rectangle { min, max } => {
476 let rect = crate::rectangle::RectangleGateGeometry {
477 min: min.clone(),
478 max: max.clone(),
479 };
480 rect.is_valid(x_param, y_param)
481 }
482 GateGeometry::Ellipse {
483 center,
484 radius_x,
485 radius_y,
486 angle,
487 } => {
488 let ellipse = crate::ellipse::EllipseGateGeometry {
489 center: center.clone(),
490 radius_x: *radius_x,
491 radius_y: *radius_y,
492 angle: *angle,
493 };
494 ellipse.is_valid(x_param, y_param)
495 }
496 }
497 }
498}
499
500impl GateGeometryOps for GateGeometry {
501 fn gate_type_name(&self) -> &'static str {
502 match self {
503 GateGeometry::Polygon { .. } => "Polygon",
504 GateGeometry::Rectangle { .. } => "Rectangle",
505 GateGeometry::Ellipse { .. } => "Ellipse",
506 }
507 }
508}
509
510fn point_in_polygon(x: f32, y: f32, polygon: &[(f32, f32)]) -> bool {
512 let mut inside = false;
513 let n = polygon.len();
514
515 for i in 0..n {
516 let (x1, y1) = polygon[i];
517 let (x2, y2) = polygon[(i + 1) % n];
518
519 if ((y1 > y) != (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1) {
520 inside = !inside;
521 }
522 }
523
524 inside
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
558#[serde(tag = "name")]
559pub enum GateMode {
560 Global,
562 FileSpecific {
564 #[serde(with = "arc_str_serde")]
566 guid: Arc<str>,
567 },
568 FileGroup {
570 #[serde(with = "arc_str_vec")]
572 guids: Vec<Arc<str>>,
573 },
574}
575
576impl GateMode {
577 pub fn applies_to(&self, file_guid: &str) -> bool {
581 match self {
582 GateMode::Global => true,
583 GateMode::FileSpecific { guid } => guid.as_ref() == file_guid,
584 GateMode::FileGroup { guids } => guids.iter().any(|g| g.as_ref() == file_guid),
585 }
586 }
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
592pub struct LabelPosition {
593 pub offset_x: f32,
595 pub offset_y: f32,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct Gate {
637 #[serde(with = "arc_str_serde")]
638 pub id: Arc<str>,
639 pub name: String,
640 pub geometry: GateGeometry,
641 pub mode: GateMode,
642 #[serde(with = "arc_str_pair")]
644 pub parameters: (Arc<str>, Arc<str>),
645 pub label_position: Option<LabelPosition>,
647}
648
649impl Gate {
650 pub fn new(
663 id: impl Into<Arc<str>>,
664 name: impl Into<String>,
665 geometry: GateGeometry,
666 x_param: impl Into<Arc<str>>,
667 y_param: impl Into<Arc<str>>,
668 ) -> Self {
669 let x_param = x_param.into();
670 let y_param = y_param.into();
671
672 Self {
673 id: id.into(),
674 name: name.into(),
675 geometry,
676 mode: GateMode::Global, parameters: (x_param, y_param),
678 label_position: None,
679 }
680 }
681
682 pub fn x_parameter_channel_name(&self) -> &str {
684 self.parameters.0.as_ref()
685 }
686
687 pub fn y_parameter_channel_name(&self) -> &str {
689 self.parameters.1.as_ref()
690 }
691}
692
693mod arc_str_serde {
695 use serde::{Deserialize, Deserializer, Serialize, Serializer};
696 use std::sync::Arc;
697
698 pub fn serialize<S>(arc: &Arc<str>, serializer: S) -> Result<S::Ok, S::Error>
699 where
700 S: Serializer,
701 {
702 arc.as_ref().serialize(serializer)
703 }
704
705 pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<str>, D::Error>
706 where
707 D: Deserializer<'de>,
708 {
709 let s = String::deserialize(deserializer)?;
710 Ok(Arc::from(s.as_str()))
711 }
712}
713
714mod arc_str_vec {
715 use serde::{Deserialize, Deserializer, Serialize, Serializer};
716 use std::sync::Arc;
717
718 pub fn serialize<S>(vec: &Vec<Arc<str>>, serializer: S) -> Result<S::Ok, S::Error>
719 where
720 S: Serializer,
721 {
722 let strings: Vec<&str> = vec.iter().map(|arc| arc.as_ref()).collect();
723 strings.serialize(serializer)
724 }
725
726 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Arc<str>>, D::Error>
727 where
728 D: Deserializer<'de>,
729 {
730 let vec = Vec::<String>::deserialize(deserializer)?;
731 Ok(vec.into_iter().map(|s| Arc::from(s.as_str())).collect())
732 }
733}
734
735mod arc_str_pair {
736 use serde::{Deserialize, Deserializer, Serialize, Serializer};
737 use std::sync::Arc;
738
739 pub fn serialize<S>(pair: &(Arc<str>, Arc<str>), serializer: S) -> Result<S::Ok, S::Error>
740 where
741 S: Serializer,
742 {
743 (pair.0.as_ref(), pair.1.as_ref()).serialize(serializer)
744 }
745
746 pub fn deserialize<'de, D>(deserializer: D) -> Result<(Arc<str>, Arc<str>), D::Error>
747 where
748 D: Deserializer<'de>,
749 {
750 let (s1, s2) = <(String, String)>::deserialize(deserializer)?;
751 Ok((Arc::from(s1.as_str()), Arc::from(s2.as_str())))
752 }
753}
754
755mod arc_str_hashmap {
756 use serde::{Deserialize, Deserializer, Serialize, Serializer};
757 use std::collections::HashMap;
758 use std::sync::Arc;
759
760 pub fn serialize<S>(map: &HashMap<Arc<str>, f32>, serializer: S) -> Result<S::Ok, S::Error>
761 where
762 S: Serializer,
763 {
764 let string_map: HashMap<&str, f32> = map.iter().map(|(k, v)| (k.as_ref(), *v)).collect();
765 string_map.serialize(serializer)
766 }
767
768 pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<Arc<str>, f32>, D::Error>
769 where
770 D: Deserializer<'de>,
771 {
772 let map = HashMap::<String, f32>::deserialize(deserializer)?;
773 Ok(map
774 .into_iter()
775 .map(|(k, v)| (Arc::from(k.as_str()), v))
776 .collect())
777 }
778}