Skip to main content

flow_gate_core/gate/
rectangle.rs

1use smallvec::SmallVec;
2
3use crate::error::FlowGateError;
4use crate::traits::{Gate, GateId, ParameterName};
5use crate::transform::TransformKind;
6
7#[derive(Debug, Clone)]
8pub struct RectangleDimension {
9    pub parameter: ParameterName,
10    pub transform: Option<TransformKind>,
11    pub min: Option<f64>,
12    pub max: Option<f64>,
13}
14
15#[derive(Debug, Clone)]
16pub struct RectangleGate {
17    id: GateId,
18    parent_id: Option<GateId>,
19    dimensions: SmallVec<[RectangleDimension; 4]>,
20    dim_names: SmallVec<[ParameterName; 4]>,
21}
22
23impl RectangleGate {
24    pub fn new(
25        id: GateId,
26        parent_id: Option<GateId>,
27        dimensions: Vec<RectangleDimension>,
28    ) -> Result<Self, FlowGateError> {
29        if dimensions.is_empty() {
30            return Err(FlowGateError::InvalidGate(
31                "RectangleGate requires at least one dimension".to_string(),
32            ));
33        }
34        let dimensions: SmallVec<[RectangleDimension; 4]> = dimensions.into();
35        let dim_names = dimensions.iter().map(|d| d.parameter.clone()).collect();
36        Ok(Self {
37            id,
38            parent_id,
39            dimensions,
40            dim_names,
41        })
42    }
43
44    pub fn rectangle_dimensions(&self) -> &[RectangleDimension] {
45        &self.dimensions
46    }
47}
48
49impl Gate for RectangleGate {
50    fn dimensions(&self) -> &[ParameterName] {
51        &self.dim_names
52    }
53
54    fn contains(&self, coords: &[f64]) -> bool {
55        if coords.len() != self.dimensions.len() {
56            return false;
57        }
58        coords
59            .iter()
60            .zip(self.dimensions.iter())
61            .all(|(&coord, dim)| {
62                if !coord.is_finite() {
63                    return false;
64                }
65                let above_min = dim.min.is_none_or(|lo| coord >= lo);
66                // Gating-ML ranges are [min, max), i.e. inclusive min and exclusive max.
67                let below_max = dim.max.is_none_or(|hi| coord < hi);
68                above_min && below_max
69            })
70    }
71
72    fn gate_id(&self) -> &GateId {
73        &self.id
74    }
75
76    fn parent_id(&self) -> Option<&GateId> {
77        self.parent_id.as_ref()
78    }
79}