Skip to main content

dear_implot/plots/
bar_groups.rs

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