dear_implot/plots/
bar.rs

1//! Bar plot implementation
2
3use super::{Plot, PlotError, safe_cstring};
4use crate::{BarsFlags, sys};
5
6/// Builder for bar plots with customization options
7pub struct BarPlot<'a> {
8    label: &'a str,
9    values: &'a [f64],
10    bar_size: f64,
11    shift: f64,
12    flags: BarsFlags,
13    offset: i32,
14    stride: i32,
15}
16
17impl<'a> BarPlot<'a> {
18    /// Create a new bar plot with the given label and values
19    pub fn new(label: &'a str, values: &'a [f64]) -> Self {
20        Self {
21            label,
22            values,
23            bar_size: 0.67, // Default bar width
24            shift: 0.0,
25            flags: BarsFlags::NONE,
26            offset: 0,
27            stride: std::mem::size_of::<f64>() as i32,
28        }
29    }
30
31    /// Set the bar width (in plot units)
32    pub fn with_bar_size(mut self, bar_size: f64) -> Self {
33        self.bar_size = bar_size;
34        self
35    }
36
37    /// Set the bar shift (in plot units)
38    pub fn with_shift(mut self, shift: f64) -> Self {
39        self.shift = shift;
40        self
41    }
42
43    /// Set bar flags for customization
44    pub fn with_flags(mut self, flags: BarsFlags) -> Self {
45        self.flags = flags;
46        self
47    }
48
49    /// Set data offset for partial plotting
50    pub fn with_offset(mut self, offset: i32) -> Self {
51        self.offset = offset;
52        self
53    }
54
55    /// Set data stride for non-contiguous data
56    pub fn with_stride(mut self, stride: i32) -> Self {
57        self.stride = stride;
58        self
59    }
60
61    /// Validate the plot data
62    pub fn validate(&self) -> Result<(), PlotError> {
63        if self.values.is_empty() {
64            Err(PlotError::EmptyData)
65        } else {
66            Ok(())
67        }
68    }
69}
70
71impl<'a> Plot for BarPlot<'a> {
72    fn plot(&self) {
73        if self.validate().is_err() {
74            return; // Skip plotting if data is invalid
75        }
76
77        let label_cstr = safe_cstring(self.label);
78
79        unsafe {
80            sys::ImPlot_PlotBars_doublePtrInt(
81                label_cstr.as_ptr(),
82                self.values.as_ptr(),
83                self.values.len() as i32,
84                self.bar_size,
85                self.shift,
86                self.flags.bits() as i32,
87                self.offset,
88                self.stride,
89            );
90        }
91    }
92
93    fn label(&self) -> &str {
94        self.label
95    }
96}
97
98/// Bar plot with explicit X positions
99pub struct PositionalBarPlot<'a> {
100    label: &'a str,
101    x_data: &'a [f64],
102    y_data: &'a [f64],
103    bar_size: f64,
104    flags: BarsFlags,
105}
106
107impl<'a> PositionalBarPlot<'a> {
108    /// Create a new positional bar plot with explicit X and Y data
109    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
110        Self {
111            label,
112            x_data,
113            y_data,
114            bar_size: 0.67,
115            flags: BarsFlags::NONE,
116        }
117    }
118
119    /// Set the bar width (in plot units)
120    pub fn with_bar_size(mut self, bar_size: f64) -> Self {
121        self.bar_size = bar_size;
122        self
123    }
124
125    /// Set bar flags for customization
126    pub fn with_flags(mut self, flags: BarsFlags) -> Self {
127        self.flags = flags;
128        self
129    }
130
131    /// Validate the plot data
132    pub fn validate(&self) -> Result<(), PlotError> {
133        super::validate_data_lengths(self.x_data, self.y_data)
134    }
135}
136
137impl<'a> Plot for PositionalBarPlot<'a> {
138    fn plot(&self) {
139        if self.validate().is_err() {
140            return; // Skip plotting if data is invalid
141        }
142
143        let label_cstr = safe_cstring(self.label);
144
145        unsafe {
146            sys::ImPlot_PlotBars_doublePtrdoublePtr(
147                label_cstr.as_ptr(),
148                self.x_data.as_ptr(),
149                self.y_data.as_ptr(),
150                self.y_data.len() as i32,
151                self.bar_size,
152                self.flags.bits() as i32,
153                0,
154                std::mem::size_of::<f64>() as i32,
155            );
156        }
157    }
158
159    fn label(&self) -> &str {
160        self.label
161    }
162}
163
164/// Convenience functions for quick bar plotting
165impl<'ui> crate::PlotUi<'ui> {
166    /// Plot a bar chart with values (X will be indices)
167    pub fn bar_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
168        let plot = BarPlot::new(label, values);
169        plot.validate()?;
170        plot.plot();
171        Ok(())
172    }
173
174    /// Plot a bar chart with custom bar width
175    pub fn bar_plot_with_width(
176        &self,
177        label: &str,
178        values: &[f64],
179        width: f64,
180    ) -> Result<(), PlotError> {
181        let plot = BarPlot::new(label, values).with_bar_size(width);
182        plot.validate()?;
183        plot.plot();
184        Ok(())
185    }
186
187    /// Plot a positional bar chart with explicit X and Y data
188    pub fn positional_bar_plot(
189        &self,
190        label: &str,
191        x_data: &[f64],
192        y_data: &[f64],
193    ) -> Result<(), PlotError> {
194        let plot = PositionalBarPlot::new(label, x_data, y_data);
195        plot.validate()?;
196        plot.plot();
197        Ok(())
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_bar_plot_creation() {
207        let values = [1.0, 2.0, 3.0, 4.0];
208        let plot = BarPlot::new("test", &values);
209        assert_eq!(plot.label(), "test");
210        assert!(plot.validate().is_ok());
211    }
212
213    #[test]
214    fn test_bar_plot_empty_data() {
215        let values: &[f64] = &[];
216        let plot = BarPlot::new("test", values);
217        assert!(plot.validate().is_err());
218    }
219
220    #[test]
221    fn test_positional_bar_plot() {
222        let x_data = [1.0, 2.0, 3.0, 4.0];
223        let y_data = [1.0, 4.0, 2.0, 3.0];
224
225        let plot = PositionalBarPlot::new("test", &x_data, &y_data);
226        assert_eq!(plot.label(), "test");
227        assert!(plot.validate().is_ok());
228    }
229
230    #[test]
231    fn test_positional_bar_plot_validation() {
232        let x_data = [1.0, 2.0, 3.0];
233        let y_data = [1.0, 4.0]; // Different length
234
235        let plot = PositionalBarPlot::new("test", &x_data, &y_data);
236        assert!(plot.validate().is_err());
237    }
238}