1use super::{
4 PlotData, PlotDataLayout, PlotError, PlotItemStyle, plot_spec_with_style, with_plot_str_slice,
5};
6use crate::{BarGroupsFlags, ItemFlags, sys};
7
8pub struct BarGroupsPlot<'a> {
10 label_ids: Vec<&'a str>,
11 values: &'a [f64],
12 style: PlotItemStyle,
13 item_count: usize,
14 group_count: usize,
15 group_size: f64,
16 shift: f64,
17 flags: BarGroupsFlags,
18 item_flags: ItemFlags,
19}
20
21impl<'a> super::PlotItemStyled for BarGroupsPlot<'a> {
22 fn style_mut(&mut self) -> &mut PlotItemStyle {
23 &mut self.style
24 }
25}
26
27impl<'a> BarGroupsPlot<'a> {
28 pub fn new(
36 label_ids: Vec<&'a str>,
37 values: &'a [f64],
38 item_count: usize,
39 group_count: usize,
40 ) -> Self {
41 Self {
42 label_ids,
43 values,
44 style: PlotItemStyle::default(),
45 item_count,
46 group_count,
47 group_size: 0.67,
48 shift: 0.0,
49 flags: BarGroupsFlags::NONE,
50 item_flags: ItemFlags::NONE,
51 }
52 }
53
54 pub fn with_group_size(mut self, group_size: f64) -> Self {
56 self.group_size = group_size;
57 self
58 }
59
60 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
62 self.style = style;
63 self
64 }
65
66 pub fn with_shift(mut self, shift: f64) -> Self {
68 self.shift = shift;
69 self
70 }
71
72 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
74 self.flags = flags;
75 self
76 }
77
78 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
80 self.item_flags = flags;
81 self
82 }
83
84 pub fn horizontal(mut self) -> Self {
86 self.flags |= BarGroupsFlags::HORIZONTAL;
87 self
88 }
89
90 pub fn stacked(mut self) -> Self {
92 self.flags |= BarGroupsFlags::STACKED;
93 self
94 }
95
96 pub fn validate(&self) -> Result<(), PlotError> {
98 if self.label_ids.len() != self.item_count {
99 return Err(PlotError::InvalidData(format!(
100 "Label count ({}) must match item count ({})",
101 self.label_ids.len(),
102 self.item_count
103 )));
104 }
105
106 let expected_values = self
107 .item_count
108 .checked_mul(self.group_count)
109 .ok_or_else(|| {
110 PlotError::InvalidData("item_count * group_count overflowed usize".to_string())
111 })?;
112 if self.values.len() != expected_values {
113 return Err(PlotError::InvalidData(format!(
114 "Values length ({}) must equal item_count * group_count ({})",
115 self.values.len(),
116 expected_values
117 )));
118 }
119
120 if self.item_count == 0 || self.group_count == 0 {
121 return Err(PlotError::EmptyData);
122 }
123
124 let _ = i32::try_from(self.item_count).map_err(|_| {
125 PlotError::InvalidData("item_count exceeded ImPlot's i32 range".to_string())
126 })?;
127 let _ = i32::try_from(self.group_count).map_err(|_| {
128 PlotError::InvalidData("group_count exceeded ImPlot's i32 range".to_string())
129 })?;
130
131 Ok(())
132 }
133
134 pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
136 if self.validate().is_err() {
137 return;
138 }
139 let Ok(item_count) = i32::try_from(self.item_count) else {
140 return;
141 };
142 let Ok(group_count) = i32::try_from(self.group_count) else {
143 return;
144 };
145 plot_ui.with_bound_context(|| {
146 with_plot_str_slice(&self.label_ids, |label_ptrs| unsafe {
147 let spec = plot_spec_with_style(
148 self.style,
149 self.flags.bits() | self.item_flags.bits(),
150 PlotDataLayout::DEFAULT,
151 );
152 sys::ImPlot_PlotBarGroups_doublePtr(
153 label_ptrs.as_ptr(),
154 self.values.as_ptr(),
155 item_count,
156 group_count,
157 self.group_size,
158 self.shift,
159 spec,
160 );
161 })
162 })
163 }
164}
165
166impl<'a> PlotData for BarGroupsPlot<'a> {
167 fn label(&self) -> &str {
168 "BarGroups" }
170
171 fn data_len(&self) -> usize {
172 self.values.len()
173 }
174}
175
176pub struct BarGroupsPlotF32<'a> {
178 label_ids: Vec<&'a str>,
179 values: &'a [f32],
180 style: PlotItemStyle,
181 item_count: usize,
182 group_count: usize,
183 group_size: f64,
184 shift: f64,
185 flags: BarGroupsFlags,
186 item_flags: ItemFlags,
187}
188
189impl<'a> super::PlotItemStyled for BarGroupsPlotF32<'a> {
190 fn style_mut(&mut self) -> &mut PlotItemStyle {
191 &mut self.style
192 }
193}
194
195impl<'a> BarGroupsPlotF32<'a> {
196 pub fn new(
198 label_ids: Vec<&'a str>,
199 values: &'a [f32],
200 item_count: usize,
201 group_count: usize,
202 ) -> Self {
203 Self {
204 label_ids,
205 values,
206 style: PlotItemStyle::default(),
207 item_count,
208 group_count,
209 group_size: 0.67,
210 shift: 0.0,
211 flags: BarGroupsFlags::NONE,
212 item_flags: ItemFlags::NONE,
213 }
214 }
215
216 pub fn with_group_size(mut self, group_size: f64) -> Self {
218 self.group_size = group_size;
219 self
220 }
221
222 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
224 self.style = style;
225 self
226 }
227
228 pub fn with_shift(mut self, shift: f64) -> Self {
230 self.shift = shift;
231 self
232 }
233
234 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
236 self.flags = flags;
237 self
238 }
239
240 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
242 self.item_flags = flags;
243 self
244 }
245
246 pub fn horizontal(mut self) -> Self {
248 self.flags |= BarGroupsFlags::HORIZONTAL;
249 self
250 }
251
252 pub fn stacked(mut self) -> Self {
254 self.flags |= BarGroupsFlags::STACKED;
255 self
256 }
257
258 pub fn validate(&self) -> Result<(), PlotError> {
260 if self.label_ids.len() != self.item_count {
261 return Err(PlotError::InvalidData(format!(
262 "Label count ({}) must match item count ({})",
263 self.label_ids.len(),
264 self.item_count
265 )));
266 }
267
268 let expected_values = self
269 .item_count
270 .checked_mul(self.group_count)
271 .ok_or_else(|| {
272 PlotError::InvalidData("item_count * group_count overflowed usize".to_string())
273 })?;
274 if self.values.len() != expected_values {
275 return Err(PlotError::InvalidData(format!(
276 "Values length ({}) must equal item_count * group_count ({})",
277 self.values.len(),
278 expected_values
279 )));
280 }
281
282 if self.item_count == 0 || self.group_count == 0 {
283 return Err(PlotError::EmptyData);
284 }
285
286 let _ = i32::try_from(self.item_count).map_err(|_| {
287 PlotError::InvalidData("item_count exceeded ImPlot's i32 range".to_string())
288 })?;
289 let _ = i32::try_from(self.group_count).map_err(|_| {
290 PlotError::InvalidData("group_count exceeded ImPlot's i32 range".to_string())
291 })?;
292
293 Ok(())
294 }
295
296 pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
298 if self.validate().is_err() {
299 return;
300 }
301 let Ok(item_count) = i32::try_from(self.item_count) else {
302 return;
303 };
304 let Ok(group_count) = i32::try_from(self.group_count) else {
305 return;
306 };
307 plot_ui.with_bound_context(|| {
308 with_plot_str_slice(&self.label_ids, |label_ptrs| unsafe {
309 let spec = plot_spec_with_style(
310 self.style,
311 self.flags.bits() | self.item_flags.bits(),
312 PlotDataLayout::DEFAULT,
313 );
314 sys::ImPlot_PlotBarGroups_FloatPtr(
315 label_ptrs.as_ptr(),
316 self.values.as_ptr(),
317 item_count,
318 group_count,
319 self.group_size,
320 self.shift,
321 spec,
322 );
323 })
324 })
325 }
326}
327
328impl<'a> PlotData for BarGroupsPlotF32<'a> {
329 fn label(&self) -> &str {
330 "BarGroups" }
332
333 fn data_len(&self) -> usize {
334 self.values.len()
335 }
336}
337
338pub struct SimpleBarGroupsPlot<'a> {
340 labels: Vec<&'a str>,
341 data: Vec<Vec<f64>>,
342 style: PlotItemStyle,
343 group_size: f64,
344 flags: BarGroupsFlags,
345 item_flags: ItemFlags,
346}
347
348impl<'a> super::PlotItemStyled for SimpleBarGroupsPlot<'a> {
349 fn style_mut(&mut self) -> &mut PlotItemStyle {
350 &mut self.style
351 }
352}
353
354impl<'a> SimpleBarGroupsPlot<'a> {
355 pub fn new(labels: Vec<&'a str>, data: Vec<Vec<f64>>) -> Self {
361 Self {
362 labels,
363 data,
364 style: PlotItemStyle::default(),
365 group_size: 0.67,
366 flags: BarGroupsFlags::NONE,
367 item_flags: ItemFlags::NONE,
368 }
369 }
370
371 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
373 self.style = style;
374 self
375 }
376
377 pub fn with_group_size(mut self, group_size: f64) -> Self {
379 self.group_size = group_size;
380 self
381 }
382
383 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
385 self.flags = flags;
386 self
387 }
388
389 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
391 self.item_flags = flags;
392 self
393 }
394
395 pub fn horizontal(mut self) -> Self {
397 self.flags |= BarGroupsFlags::HORIZONTAL;
398 self
399 }
400
401 pub fn stacked(mut self) -> Self {
403 self.flags |= BarGroupsFlags::STACKED;
404 self
405 }
406
407 pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
409 if self.data.is_empty() || self.labels.is_empty() {
410 return;
411 }
412
413 let item_count = self.data.len();
414 let group_count = self.data[0].len();
415
416 let mut flattened_data = Vec::with_capacity(item_count * group_count);
418 for group_idx in 0..group_count {
419 for item_idx in 0..item_count {
420 if group_idx < self.data[item_idx].len() {
421 flattened_data.push(self.data[item_idx][group_idx]);
422 } else {
423 flattened_data.push(0.0); }
425 }
426 }
427
428 let plot = BarGroupsPlot::new(self.labels, &flattened_data, item_count, group_count)
429 .with_style(self.style)
430 .with_group_size(self.group_size)
431 .with_flags(self.flags)
432 .with_item_flags(self.item_flags);
433
434 plot.plot(plot_ui);
435 }
436}