dear_implot/plots/
bar_groups.rs

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