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, plot_ui: &crate::PlotUi<'_>) {
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 let _guard = plot_ui.bind();
109 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
110 let spec = plot_spec_with_style(
111 self.style,
112 self.flags.bits() | self.item_flags.bits(),
113 self.layout,
114 );
115 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
116 label_ptr,
117 self.x_data.as_ptr(),
118 self.y_data.as_ptr(),
119 self.err_data.as_ptr(),
120 count,
121 spec,
122 );
123 })
124 }
125
126 fn label(&self) -> &str {
127 self.label
128 }
129}
130
131pub struct AsymmetricErrorBarsPlot<'a> {
133 label: &'a str,
134 x_data: &'a [f64],
135 y_data: &'a [f64],
136 err_neg: &'a [f64],
137 err_pos: &'a [f64],
138 style: PlotItemStyle,
139 flags: ErrorBarsFlags,
140 item_flags: ItemFlags,
141 layout: PlotDataLayout,
142}
143
144impl<'a> super::PlotItemStyled for AsymmetricErrorBarsPlot<'a> {
145 fn style_mut(&mut self) -> &mut PlotItemStyle {
146 &mut self.style
147 }
148}
149
150impl<'a> AsymmetricErrorBarsPlot<'a> {
151 pub fn new(
160 label: &'a str,
161 x_data: &'a [f64],
162 y_data: &'a [f64],
163 err_neg: &'a [f64],
164 err_pos: &'a [f64],
165 ) -> Self {
166 Self {
167 label,
168 x_data,
169 y_data,
170 err_neg,
171 err_pos,
172 style: PlotItemStyle::default(),
173 flags: ErrorBarsFlags::NONE,
174 item_flags: ItemFlags::NONE,
175 layout: PlotDataLayout::DEFAULT,
176 }
177 }
178
179 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
181 self.flags = flags;
182 self
183 }
184
185 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
187 self.item_flags = flags;
188 self
189 }
190
191 pub fn with_data_layout(mut self, layout: PlotDataLayout) -> Self {
193 self.layout = layout;
194 self
195 }
196
197 pub fn with_offset(mut self, offset: PlotDataOffset) -> Self {
199 self.layout = self.layout.with_offset(offset);
200 self
201 }
202
203 pub fn with_stride(mut self, stride: PlotDataStride) -> Self {
205 self.layout = self.layout.with_stride(stride);
206 self
207 }
208
209 pub fn horizontal(mut self) -> Self {
211 self.flags |= ErrorBarsFlags::HORIZONTAL;
212 self
213 }
214
215 pub fn validate(&self) -> Result<(), PlotError> {
217 validate_data_lengths(self.x_data, self.y_data)?;
218 validate_data_lengths(self.x_data, self.err_neg)?;
219 validate_data_lengths(self.x_data, self.err_pos)?;
220
221 if self.err_neg.iter().any(|&err| err < 0.0) || self.err_pos.iter().any(|&err| err < 0.0) {
223 return Err(PlotError::InvalidData(
224 "Error values cannot be negative".to_string(),
225 ));
226 }
227
228 Ok(())
229 }
230}
231
232impl<'a> Plot for AsymmetricErrorBarsPlot<'a> {
233 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
234 if self.validate().is_err() {
235 return;
236 }
237 let Ok(count) = i32::try_from(self.x_data.len()) else {
238 return;
239 };
240 let _guard = plot_ui.bind();
241 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
242 let spec = plot_spec_with_style(
243 self.style,
244 self.flags.bits() | self.item_flags.bits(),
245 self.layout,
246 );
247 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrdoublePtr(
248 label_ptr,
249 self.x_data.as_ptr(),
250 self.y_data.as_ptr(),
251 self.err_neg.as_ptr(),
252 self.err_pos.as_ptr(),
253 count,
254 spec,
255 );
256 })
257 }
258
259 fn label(&self) -> &str {
260 self.label
261 }
262}
263
264pub struct SimpleErrorBarsPlot<'a> {
266 label: &'a str,
267 values: &'a [f64],
268 errors: &'a [f64],
269 style: PlotItemStyle,
270 flags: ErrorBarsFlags,
271 item_flags: ItemFlags,
272 x_scale: f64,
273 x_start: f64,
274}
275
276impl<'a> super::PlotItemStyled for SimpleErrorBarsPlot<'a> {
277 fn style_mut(&mut self) -> &mut PlotItemStyle {
278 &mut self.style
279 }
280}
281
282impl<'a> SimpleErrorBarsPlot<'a> {
283 pub fn new(label: &'a str, values: &'a [f64], errors: &'a [f64]) -> Self {
285 Self {
286 label,
287 values,
288 errors,
289 style: PlotItemStyle::default(),
290 flags: ErrorBarsFlags::NONE,
291 item_flags: ItemFlags::NONE,
292 x_scale: 1.0,
293 x_start: 0.0,
294 }
295 }
296
297 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
299 self.flags = flags;
300 self
301 }
302
303 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
305 self.item_flags = flags;
306 self
307 }
308
309 pub fn horizontal(mut self) -> Self {
311 self.flags |= ErrorBarsFlags::HORIZONTAL;
312 self
313 }
314
315 pub fn with_x_scale(mut self, scale: f64) -> Self {
317 self.x_scale = scale;
318 self
319 }
320
321 pub fn with_x_start(mut self, start: f64) -> Self {
323 self.x_start = start;
324 self
325 }
326
327 pub fn validate(&self) -> Result<(), PlotError> {
329 validate_data_lengths(self.values, self.errors)?;
330
331 if self.errors.iter().any(|&err| err < 0.0) {
332 return Err(PlotError::InvalidData(
333 "Error values cannot be negative".to_string(),
334 ));
335 }
336
337 Ok(())
338 }
339}
340
341impl<'a> Plot for SimpleErrorBarsPlot<'a> {
342 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
343 if self.validate().is_err() {
344 return;
345 }
346 let Ok(count) = i32::try_from(self.values.len()) else {
347 return;
348 };
349
350 let x_data: Vec<f64> = (0..self.values.len())
352 .map(|i| self.x_start + i as f64 * self.x_scale)
353 .collect();
354
355 let _guard = plot_ui.bind();
356 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
357 let spec = plot_spec_with_style(
358 self.style,
359 self.flags.bits() | self.item_flags.bits(),
360 PlotDataLayout::DEFAULT,
361 );
362 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
363 label_ptr,
364 x_data.as_ptr(),
365 self.values.as_ptr(),
366 self.errors.as_ptr(),
367 count,
368 spec,
369 );
370 })
371 }
372
373 fn label(&self) -> &str {
374 self.label
375 }
376}
377
378impl<'ui> crate::PlotUi<'ui> {
380 pub fn error_bars_plot(
382 &self,
383 label: &str,
384 x_data: &[f64],
385 y_data: &[f64],
386 err_data: &[f64],
387 ) -> Result<(), PlotError> {
388 let plot = ErrorBarsPlot::new(label, x_data, y_data, err_data);
389 plot.validate()?;
390 plot.plot(self);
391 Ok(())
392 }
393
394 pub fn asymmetric_error_bars_plot(
396 &self,
397 label: &str,
398 x_data: &[f64],
399 y_data: &[f64],
400 err_neg: &[f64],
401 err_pos: &[f64],
402 ) -> Result<(), PlotError> {
403 let plot = AsymmetricErrorBarsPlot::new(label, x_data, y_data, err_neg, err_pos);
404 plot.validate()?;
405 plot.plot(self);
406 Ok(())
407 }
408
409 pub fn simple_error_bars_plot(
411 &self,
412 label: &str,
413 values: &[f64],
414 errors: &[f64],
415 ) -> Result<(), PlotError> {
416 let plot = SimpleErrorBarsPlot::new(label, values, errors);
417 plot.validate()?;
418 plot.plot(self);
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}