Skip to main content

dear_implot/plots/
bar_groups.rs

1//! Bar groups plot implementation
2
3use super::{PlotData, PlotError, PlotItemStyle, plot_spec_with_style, with_plot_str_slice};
4use crate::{BarGroupsFlags, ItemFlags, sys};
5
6/// Builder for bar groups plots with extensive customization options
7pub struct BarGroupsPlot<'a> {
8    label_ids: Vec<&'a str>,
9    values: &'a [f64],
10    style: PlotItemStyle,
11    item_count: usize,
12    group_count: usize,
13    group_size: f64,
14    shift: f64,
15    flags: BarGroupsFlags,
16    item_flags: ItemFlags,
17}
18
19impl<'a> super::PlotItemStyled for BarGroupsPlot<'a> {
20    fn style_mut(&mut self) -> &mut PlotItemStyle {
21        &mut self.style
22    }
23}
24
25impl<'a> BarGroupsPlot<'a> {
26    /// Create a new bar groups plot
27    ///
28    /// # Arguments
29    /// * `label_ids` - Labels for each item in the group
30    /// * `values` - Values in row-major order (item_count rows, group_count cols)
31    /// * `item_count` - Number of items (series) in each group
32    /// * `group_count` - Number of groups
33    pub fn new(
34        label_ids: Vec<&'a str>,
35        values: &'a [f64],
36        item_count: usize,
37        group_count: usize,
38    ) -> Self {
39        Self {
40            label_ids,
41            values,
42            style: PlotItemStyle::default(),
43            item_count,
44            group_count,
45            group_size: 0.67,
46            shift: 0.0,
47            flags: BarGroupsFlags::NONE,
48            item_flags: ItemFlags::NONE,
49        }
50    }
51
52    /// Set the group size (width of each group)
53    pub fn with_group_size(mut self, group_size: f64) -> Self {
54        self.group_size = group_size;
55        self
56    }
57
58    /// Set ImPlotSpec-backed style overrides for this bar groups plot.
59    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
60        self.style = style;
61        self
62    }
63
64    /// Set the shift (horizontal offset)
65    pub fn with_shift(mut self, shift: f64) -> Self {
66        self.shift = shift;
67        self
68    }
69
70    /// Set bar groups flags for customization
71    pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
72        self.flags = flags;
73        self
74    }
75
76    /// Set common item flags for this plot item (applies to all plot types)
77    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
78        self.item_flags = flags;
79        self
80    }
81
82    /// Make bars horizontal instead of vertical
83    pub fn horizontal(mut self) -> Self {
84        self.flags |= BarGroupsFlags::HORIZONTAL;
85        self
86    }
87
88    /// Stack bars instead of grouping them side by side
89    pub fn stacked(mut self) -> Self {
90        self.flags |= BarGroupsFlags::STACKED;
91        self
92    }
93
94    /// Validate the plot data
95    pub fn validate(&self) -> Result<(), PlotError> {
96        if self.label_ids.len() != self.item_count {
97            return Err(PlotError::InvalidData(format!(
98                "Label count ({}) must match item count ({})",
99                self.label_ids.len(),
100                self.item_count
101            )));
102        }
103
104        let expected_values = self.item_count * self.group_count;
105        if self.values.len() != expected_values {
106            return Err(PlotError::InvalidData(format!(
107                "Values length ({}) must equal item_count * group_count ({})",
108                self.values.len(),
109                expected_values
110            )));
111        }
112
113        if self.item_count == 0 || self.group_count == 0 {
114            return Err(PlotError::EmptyData);
115        }
116
117        Ok(())
118    }
119
120    /// Plot the bar groups
121    pub fn plot(self) {
122        with_plot_str_slice(&self.label_ids, |label_ptrs| unsafe {
123            let spec = plot_spec_with_style(
124                self.style,
125                self.flags.bits() | self.item_flags.bits(),
126                0,
127                crate::IMPLOT_AUTO,
128            );
129            sys::ImPlot_PlotBarGroups_doublePtr(
130                label_ptrs.as_ptr(),
131                self.values.as_ptr(),
132                self.item_count as i32,
133                self.group_count as i32,
134                self.group_size,
135                self.shift,
136                spec,
137            );
138        })
139    }
140}
141
142impl<'a> PlotData for BarGroupsPlot<'a> {
143    fn label(&self) -> &str {
144        "BarGroups" // Generic label for groups
145    }
146
147    fn data_len(&self) -> usize {
148        self.values.len()
149    }
150}
151
152/// Bar groups plot for f32 data
153pub struct BarGroupsPlotF32<'a> {
154    label_ids: Vec<&'a str>,
155    values: &'a [f32],
156    style: PlotItemStyle,
157    item_count: usize,
158    group_count: usize,
159    group_size: f64,
160    shift: f64,
161    flags: BarGroupsFlags,
162    item_flags: ItemFlags,
163}
164
165impl<'a> super::PlotItemStyled for BarGroupsPlotF32<'a> {
166    fn style_mut(&mut self) -> &mut PlotItemStyle {
167        &mut self.style
168    }
169}
170
171impl<'a> BarGroupsPlotF32<'a> {
172    /// Create a new bar groups plot with f32 data
173    pub fn new(
174        label_ids: Vec<&'a str>,
175        values: &'a [f32],
176        item_count: usize,
177        group_count: usize,
178    ) -> Self {
179        Self {
180            label_ids,
181            values,
182            style: PlotItemStyle::default(),
183            item_count,
184            group_count,
185            group_size: 0.67,
186            shift: 0.0,
187            flags: BarGroupsFlags::NONE,
188            item_flags: ItemFlags::NONE,
189        }
190    }
191
192    /// Set the group size
193    pub fn with_group_size(mut self, group_size: f64) -> Self {
194        self.group_size = group_size;
195        self
196    }
197
198    /// Set ImPlotSpec-backed style overrides for this bar groups plot.
199    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
200        self.style = style;
201        self
202    }
203
204    /// Set the shift
205    pub fn with_shift(mut self, shift: f64) -> Self {
206        self.shift = shift;
207        self
208    }
209
210    /// Set flags
211    pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
212        self.flags = flags;
213        self
214    }
215
216    /// Set common item flags for this plot item (applies to all plot types)
217    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
218        self.item_flags = flags;
219        self
220    }
221
222    /// Make bars horizontal
223    pub fn horizontal(mut self) -> Self {
224        self.flags |= BarGroupsFlags::HORIZONTAL;
225        self
226    }
227
228    /// Stack bars
229    pub fn stacked(mut self) -> Self {
230        self.flags |= BarGroupsFlags::STACKED;
231        self
232    }
233
234    /// Validate the plot data
235    pub fn validate(&self) -> Result<(), PlotError> {
236        if self.label_ids.len() != self.item_count {
237            return Err(PlotError::InvalidData(format!(
238                "Label count ({}) must match item count ({})",
239                self.label_ids.len(),
240                self.item_count
241            )));
242        }
243
244        let expected_values = self.item_count * self.group_count;
245        if self.values.len() != expected_values {
246            return Err(PlotError::InvalidData(format!(
247                "Values length ({}) must equal item_count * group_count ({})",
248                self.values.len(),
249                expected_values
250            )));
251        }
252
253        if self.item_count == 0 || self.group_count == 0 {
254            return Err(PlotError::EmptyData);
255        }
256
257        Ok(())
258    }
259
260    /// Plot the bar groups
261    pub fn plot(self) {
262        with_plot_str_slice(&self.label_ids, |label_ptrs| unsafe {
263            let spec = plot_spec_with_style(
264                self.style,
265                self.flags.bits() | self.item_flags.bits(),
266                0,
267                crate::IMPLOT_AUTO,
268            );
269            sys::ImPlot_PlotBarGroups_FloatPtr(
270                label_ptrs.as_ptr(),
271                self.values.as_ptr(),
272                self.item_count as i32,
273                self.group_count as i32,
274                self.group_size,
275                self.shift,
276                spec,
277            );
278        })
279    }
280}
281
282impl<'a> PlotData for BarGroupsPlotF32<'a> {
283    fn label(&self) -> &str {
284        "BarGroups" // Generic label for groups
285    }
286
287    fn data_len(&self) -> usize {
288        self.values.len()
289    }
290}
291
292/// Simple bar groups plot with automatic layout
293pub struct SimpleBarGroupsPlot<'a> {
294    labels: Vec<&'a str>,
295    data: Vec<Vec<f64>>,
296    style: PlotItemStyle,
297    group_size: f64,
298    flags: BarGroupsFlags,
299    item_flags: ItemFlags,
300}
301
302impl<'a> super::PlotItemStyled for SimpleBarGroupsPlot<'a> {
303    fn style_mut(&mut self) -> &mut PlotItemStyle {
304        &mut self.style
305    }
306}
307
308impl<'a> SimpleBarGroupsPlot<'a> {
309    /// Create a simple bar groups plot from a 2D data structure
310    ///
311    /// # Arguments
312    /// * `labels` - Labels for each series
313    /// * `data` - Vector of vectors, where each inner vector is a series
314    pub fn new(labels: Vec<&'a str>, data: Vec<Vec<f64>>) -> Self {
315        Self {
316            labels,
317            data,
318            style: PlotItemStyle::default(),
319            group_size: 0.67,
320            flags: BarGroupsFlags::NONE,
321            item_flags: ItemFlags::NONE,
322        }
323    }
324
325    /// Set ImPlotSpec-backed style overrides for this bar groups plot.
326    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
327        self.style = style;
328        self
329    }
330
331    /// Set the group size
332    pub fn with_group_size(mut self, group_size: f64) -> Self {
333        self.group_size = group_size;
334        self
335    }
336
337    /// Set flags
338    pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
339        self.flags = flags;
340        self
341    }
342
343    /// Set common item flags for this plot item (applies to all plot types)
344    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
345        self.item_flags = flags;
346        self
347    }
348
349    /// Make bars horizontal
350    pub fn horizontal(mut self) -> Self {
351        self.flags |= BarGroupsFlags::HORIZONTAL;
352        self
353    }
354
355    /// Stack bars
356    pub fn stacked(mut self) -> Self {
357        self.flags |= BarGroupsFlags::STACKED;
358        self
359    }
360
361    /// Plot the bar groups
362    pub fn plot(self) {
363        if self.data.is_empty() || self.labels.is_empty() {
364            return;
365        }
366
367        let item_count = self.data.len();
368        let group_count = self.data[0].len();
369
370        // Flatten data into row-major order
371        let mut flattened_data = Vec::with_capacity(item_count * group_count);
372        for group_idx in 0..group_count {
373            for item_idx in 0..item_count {
374                if group_idx < self.data[item_idx].len() {
375                    flattened_data.push(self.data[item_idx][group_idx]);
376                } else {
377                    flattened_data.push(0.0); // Fill missing data with zeros
378                }
379            }
380        }
381
382        let plot = BarGroupsPlot::new(self.labels, &flattened_data, item_count, group_count)
383            .with_style(self.style)
384            .with_group_size(self.group_size)
385            .with_flags(self.flags)
386            .with_item_flags(self.item_flags);
387
388        plot.plot();
389    }
390}