Skip to main content

flow_plots/options/
histogram.rs

1//! Options for histogram plots
2
3use crate::options::density::default_gate_colors;
4use crate::options::{AxisOptions, BasePlotOptions, PlotOptions};
5use derive_builder::Builder;
6
7/// Options for histogram plots
8///
9/// Supports single and overlaid histograms with filled or line-only style.
10///
11/// # Example
12///
13/// ```rust,no_run
14/// use flow_plots::options::{BasePlotOptions, HistogramPlotOptions};
15///
16/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
17/// let options = HistogramPlotOptions::new()
18///     .base(BasePlotOptions::new().width(800u32).height(600u32).build()?)
19///     .histogram_filled(true)
20///     .num_bins(50usize)
21///     .build()?;
22/// # Ok(())
23/// # }
24/// ```
25#[derive(Builder, Clone, Debug)]
26#[builder(setter(into, strip_option), default)]
27pub struct HistogramPlotOptions {
28    /// Base plot options (layout, dimensions, etc.)
29    #[builder(default)]
30    pub base: BasePlotOptions,
31
32    /// X-axis configuration (data range, transform, label)
33    #[builder(default)]
34    pub x_axis: AxisOptions,
35
36    /// Draw filled histogram (AreaSeries) vs line-only (LineSeries)
37    #[builder(default = "true")]
38    pub histogram_filled: bool,
39
40    /// Number of bins when binning raw values. Ignored for pre-binned data.
41    #[builder(default = "50")]
42    pub num_bins: usize,
43
44    /// Colors for each gate/series in overlaid histograms. Indexed by gate_id.
45    #[builder(default)]
46    pub gate_colors: Vec<(u8, u8, u8)>,
47
48    /// Y-offset between baselines for overlaid histograms. 0 = no separation (overlap).
49    #[builder(default = "0.0")]
50    pub baseline_separation: f32,
51
52    /// Scale each overlaid series to its modal peak (max count = 1.0) for visual comparison
53    #[builder(default = "false")]
54    pub scale_to_peak: bool,
55
56    /// Line width for unfilled histogram (when histogram_filled is false)
57    #[builder(default = "2.0")]
58    pub line_width: f64,
59}
60
61impl Default for HistogramPlotOptions {
62    fn default() -> Self {
63        Self {
64            base: BasePlotOptions::default(),
65            x_axis: AxisOptions::default(),
66            histogram_filled: true,
67            num_bins: 50,
68            gate_colors: Vec::new(),
69            baseline_separation: 0.0,
70            scale_to_peak: false,
71            line_width: 2.0,
72        }
73    }
74}
75
76impl PlotOptions for HistogramPlotOptions {
77    fn base(&self) -> &BasePlotOptions {
78        &self.base
79    }
80}
81
82impl HistogramPlotOptions {
83    /// Create a new builder for HistogramPlotOptions
84    pub fn new() -> HistogramPlotOptionsBuilder {
85        HistogramPlotOptionsBuilder::default()
86    }
87
88    /// Get gate color for the given gate_id
89    pub fn gate_color(&self, gate_id: u32) -> (u8, u8, u8) {
90        let colors = if self.gate_colors.is_empty() {
91            default_gate_colors()
92        } else {
93            self.gate_colors.clone()
94        };
95        colors
96            .get(gate_id as usize)
97            .copied()
98            .unwrap_or((60, 60, 60))
99    }
100}