dear_implot/plots/
bar_groups.rs1use super::{PlotData, PlotError, safe_cstring};
4use crate::BarGroupsFlags;
5use crate::sys;
6use std::ffi::CString;
7
8pub struct BarGroupsPlot<'a> {
10 label_ids: Vec<&'a str>,
11 values: &'a [f64],
12 item_count: usize,
13 group_count: usize,
14 group_size: f64,
15 shift: f64,
16 flags: BarGroupsFlags,
17}
18
19impl<'a> BarGroupsPlot<'a> {
20 pub fn new(
28 label_ids: Vec<&'a str>,
29 values: &'a [f64],
30 item_count: usize,
31 group_count: usize,
32 ) -> Self {
33 Self {
34 label_ids,
35 values,
36 item_count,
37 group_count,
38 group_size: 0.67,
39 shift: 0.0,
40 flags: BarGroupsFlags::NONE,
41 }
42 }
43
44 pub fn with_group_size(mut self, group_size: f64) -> Self {
46 self.group_size = group_size;
47 self
48 }
49
50 pub fn with_shift(mut self, shift: f64) -> Self {
52 self.shift = shift;
53 self
54 }
55
56 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
58 self.flags = flags;
59 self
60 }
61
62 pub fn horizontal(mut self) -> Self {
64 self.flags |= BarGroupsFlags::HORIZONTAL;
65 self
66 }
67
68 pub fn stacked(mut self) -> Self {
70 self.flags |= BarGroupsFlags::STACKED;
71 self
72 }
73
74 pub fn validate(&self) -> Result<(), PlotError> {
76 if self.label_ids.len() != self.item_count {
77 return Err(PlotError::InvalidData(format!(
78 "Label count ({}) must match item count ({})",
79 self.label_ids.len(),
80 self.item_count
81 )));
82 }
83
84 let expected_values = self.item_count * self.group_count;
85 if self.values.len() != expected_values {
86 return Err(PlotError::InvalidData(format!(
87 "Values length ({}) must equal item_count * group_count ({})",
88 self.values.len(),
89 expected_values
90 )));
91 }
92
93 if self.item_count == 0 || self.group_count == 0 {
94 return Err(PlotError::EmptyData);
95 }
96
97 Ok(())
98 }
99
100 pub fn plot(self) {
102 let label_cstrings: Vec<CString> = self
104 .label_ids
105 .iter()
106 .map(|&label| safe_cstring(label))
107 .collect();
108
109 let label_ptrs: Vec<*const i8> = label_cstrings.iter().map(|cstr| cstr.as_ptr()).collect();
110
111 unsafe {
112 sys::ImPlot_PlotBarGroups_doublePtr(
113 label_ptrs.as_ptr(),
114 self.values.as_ptr(),
115 self.item_count as i32,
116 self.group_count as i32,
117 self.group_size,
118 self.shift,
119 self.flags.bits() as i32,
120 );
121 }
122 }
123}
124
125impl<'a> PlotData for BarGroupsPlot<'a> {
126 fn label(&self) -> &str {
127 "BarGroups" }
129
130 fn data_len(&self) -> usize {
131 self.values.len()
132 }
133}
134
135pub struct BarGroupsPlotF32<'a> {
137 label_ids: Vec<&'a str>,
138 values: &'a [f32],
139 item_count: usize,
140 group_count: usize,
141 group_size: f64,
142 shift: f64,
143 flags: BarGroupsFlags,
144}
145
146impl<'a> BarGroupsPlotF32<'a> {
147 pub fn new(
149 label_ids: Vec<&'a str>,
150 values: &'a [f32],
151 item_count: usize,
152 group_count: usize,
153 ) -> Self {
154 Self {
155 label_ids,
156 values,
157 item_count,
158 group_count,
159 group_size: 0.67,
160 shift: 0.0,
161 flags: BarGroupsFlags::NONE,
162 }
163 }
164
165 pub fn with_group_size(mut self, group_size: f64) -> Self {
167 self.group_size = group_size;
168 self
169 }
170
171 pub fn with_shift(mut self, shift: f64) -> Self {
173 self.shift = shift;
174 self
175 }
176
177 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
179 self.flags = flags;
180 self
181 }
182
183 pub fn horizontal(mut self) -> Self {
185 self.flags |= BarGroupsFlags::HORIZONTAL;
186 self
187 }
188
189 pub fn stacked(mut self) -> Self {
191 self.flags |= BarGroupsFlags::STACKED;
192 self
193 }
194
195 pub fn validate(&self) -> Result<(), PlotError> {
197 if self.label_ids.len() != self.item_count {
198 return Err(PlotError::InvalidData(format!(
199 "Label count ({}) must match item count ({})",
200 self.label_ids.len(),
201 self.item_count
202 )));
203 }
204
205 let expected_values = self.item_count * self.group_count;
206 if self.values.len() != expected_values {
207 return Err(PlotError::InvalidData(format!(
208 "Values length ({}) must equal item_count * group_count ({})",
209 self.values.len(),
210 expected_values
211 )));
212 }
213
214 if self.item_count == 0 || self.group_count == 0 {
215 return Err(PlotError::EmptyData);
216 }
217
218 Ok(())
219 }
220
221 pub fn plot(self) {
223 let label_cstrings: Vec<CString> = self
225 .label_ids
226 .iter()
227 .map(|&label| safe_cstring(label))
228 .collect();
229
230 let label_ptrs: Vec<*const i8> = label_cstrings.iter().map(|cstr| cstr.as_ptr()).collect();
231
232 unsafe {
233 sys::ImPlot_PlotBarGroups_FloatPtr(
234 label_ptrs.as_ptr(),
235 self.values.as_ptr(),
236 self.item_count as i32,
237 self.group_count as i32,
238 self.group_size,
239 self.shift,
240 self.flags.bits() as i32,
241 );
242 }
243 }
244}
245
246impl<'a> PlotData for BarGroupsPlotF32<'a> {
247 fn label(&self) -> &str {
248 "BarGroups" }
250
251 fn data_len(&self) -> usize {
252 self.values.len()
253 }
254}
255
256pub struct SimpleBarGroupsPlot<'a> {
258 labels: Vec<&'a str>,
259 data: Vec<Vec<f64>>,
260 group_size: f64,
261 flags: BarGroupsFlags,
262}
263
264impl<'a> SimpleBarGroupsPlot<'a> {
265 pub fn new(labels: Vec<&'a str>, data: Vec<Vec<f64>>) -> Self {
271 Self {
272 labels,
273 data,
274 group_size: 0.67,
275 flags: BarGroupsFlags::NONE,
276 }
277 }
278
279 pub fn with_group_size(mut self, group_size: f64) -> Self {
281 self.group_size = group_size;
282 self
283 }
284
285 pub fn with_flags(mut self, flags: BarGroupsFlags) -> Self {
287 self.flags = flags;
288 self
289 }
290
291 pub fn horizontal(mut self) -> Self {
293 self.flags |= BarGroupsFlags::HORIZONTAL;
294 self
295 }
296
297 pub fn stacked(mut self) -> Self {
299 self.flags |= BarGroupsFlags::STACKED;
300 self
301 }
302
303 pub fn plot(self) {
305 if self.data.is_empty() || self.labels.is_empty() {
306 return;
307 }
308
309 let item_count = self.data.len();
310 let group_count = self.data[0].len();
311
312 let mut flattened_data = Vec::with_capacity(item_count * group_count);
314 for group_idx in 0..group_count {
315 for item_idx in 0..item_count {
316 if group_idx < self.data[item_idx].len() {
317 flattened_data.push(self.data[item_idx][group_idx]);
318 } else {
319 flattened_data.push(0.0); }
321 }
322 }
323
324 let plot = BarGroupsPlot::new(self.labels, &flattened_data, item_count, group_count)
325 .with_group_size(self.group_size)
326 .with_flags(self.flags);
327
328 plot.plot();
329 }
330}