Skip to main content

flow_plots/plots/
density.rs

1use crate::PlotBytes;
2use crate::density_calc::calculate_density_per_pixel;
3use crate::options::{DensityPlotOptions, PlotOptions};
4use crate::plots::traits::Plot;
5use crate::render::RenderConfig;
6use crate::render::plotters_backend::render_pixels;
7use anyhow::Result;
8
9/// Density plot implementation
10///
11/// Creates a 2D density plot from (x, y) coordinate pairs by binning
12/// data points into pixels and coloring by density.
13///
14/// # Example
15///
16/// ```rust,no_run
17/// use flow_plots::plots::density::DensityPlot;
18/// use flow_plots::plots::traits::Plot;
19/// use flow_plots::options::{DensityPlotOptions, BasePlotOptions};
20/// use flow_plots::render::RenderConfig;
21///
22/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
23/// let plot = DensityPlot::new();
24/// let options = DensityPlotOptions::new()
25///     .base(BasePlotOptions::new().width(800u32).height(600u32).build()?)
26///     .build()?;
27/// let data: Vec<(f32, f32)> = vec![(100.0, 200.0), (150.0, 250.0)];
28/// let mut render_config = RenderConfig::default();
29/// let bytes = plot.render(data, &options, &mut render_config)?;
30/// # Ok(())
31/// # }
32/// ```
33pub struct DensityPlot;
34
35impl DensityPlot {
36    /// Create a new DensityPlot instance
37    pub fn new() -> Self {
38        Self
39    }
40
41    /// Render multiple density plots in batch
42    ///
43    /// High-level convenience method that handles both density calculation
44    /// and rendering. For apps that want to orchestrate rendering themselves,
45    /// use `calculate_density_per_pixel_batch` instead.
46    ///
47    /// # Arguments
48    /// * `requests` - Vector of (data, options) tuples
49    /// * `render_config` - Rendering configuration (shared across all plots)
50    ///
51    /// # Returns
52    /// Vector of plot bytes, one per request
53    pub fn render_batch(
54        &self,
55        requests: &[(Vec<(f32, f32)>, DensityPlotOptions)],
56        render_config: &mut RenderConfig,
57    ) -> Result<Vec<PlotBytes>> {
58        use crate::density_calc::calculate_density_per_pixel_batch;
59
60        // Calculate density for all plots
61        let raw_pixels_batch = calculate_density_per_pixel_batch(requests);
62
63        // Render each plot
64        let mut results = Vec::with_capacity(requests.len());
65        for (i, raw_pixels) in raw_pixels_batch.iter().enumerate() {
66            let bytes = render_pixels(raw_pixels.clone(), &requests[i].1, render_config)?;
67            results.push(bytes);
68        }
69        Ok(results)
70    }
71}
72
73impl Plot for DensityPlot {
74    type Options = DensityPlotOptions;
75    type Data = Vec<(f32, f32)>;
76
77    fn render(
78        &self,
79        data: Self::Data,
80        options: &Self::Options,
81        render_config: &mut RenderConfig,
82    ) -> Result<PlotBytes> {
83        let density_start = std::time::Instant::now();
84
85        // Calculate density per pixel
86        let base = options.base();
87        let raw_pixels = calculate_density_per_pixel(
88            &data[..],
89            base.width as usize,
90            base.height as usize,
91            options,
92        );
93
94        eprintln!(
95            "  ├─ Density calculation: {:?} ({} pixels at {}x{})",
96            density_start.elapsed(),
97            raw_pixels.len(),
98            base.width,
99            base.height
100        );
101
102        let draw_start = std::time::Instant::now();
103        let result = render_pixels(raw_pixels, options, render_config);
104        eprintln!("  └─ Draw + encode: {:?}", draw_start.elapsed());
105
106        result
107    }
108}