Skip to main content

flow_plots/
scatter_data.rs

1//! Scatter plot data types supporting simple, gate-colored, and z-axis colored plots.
2
3/// Scatter plot data with optional per-point metadata for overlay and continuous coloring.
4///
5/// Use [`From`] to convert from `Vec<(f32, f32)>` for simple plots.
6///
7/// # Examples
8///
9/// Simple scatter (solid or density):
10/// ```
11/// # use flow_plots::scatter_data::ScatterPlotData;
12/// let data: ScatterPlotData = vec![(1.0, 2.0), (3.0, 4.0)].into();
13/// ```
14///
15/// Scatter with discrete gate colors:
16/// ```
17/// # use flow_plots::scatter_data::ScatterPlotData;
18/// let points = vec![(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)];
19/// let gate_ids = vec![0, 1, 0];  // gate index per point
20/// let data = ScatterPlotData::with_gates(points, gate_ids);
21/// ```
22///
23/// Scatter colored by continuous z-axis:
24/// ```
25/// # use flow_plots::scatter_data::ScatterPlotData;
26/// let points = vec![(1.0, 2.0), (3.0, 4.0)];
27/// let z_values = vec![0.5, 1.0];
28/// let data = ScatterPlotData::with_z(points, z_values);
29/// ```
30#[derive(Clone, Debug)]
31pub struct ScatterPlotData {
32    /// (x, y) coordinate pairs
33    pub points: Vec<(f32, f32)>,
34    /// Gate index per point (for discrete overlay coloring). Indexes into `gate_colors` in options.
35    pub gate_ids: Option<Vec<u32>>,
36    /// Z-axis value per point (for continuous colormap coloring)
37    pub z_values: Option<Vec<f32>>,
38}
39
40impl ScatterPlotData {
41    /// Create simple scatter data (no gates, no z-axis).
42    pub fn new(points: Vec<(f32, f32)>) -> Self {
43        Self {
44            points,
45            gate_ids: None,
46            z_values: None,
47        }
48    }
49
50    /// Create scatter data with discrete gate IDs for overlay coloring.
51    ///
52    /// # Errors
53    /// Returns `Err` if `gate_ids.len() != points.len()`.
54    pub fn with_gates(
55        points: Vec<(f32, f32)>,
56        gate_ids: Vec<u32>,
57    ) -> Result<Self, ScatterDataError> {
58        if gate_ids.len() != points.len() {
59            return Err(ScatterDataError {
60                points_len: points.len(),
61                metadata_len: gate_ids.len(),
62                field: "gate_ids",
63            });
64        }
65        Ok(Self {
66            points,
67            gate_ids: Some(gate_ids),
68            z_values: None,
69        })
70    }
71
72    /// Create scatter data with z-axis values for continuous coloring.
73    ///
74    /// # Errors
75    /// Returns `Err` if `z_values.len() != points.len()`.
76    pub fn with_z(points: Vec<(f32, f32)>, z_values: Vec<f32>) -> Result<Self, ScatterDataError> {
77        if z_values.len() != points.len() {
78            return Err(ScatterDataError {
79                points_len: points.len(),
80                metadata_len: z_values.len(),
81                field: "z_values",
82            });
83        }
84        Ok(Self {
85            points,
86            gate_ids: None,
87            z_values: Some(z_values),
88        })
89    }
90
91    /// Slice of (x, y) points.
92    pub fn xy(&self) -> &[(f32, f32)] {
93        &self.points
94    }
95
96    /// Whether this data has gate overlay info.
97    pub fn has_gates(&self) -> bool {
98        self.gate_ids.is_some()
99    }
100
101    /// Whether this data has z-axis values for continuous coloring.
102    pub fn has_z(&self) -> bool {
103        self.z_values.is_some()
104    }
105}
106
107impl From<Vec<(f32, f32)>> for ScatterPlotData {
108    fn from(points: Vec<(f32, f32)>) -> Self {
109        Self::new(points)
110    }
111}
112
113/// Error when constructing scatter plot data with invalid metadata lengths.
114#[derive(Debug, Clone)]
115pub struct ScatterDataError {
116    points_len: usize,
117    metadata_len: usize,
118    field: &'static str,
119}
120
121impl std::fmt::Display for ScatterDataError {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        write!(
124            f,
125            "length mismatch: points has {} elements but {} has {}",
126            self.points_len, self.field, self.metadata_len
127        )
128    }
129}
130
131impl std::error::Error for ScatterDataError {}