dear_implot/plots/
bar.rs

1//! Bar plot implementation
2
3use super::{Plot, PlotError, with_plot_str_or_empty};
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        let Ok(count) = i32::try_from(self.values.len()) else {
77            return;
78        };
79
80        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
81            sys::ImPlot_PlotBars_doublePtrInt(
82                label_ptr,
83                self.values.as_ptr(),
84                count,
85                self.bar_size,
86                self.shift,
87                self.flags.bits() as i32,
88                self.offset,
89                self.stride,
90            );
91        })
92    }
93
94    fn label(&self) -> &str {
95        self.label
96    }
97}
98
99/// Bar plot with explicit X positions
100pub struct PositionalBarPlot<'a> {
101    label: &'a str,
102    x_data: &'a [f64],
103    y_data: &'a [f64],
104    bar_size: f64,
105    flags: BarsFlags,
106}
107
108impl<'a> PositionalBarPlot<'a> {
109    /// Create a new positional bar plot with explicit X and Y data
110    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
111        Self {
112            label,
113            x_data,
114            y_data,
115            bar_size: 0.67,
116            flags: BarsFlags::NONE,
117        }
118    }
119
120    /// Set the bar width (in plot units)
121    pub fn with_bar_size(mut self, bar_size: f64) -> Self {
122        self.bar_size = bar_size;
123        self
124    }
125
126    /// Set bar flags for customization
127    pub fn with_flags(mut self, flags: BarsFlags) -> Self {
128        self.flags = flags;
129        self
130    }
131
132    /// Validate the plot data
133    pub fn validate(&self) -> Result<(), PlotError> {
134        super::validate_data_lengths(self.x_data, self.y_data)
135    }
136}
137
138impl<'a> Plot for PositionalBarPlot<'a> {
139    fn plot(&self) {
140        if self.validate().is_err() {
141            return; // Skip plotting if data is invalid
142        }
143        let Ok(count) = i32::try_from(self.y_data.len()) else {
144            return;
145        };
146
147        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
148            sys::ImPlot_PlotBars_doublePtrdoublePtr(
149                label_ptr,
150                self.x_data.as_ptr(),
151                self.y_data.as_ptr(),
152                count,
153                self.bar_size,
154                self.flags.bits() as i32,
155                0,
156                std::mem::size_of::<f64>() as i32,
157            );
158        })
159    }
160
161    fn label(&self) -> &str {
162        self.label
163    }
164}
165
166/// Convenience functions for quick bar plotting
167impl<'ui> crate::PlotUi<'ui> {
168    /// Plot a bar chart with values (X will be indices)
169    pub fn bar_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
170        let plot = BarPlot::new(label, values);
171        plot.validate()?;
172        plot.plot();
173        Ok(())
174    }
175
176    /// Plot a bar chart with custom bar width
177    pub fn bar_plot_with_width(
178        &self,
179        label: &str,
180        values: &[f64],
181        width: f64,
182    ) -> Result<(), PlotError> {
183        let plot = BarPlot::new(label, values).with_bar_size(width);
184        plot.validate()?;
185        plot.plot();
186        Ok(())
187    }
188
189    /// Plot a positional bar chart with explicit X and Y data
190    pub fn positional_bar_plot(
191        &self,
192        label: &str,
193        x_data: &[f64],
194        y_data: &[f64],
195    ) -> Result<(), PlotError> {
196        let plot = PositionalBarPlot::new(label, x_data, y_data);
197        plot.validate()?;
198        plot.plot();
199        Ok(())
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn test_bar_plot_creation() {
209        let values = [1.0, 2.0, 3.0, 4.0];
210        let plot = BarPlot::new("test", &values);
211        assert_eq!(plot.label(), "test");
212        assert!(plot.validate().is_ok());
213    }
214
215    #[test]
216    fn test_bar_plot_empty_data() {
217        let values: &[f64] = &[];
218        let plot = BarPlot::new("test", values);
219        assert!(plot.validate().is_err());
220    }
221
222    #[test]
223    fn test_positional_bar_plot() {
224        let x_data = [1.0, 2.0, 3.0, 4.0];
225        let y_data = [1.0, 4.0, 2.0, 3.0];
226
227        let plot = PositionalBarPlot::new("test", &x_data, &y_data);
228        assert_eq!(plot.label(), "test");
229        assert!(plot.validate().is_ok());
230    }
231
232    #[test]
233    fn test_positional_bar_plot_validation() {
234        let x_data = [1.0, 2.0, 3.0];
235        let y_data = [1.0, 4.0]; // Different length
236
237        let plot = PositionalBarPlot::new("test", &x_data, &y_data);
238        assert!(plot.validate().is_err());
239    }
240}