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