1use super::{
4 Plot, PlotDataLayout, PlotDataOffset, PlotDataStride, PlotError, PlotItemStyle,
5 plot_spec_with_style, validate_data_lengths, with_plot_str_or_empty,
6};
7use crate::{ErrorBarsFlags, ItemFlags, sys};
8
9pub struct ErrorBarsPlot<'a> {
11 label: &'a str,
12 x_data: &'a [f64],
13 y_data: &'a [f64],
14 err_data: &'a [f64],
15 style: PlotItemStyle,
16 flags: ErrorBarsFlags,
17 item_flags: ItemFlags,
18 layout: PlotDataLayout,
19}
20
21impl<'a> super::PlotItemStyled for ErrorBarsPlot<'a> {
22 fn style_mut(&mut self) -> &mut PlotItemStyle {
23 &mut self.style
24 }
25}
26
27impl<'a> ErrorBarsPlot<'a> {
28 pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64], err_data: &'a [f64]) -> Self {
36 Self {
37 label,
38 x_data,
39 y_data,
40 err_data,
41 style: PlotItemStyle::default(),
42 flags: ErrorBarsFlags::NONE,
43 item_flags: ItemFlags::NONE,
44 layout: PlotDataLayout::DEFAULT,
45 }
46 }
47
48 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
50 self.flags = flags;
51 self
52 }
53
54 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
56 self.item_flags = flags;
57 self
58 }
59
60 pub fn with_data_layout(mut self, layout: PlotDataLayout) -> Self {
62 self.layout = layout;
63 self
64 }
65
66 pub fn with_offset(mut self, offset: PlotDataOffset) -> Self {
68 self.layout = self.layout.with_offset(offset);
69 self
70 }
71
72 pub fn with_stride(mut self, stride: PlotDataStride) -> Self {
74 self.layout = self.layout.with_stride(stride);
75 self
76 }
77
78 pub fn horizontal(mut self) -> Self {
80 self.flags |= ErrorBarsFlags::HORIZONTAL;
81 self
82 }
83
84 pub fn validate(&self) -> Result<(), PlotError> {
86 validate_data_lengths(self.x_data, self.y_data)?;
87 validate_data_lengths(self.x_data, self.err_data)?;
88
89 if self.err_data.iter().any(|&err| err < 0.0) {
91 return Err(PlotError::InvalidData(
92 "Error values cannot be negative".to_string(),
93 ));
94 }
95
96 Ok(())
97 }
98}
99
100impl<'a> Plot for ErrorBarsPlot<'a> {
101 fn plot(&self) {
102 if self.validate().is_err() {
103 return;
104 }
105 let Ok(count) = i32::try_from(self.x_data.len()) else {
106 return;
107 };
108 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
109 let spec = plot_spec_with_style(
110 self.style,
111 self.flags.bits() | self.item_flags.bits(),
112 self.layout,
113 );
114 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
115 label_ptr,
116 self.x_data.as_ptr(),
117 self.y_data.as_ptr(),
118 self.err_data.as_ptr(),
119 count,
120 spec,
121 );
122 })
123 }
124
125 fn label(&self) -> &str {
126 self.label
127 }
128}
129
130pub struct AsymmetricErrorBarsPlot<'a> {
132 label: &'a str,
133 x_data: &'a [f64],
134 y_data: &'a [f64],
135 err_neg: &'a [f64],
136 err_pos: &'a [f64],
137 style: PlotItemStyle,
138 flags: ErrorBarsFlags,
139 item_flags: ItemFlags,
140 layout: PlotDataLayout,
141}
142
143impl<'a> super::PlotItemStyled for AsymmetricErrorBarsPlot<'a> {
144 fn style_mut(&mut self) -> &mut PlotItemStyle {
145 &mut self.style
146 }
147}
148
149impl<'a> AsymmetricErrorBarsPlot<'a> {
150 pub fn new(
159 label: &'a str,
160 x_data: &'a [f64],
161 y_data: &'a [f64],
162 err_neg: &'a [f64],
163 err_pos: &'a [f64],
164 ) -> Self {
165 Self {
166 label,
167 x_data,
168 y_data,
169 err_neg,
170 err_pos,
171 style: PlotItemStyle::default(),
172 flags: ErrorBarsFlags::NONE,
173 item_flags: ItemFlags::NONE,
174 layout: PlotDataLayout::DEFAULT,
175 }
176 }
177
178 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
180 self.flags = flags;
181 self
182 }
183
184 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
186 self.item_flags = flags;
187 self
188 }
189
190 pub fn with_data_layout(mut self, layout: PlotDataLayout) -> Self {
192 self.layout = layout;
193 self
194 }
195
196 pub fn with_offset(mut self, offset: PlotDataOffset) -> Self {
198 self.layout = self.layout.with_offset(offset);
199 self
200 }
201
202 pub fn with_stride(mut self, stride: PlotDataStride) -> Self {
204 self.layout = self.layout.with_stride(stride);
205 self
206 }
207
208 pub fn horizontal(mut self) -> Self {
210 self.flags |= ErrorBarsFlags::HORIZONTAL;
211 self
212 }
213
214 pub fn validate(&self) -> Result<(), PlotError> {
216 validate_data_lengths(self.x_data, self.y_data)?;
217 validate_data_lengths(self.x_data, self.err_neg)?;
218 validate_data_lengths(self.x_data, self.err_pos)?;
219
220 if self.err_neg.iter().any(|&err| err < 0.0) || self.err_pos.iter().any(|&err| err < 0.0) {
222 return Err(PlotError::InvalidData(
223 "Error values cannot be negative".to_string(),
224 ));
225 }
226
227 Ok(())
228 }
229}
230
231impl<'a> Plot for AsymmetricErrorBarsPlot<'a> {
232 fn plot(&self) {
233 if self.validate().is_err() {
234 return;
235 }
236 let Ok(count) = i32::try_from(self.x_data.len()) else {
237 return;
238 };
239 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
240 let spec = plot_spec_with_style(
241 self.style,
242 self.flags.bits() | self.item_flags.bits(),
243 self.layout,
244 );
245 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrdoublePtr(
246 label_ptr,
247 self.x_data.as_ptr(),
248 self.y_data.as_ptr(),
249 self.err_neg.as_ptr(),
250 self.err_pos.as_ptr(),
251 count,
252 spec,
253 );
254 })
255 }
256
257 fn label(&self) -> &str {
258 self.label
259 }
260}
261
262pub struct SimpleErrorBarsPlot<'a> {
264 label: &'a str,
265 values: &'a [f64],
266 errors: &'a [f64],
267 style: PlotItemStyle,
268 flags: ErrorBarsFlags,
269 item_flags: ItemFlags,
270 x_scale: f64,
271 x_start: f64,
272}
273
274impl<'a> super::PlotItemStyled for SimpleErrorBarsPlot<'a> {
275 fn style_mut(&mut self) -> &mut PlotItemStyle {
276 &mut self.style
277 }
278}
279
280impl<'a> SimpleErrorBarsPlot<'a> {
281 pub fn new(label: &'a str, values: &'a [f64], errors: &'a [f64]) -> Self {
283 Self {
284 label,
285 values,
286 errors,
287 style: PlotItemStyle::default(),
288 flags: ErrorBarsFlags::NONE,
289 item_flags: ItemFlags::NONE,
290 x_scale: 1.0,
291 x_start: 0.0,
292 }
293 }
294
295 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
297 self.flags = flags;
298 self
299 }
300
301 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
303 self.item_flags = flags;
304 self
305 }
306
307 pub fn horizontal(mut self) -> Self {
309 self.flags |= ErrorBarsFlags::HORIZONTAL;
310 self
311 }
312
313 pub fn with_x_scale(mut self, scale: f64) -> Self {
315 self.x_scale = scale;
316 self
317 }
318
319 pub fn with_x_start(mut self, start: f64) -> Self {
321 self.x_start = start;
322 self
323 }
324
325 pub fn validate(&self) -> Result<(), PlotError> {
327 validate_data_lengths(self.values, self.errors)?;
328
329 if self.errors.iter().any(|&err| err < 0.0) {
330 return Err(PlotError::InvalidData(
331 "Error values cannot be negative".to_string(),
332 ));
333 }
334
335 Ok(())
336 }
337}
338
339impl<'a> Plot for SimpleErrorBarsPlot<'a> {
340 fn plot(&self) {
341 if self.validate().is_err() {
342 return;
343 }
344 let Ok(count) = i32::try_from(self.values.len()) else {
345 return;
346 };
347
348 let x_data: Vec<f64> = (0..self.values.len())
350 .map(|i| self.x_start + i as f64 * self.x_scale)
351 .collect();
352
353 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
354 let spec = plot_spec_with_style(
355 self.style,
356 self.flags.bits() | self.item_flags.bits(),
357 PlotDataLayout::DEFAULT,
358 );
359 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
360 label_ptr,
361 x_data.as_ptr(),
362 self.values.as_ptr(),
363 self.errors.as_ptr(),
364 count,
365 spec,
366 );
367 })
368 }
369
370 fn label(&self) -> &str {
371 self.label
372 }
373}
374
375impl<'ui> crate::PlotUi<'ui> {
377 pub fn error_bars_plot(
379 &self,
380 label: &str,
381 x_data: &[f64],
382 y_data: &[f64],
383 err_data: &[f64],
384 ) -> Result<(), PlotError> {
385 let plot = ErrorBarsPlot::new(label, x_data, y_data, err_data);
386 plot.validate()?;
387 self.bind();
388 plot.plot();
389 Ok(())
390 }
391
392 pub fn asymmetric_error_bars_plot(
394 &self,
395 label: &str,
396 x_data: &[f64],
397 y_data: &[f64],
398 err_neg: &[f64],
399 err_pos: &[f64],
400 ) -> Result<(), PlotError> {
401 let plot = AsymmetricErrorBarsPlot::new(label, x_data, y_data, err_neg, err_pos);
402 plot.validate()?;
403 self.bind();
404 plot.plot();
405 Ok(())
406 }
407
408 pub fn simple_error_bars_plot(
410 &self,
411 label: &str,
412 values: &[f64],
413 errors: &[f64],
414 ) -> Result<(), PlotError> {
415 let plot = SimpleErrorBarsPlot::new(label, values, errors);
416 plot.validate()?;
417 self.bind();
418 plot.plot();
419 Ok(())
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[test]
428 fn test_simple_error_bars_plot_flags() {
429 let values = [1.0, 2.0, 3.0, 4.0];
430 let errors = [0.1, 0.2, 0.3, 0.4];
431 let plot = SimpleErrorBarsPlot::new("test", &values, &errors)
432 .horizontal()
433 .with_item_flags(ItemFlags::NO_LEGEND);
434 assert_eq!(plot.label(), "test");
435 assert_eq!(plot.flags.bits(), ErrorBarsFlags::HORIZONTAL.bits());
436 assert_eq!(plot.item_flags, ItemFlags::NO_LEGEND);
437 }
438}