dear_implot/plots/
mod.rs

1//! Modular plot types for ImPlot
2//!
3//! This module provides a modular approach to different plot types,
4//! each with their own builder pattern and configuration options.
5
6pub mod bar;
7pub mod bar_groups;
8pub mod digital;
9pub mod dummy;
10pub mod error_bars;
11pub mod heatmap;
12pub mod histogram;
13pub mod image;
14pub mod inf_lines;
15pub mod line;
16pub mod pie;
17pub mod scatter;
18pub mod shaded;
19pub mod stairs;
20pub mod stems;
21pub mod text;
22
23// Re-export all plot types for convenience
24pub use bar::*;
25pub use bar_groups::*;
26pub use digital::*;
27pub use dummy::*;
28pub use error_bars::*;
29pub use heatmap::*;
30pub use histogram::*;
31pub use image::*;
32pub use inf_lines::*;
33pub use line::*;
34pub use pie::*;
35pub use scatter::*;
36pub use shaded::*;
37pub use stairs::*;
38pub use stems::*;
39pub use text::*;
40
41/// Common trait for all plot types
42pub trait Plot {
43    /// Plot this element
44    fn plot(&self);
45
46    /// Get the label for this plot
47    fn label(&self) -> &str;
48}
49
50/// Common trait for plot data validation
51pub trait PlotData {
52    /// Get the label for this plot
53    fn label(&self) -> &str;
54
55    /// Get the length of the data
56    fn data_len(&self) -> usize;
57
58    /// Check if the data is empty
59    fn is_empty(&self) -> bool {
60        self.data_len() == 0
61    }
62
63    /// Validate the data for plotting
64    fn validate(&self) -> Result<(), PlotError> {
65        if self.is_empty() {
66            Err(PlotError::EmptyData)
67        } else {
68            Ok(())
69        }
70    }
71}
72
73/// Errors that can occur during plotting
74#[derive(Debug, Clone, PartialEq)]
75pub enum PlotError {
76    /// Data arrays have mismatched lengths
77    DataLengthMismatch { x_len: usize, y_len: usize },
78    /// Data is empty
79    EmptyData,
80    /// Invalid parameter value or data
81    InvalidData(String),
82    /// String conversion error
83    StringConversion(String),
84    /// Plot creation failed
85    PlotCreationFailed(String),
86}
87
88impl std::fmt::Display for PlotError {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        match self {
91            PlotError::DataLengthMismatch { x_len, y_len } => {
92                write!(
93                    f,
94                    "Data length mismatch: x has {} elements, y has {} elements",
95                    x_len, y_len
96                )
97            }
98            PlotError::EmptyData => write!(f, "Data is empty"),
99            PlotError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
100            PlotError::StringConversion(msg) => write!(f, "String conversion error: {}", msg),
101            PlotError::PlotCreationFailed(msg) => write!(f, "Plot creation failed: {}", msg),
102        }
103    }
104}
105
106impl std::error::Error for PlotError {}
107
108// Note: PlotData trait is implemented by individual plot types
109// rather than raw data types, since each plot needs its own label
110
111/// Helper function to validate data length consistency for slices
112pub fn validate_data_lengths<T, U>(data1: &[T], data2: &[U]) -> Result<(), PlotError> {
113    if data1.is_empty() || data2.is_empty() {
114        return Err(PlotError::EmptyData);
115    }
116
117    if data1.len() != data2.len() {
118        return Err(PlotError::DataLengthMismatch {
119            x_len: data1.len(),
120            y_len: data2.len(),
121        });
122    }
123
124    Ok(())
125}
126
127/// Helper function to create a CString safely
128/// This follows the pattern used in dear-imgui for safe string conversion
129pub fn safe_cstring(s: &str) -> std::ffi::CString {
130    // Remove any null bytes from the string to prevent CString creation errors
131    let cleaned = s.replace('\0', "");
132    std::ffi::CString::new(cleaned).unwrap_or_else(|_| {
133        // If there's still an error, create an empty string
134        std::ffi::CString::new("").unwrap()
135    })
136}
137
138/// Universal plot builder that can create any plot type
139pub struct PlotBuilder<'a> {
140    plot_type: PlotType<'a>,
141}
142
143/// Enum representing different plot types
144pub enum PlotType<'a> {
145    Line {
146        label: &'a str,
147        x_data: &'a [f64],
148        y_data: &'a [f64],
149    },
150    Scatter {
151        label: &'a str,
152        x_data: &'a [f64],
153        y_data: &'a [f64],
154    },
155    Bar {
156        label: &'a str,
157        values: &'a [f64],
158        width: f64,
159    },
160    Histogram {
161        label: &'a str,
162        values: &'a [f64],
163        bins: i32,
164    },
165    Heatmap {
166        label: &'a str,
167        values: &'a [f64],
168        rows: usize,
169        cols: usize,
170    },
171    PieChart {
172        labels: Vec<&'a str>,
173        values: &'a [f64],
174        center: (f64, f64),
175        radius: f64,
176    },
177}
178
179impl<'a> PlotBuilder<'a> {
180    /// Create a line plot
181    pub fn line(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
182        Self {
183            plot_type: PlotType::Line {
184                label,
185                x_data,
186                y_data,
187            },
188        }
189    }
190
191    /// Create a scatter plot
192    pub fn scatter(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
193        Self {
194            plot_type: PlotType::Scatter {
195                label,
196                x_data,
197                y_data,
198            },
199        }
200    }
201
202    /// Create a bar plot
203    pub fn bar(label: &'a str, values: &'a [f64]) -> Self {
204        Self {
205            plot_type: PlotType::Bar {
206                label,
207                values,
208                width: 0.67,
209            },
210        }
211    }
212
213    /// Create a histogram
214    pub fn histogram(label: &'a str, values: &'a [f64]) -> Self {
215        Self {
216            plot_type: PlotType::Histogram {
217                label,
218                values,
219                bins: crate::BinMethod::Sturges as i32,
220            },
221        }
222    }
223
224    /// Create a heatmap
225    pub fn heatmap(label: &'a str, values: &'a [f64], rows: usize, cols: usize) -> Self {
226        Self {
227            plot_type: PlotType::Heatmap {
228                label,
229                values,
230                rows,
231                cols,
232            },
233        }
234    }
235
236    /// Create a pie chart
237    pub fn pie_chart(
238        labels: Vec<&'a str>,
239        values: &'a [f64],
240        center: (f64, f64),
241        radius: f64,
242    ) -> Self {
243        Self {
244            plot_type: PlotType::PieChart {
245                labels,
246                values,
247                center,
248                radius,
249            },
250        }
251    }
252
253    /// Build and plot the chart
254    pub fn build(self) -> Result<(), PlotError> {
255        match self.plot_type {
256            PlotType::Line {
257                label,
258                x_data,
259                y_data,
260            } => {
261                let plot = line::LinePlot::new(label, x_data, y_data);
262                plot.validate()?;
263                plot.plot();
264            }
265            PlotType::Scatter {
266                label,
267                x_data,
268                y_data,
269            } => {
270                let plot = scatter::ScatterPlot::new(label, x_data, y_data);
271                plot.validate()?;
272                plot.plot();
273            }
274            PlotType::Bar {
275                label,
276                values,
277                width,
278            } => {
279                let plot = bar::BarPlot::new(label, values).with_bar_size(width);
280                plot.validate()?;
281                plot.plot();
282            }
283            PlotType::Histogram {
284                label,
285                values,
286                bins,
287            } => {
288                let plot = histogram::HistogramPlot::new(label, values).with_bins(bins);
289                plot.validate()?;
290                plot.plot();
291            }
292            PlotType::Heatmap {
293                label,
294                values,
295                rows,
296                cols,
297            } => {
298                let plot = heatmap::HeatmapPlot::new(label, values, rows, cols);
299                plot.validate()?;
300                plot.plot();
301            }
302            PlotType::PieChart {
303                labels,
304                values,
305                center,
306                radius,
307            } => {
308                let plot = pie::PieChartPlot::new(labels, values, center.0, center.1, radius);
309                plot.validate()?;
310                plot.plot();
311            }
312        }
313        Ok(())
314    }
315}