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 let _guard = plot_ui.bind();
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
165impl<'a> PlotData for BarGroupsPlot<'a> {
166 fn label(&self) -> &str {
167 "BarGroups" }
169
170 fn data_len(&self) -> usize {
171 self.values.len()
172 }
173}
174
175pub struct BarGroupsPlotF32<'a> {
177 label_ids: Vec<&'a str>,
178 values: &'a [f32],
179 style: PlotItemStyle,
180 item_count: usize,
181 group_count: usize,
182 group_size: f64,
183 shift: f64,
184 flags: BarGroupsFlags,
185 item_flags: ItemFlags,
186}
187
188impl<'a> super::PlotItemStyled for BarGroupsPlotF32<'a> {
189 fn style_mut(&mut self) -> &mut PlotItemStyle {
190 &mut self.style
191 }
192}
193
194impl<'a> BarGroupsPlotF32<'a> {
195 pub fn new(
197 label_ids: Vec<&'a str>,
198 values: &'a [f32],
199 item_count: usize,
200 group_count: usize,
201 ) -> Self {
202 Self {
203 label_ids,
204 values,
205 style: PlotItemStyle::default(),
206 item_count,
207 group_count,
208 group_size: 0.67,
209 shift: 0.0,
210 flags: BarGroupsFlags::NONE,
211 item_flags: ItemFlags::NONE,
212 }
213 }
214
215 pub fn with_group_size(mut self, group_size: f64) -> Self {
217 self.group_size = group_size;
218 self
219 }
220
221 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
223 self.style = style;
224 self
225 }
226
227 pub fn with_shift(mut self, shift: f64) -> Self {
229 self.shift = shift;
230 self
231 }
232
233 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
235 self.flags = flags;
236 self
237 }
238
239 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
241 self.item_flags = flags;
242 self
243 }
244
245 pub fn horizontal(mut self) -> Self {
247 self.flags |= BarGroupsFlags::HORIZONTAL;
248 self
249 }
250
251 pub fn stacked(mut self) -> Self {
253 self.flags |= BarGroupsFlags::STACKED;
254 self
255 }
256
257 pub fn validate(&self) -> Result<(), PlotError> {
259 if self.label_ids.len() != self.item_count {
260 return Err(PlotError::InvalidData(format!(
261 "Label count ({}) must match item count ({})",
262 self.label_ids.len(),
263 self.item_count
264 )));
265 }
266
267 let expected_values = self
268 .item_count
269 .checked_mul(self.group_count)
270 .ok_or_else(|| {
271 PlotError::InvalidData("item_count * group_count overflowed usize".to_string())
272 })?;
273 if self.values.len() != expected_values {
274 return Err(PlotError::InvalidData(format!(
275 "Values length ({}) must equal item_count * group_count ({})",
276 self.values.len(),
277 expected_values
278 )));
279 }
280
281 if self.item_count == 0 || self.group_count == 0 {
282 return Err(PlotError::EmptyData);
283 }
284
285 let _ = i32::try_from(self.item_count).map_err(|_| {
286 PlotError::InvalidData("item_count exceeded ImPlot's i32 range".to_string())
287 })?;
288 let _ = i32::try_from(self.group_count).map_err(|_| {
289 PlotError::InvalidData("group_count exceeded ImPlot's i32 range".to_string())
290 })?;
291
292 Ok(())
293 }
294
295 pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
297 if self.validate().is_err() {
298 return;
299 }
300 let Ok(item_count) = i32::try_from(self.item_count) else {
301 return;
302 };
303 let Ok(group_count) = i32::try_from(self.group_count) else {
304 return;
305 };
306 let _guard = plot_ui.bind();
307 with_plot_str_slice(&self.label_ids, |label_ptrs| unsafe {
308 let spec = plot_spec_with_style(
309 self.style,
310 self.flags.bits() | self.item_flags.bits(),
311 PlotDataLayout::DEFAULT,
312 );
313 sys::ImPlot_PlotBarGroups_FloatPtr(
314 label_ptrs.as_ptr(),
315 self.values.as_ptr(),
316 item_count,
317 group_count,
318 self.group_size,
319 self.shift,
320 spec,
321 );
322 })
323 }
324}
325
326impl<'a> PlotData for BarGroupsPlotF32<'a> {
327 fn label(&self) -> &str {
328 "BarGroups" }
330
331 fn data_len(&self) -> usize {
332 self.values.len()
333 }
334}
335
336pub struct SimpleBarGroupsPlot<'a> {
338 labels: Vec<&'a str>,
339 data: Vec<Vec<f64>>,
340 style: PlotItemStyle,
341 group_size: f64,
342 flags: BarGroupsFlags,
343 item_flags: ItemFlags,
344}
345
346impl<'a> super::PlotItemStyled for SimpleBarGroupsPlot<'a> {
347 fn style_mut(&mut self) -> &mut PlotItemStyle {
348 &mut self.style
349 }
350}
351
352impl<'a> SimpleBarGroupsPlot<'a> {
353 pub fn new(labels: Vec<&'a str>, data: Vec<Vec<f64>>) -> Self {
359 Self {
360 labels,
361 data,
362 style: PlotItemStyle::default(),
363 group_size: 0.67,
364 flags: BarGroupsFlags::NONE,
365 item_flags: ItemFlags::NONE,
366 }
367 }
368
369 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
371 self.style = style;
372 self
373 }
374
375 pub fn with_group_size(mut self, group_size: f64) -> Self {
377 self.group_size = group_size;
378 self
379 }
380
381 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
383 self.flags = flags;
384 self
385 }
386
387 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
389 self.item_flags = flags;
390 self
391 }
392
393 pub fn horizontal(mut self) -> Self {
395 self.flags |= BarGroupsFlags::HORIZONTAL;
396 self
397 }
398
399 pub fn stacked(mut self) -> Self {
401 self.flags |= BarGroupsFlags::STACKED;
402 self
403 }
404
405 pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
407 if self.data.is_empty() || self.labels.is_empty() {
408 return;
409 }
410
411 let item_count = self.data.len();
412 let group_count = self.data[0].len();
413
414 let mut flattened_data = Vec::with_capacity(item_count * group_count);
416 for group_idx in 0..group_count {
417 for item_idx in 0..item_count {
418 if group_idx < self.data[item_idx].len() {
419 flattened_data.push(self.data[item_idx][group_idx]);
420 } else {
421 flattened_data.push(0.0); }
423 }
424 }
425
426 let plot = BarGroupsPlot::new(self.labels, &flattened_data, item_count, group_count)
427 .with_style(self.style)
428 .with_group_size(self.group_size)
429 .with_flags(self.flags)
430 .with_item_flags(self.item_flags);
431
432 plot.plot(plot_ui);
433 }
434}