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}