flow_gates/
ellipse.rs

1use super::error::{GateError, Result};
2use super::traits::*;
3use super::types::GateNode;
4
5#[derive(Debug, Clone)]
6pub struct EllipseGateGeometry {
7    pub center: GateNode,
8    pub radius_x: f32,
9    pub radius_y: f32,
10    pub angle: f32, // rotation angle in radians
11}
12
13impl GateCenter for EllipseGateGeometry {
14    fn calculate_center(&self, x_param: &str, y_param: &str) -> Result<(f32, f32)> {
15        let cx = self
16            .center
17            .get_coordinate(x_param)
18            .ok_or_else(|| GateError::missing_parameter(x_param, "ellipse center"))?;
19        let cy = self
20            .center
21            .get_coordinate(y_param)
22            .ok_or_else(|| GateError::missing_parameter(y_param, "ellipse center"))?;
23
24        Ok((cx, cy))
25    }
26}
27
28impl GateContainment for EllipseGateGeometry {
29    fn contains_point(&self, x: f32, y: f32, x_param: &str, y_param: &str) -> Result<bool> {
30        let cx = self
31            .center
32            .get_coordinate(x_param)
33            .ok_or_else(|| GateError::missing_parameter(x_param, "ellipse center"))?;
34        let cy = self
35            .center
36            .get_coordinate(y_param)
37            .ok_or_else(|| GateError::missing_parameter(y_param, "ellipse center"))?;
38
39        // Rotate point around center by -angle
40        let cos_a = self.angle.cos();
41        let sin_a = self.angle.sin();
42        let dx = x - cx;
43        let dy = y - cy;
44        let rotated_x = dx * cos_a + dy * sin_a;
45        let rotated_y = -dx * sin_a + dy * cos_a;
46
47        // Check if point is inside axis-aligned ellipse
48        let normalized = (rotated_x / self.radius_x).powi(2) + (rotated_y / self.radius_y).powi(2);
49        Ok(normalized <= 1.0)
50    }
51}
52
53impl GateBounds for EllipseGateGeometry {
54    fn bounding_box(&self, x_param: &str, y_param: &str) -> Result<(f32, f32, f32, f32)> {
55        let cx = self
56            .center
57            .get_coordinate(x_param)
58            .ok_or_else(|| GateError::missing_parameter(x_param, "ellipse center"))?;
59        let cy = self
60            .center
61            .get_coordinate(y_param)
62            .ok_or_else(|| GateError::missing_parameter(y_param, "ellipse center"))?;
63
64        // For rotated ellipse, calculate actual bounding box
65        // Conservative approach: use max radius
66        let max_radius = self.radius_x.max(self.radius_y);
67
68        Ok((
69            cx - max_radius,
70            cy - max_radius,
71            cx + max_radius,
72            cy + max_radius,
73        ))
74    }
75}
76
77impl GateValidation for EllipseGateGeometry {
78    fn is_valid(&self, x_param: &str, y_param: &str) -> Result<bool> {
79        // Must have valid center coordinates
80        if self.center.get_coordinate(x_param).is_none()
81            || self.center.get_coordinate(y_param).is_none()
82        {
83            return Ok(false);
84        }
85
86        // Radii must be positive
87        Ok(self.radius_x > 0.0 && self.radius_y > 0.0)
88    }
89}
90
91impl GateGeometryOps for EllipseGateGeometry {
92    fn gate_type_name(&self) -> &'static str {
93        "Ellipse"
94    }
95}