1use super::{Plot, PlotError, validate_data_lengths, with_plot_str_or_empty};
4use crate::{ErrorBarsFlags, sys};
5
6pub struct ErrorBarsPlot<'a> {
8 label: &'a str,
9 x_data: &'a [f64],
10 y_data: &'a [f64],
11 err_data: &'a [f64],
12 flags: ErrorBarsFlags,
13 offset: i32,
14 stride: i32,
15}
16
17impl<'a> ErrorBarsPlot<'a> {
18 pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64], err_data: &'a [f64]) -> Self {
26 Self {
27 label,
28 x_data,
29 y_data,
30 err_data,
31 flags: ErrorBarsFlags::NONE,
32 offset: 0,
33 stride: std::mem::size_of::<f64>() as i32,
34 }
35 }
36
37 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
39 self.flags = flags;
40 self
41 }
42
43 pub fn with_offset(mut self, offset: i32) -> Self {
45 self.offset = offset;
46 self
47 }
48
49 pub fn with_stride(mut self, stride: i32) -> Self {
51 self.stride = stride;
52 self
53 }
54
55 pub fn horizontal(self) -> Self {
57 self
60 }
61
62 pub fn validate(&self) -> Result<(), PlotError> {
64 validate_data_lengths(self.x_data, self.y_data)?;
65 validate_data_lengths(self.x_data, self.err_data)?;
66
67 if self.err_data.iter().any(|&err| err < 0.0) {
69 return Err(PlotError::InvalidData(
70 "Error values cannot be negative".to_string(),
71 ));
72 }
73
74 Ok(())
75 }
76}
77
78impl<'a> Plot for ErrorBarsPlot<'a> {
79 fn plot(&self) {
80 if self.validate().is_err() {
81 return;
82 }
83 let Ok(count) = i32::try_from(self.x_data.len()) else {
84 return;
85 };
86 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
87 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
88 label_ptr,
89 self.x_data.as_ptr(),
90 self.y_data.as_ptr(),
91 self.err_data.as_ptr(),
92 count,
93 self.flags.bits() as i32,
94 self.offset,
95 self.stride,
96 );
97 })
98 }
99
100 fn label(&self) -> &str {
101 self.label
102 }
103}
104
105pub struct AsymmetricErrorBarsPlot<'a> {
107 label: &'a str,
108 x_data: &'a [f64],
109 y_data: &'a [f64],
110 err_neg: &'a [f64],
111 err_pos: &'a [f64],
112 flags: ErrorBarsFlags,
113}
114
115impl<'a> AsymmetricErrorBarsPlot<'a> {
116 pub fn new(
125 label: &'a str,
126 x_data: &'a [f64],
127 y_data: &'a [f64],
128 err_neg: &'a [f64],
129 err_pos: &'a [f64],
130 ) -> Self {
131 Self {
132 label,
133 x_data,
134 y_data,
135 err_neg,
136 err_pos,
137 flags: ErrorBarsFlags::NONE,
138 }
139 }
140
141 pub fn with_flags(mut self, flags: ErrorBarsFlags) -> Self {
143 self.flags = flags;
144 self
145 }
146
147 pub fn validate(&self) -> Result<(), PlotError> {
149 validate_data_lengths(self.x_data, self.y_data)?;
150 validate_data_lengths(self.x_data, self.err_neg)?;
151 validate_data_lengths(self.x_data, self.err_pos)?;
152
153 if self.err_neg.iter().any(|&err| err < 0.0) || self.err_pos.iter().any(|&err| err < 0.0) {
155 return Err(PlotError::InvalidData(
156 "Error values cannot be negative".to_string(),
157 ));
158 }
159
160 Ok(())
161 }
162}
163
164impl<'a> Plot for AsymmetricErrorBarsPlot<'a> {
165 fn plot(&self) {
166 if self.validate().is_err() {
167 return;
168 }
169 let Ok(count) = i32::try_from(self.x_data.len()) else {
170 return;
171 };
172 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
173 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrdoublePtr(
174 label_ptr,
175 self.x_data.as_ptr(),
176 self.y_data.as_ptr(),
177 self.err_neg.as_ptr(),
178 self.err_pos.as_ptr(),
179 count,
180 self.flags.bits() as i32,
181 0,
182 std::mem::size_of::<f64>() as i32,
183 );
184 })
185 }
186
187 fn label(&self) -> &str {
188 self.label
189 }
190}
191
192pub struct SimpleErrorBarsPlot<'a> {
194 label: &'a str,
195 values: &'a [f64],
196 errors: &'a [f64],
197 x_scale: f64,
198 x_start: f64,
199}
200
201impl<'a> SimpleErrorBarsPlot<'a> {
202 pub fn new(label: &'a str, values: &'a [f64], errors: &'a [f64]) -> Self {
204 Self {
205 label,
206 values,
207 errors,
208 x_scale: 1.0,
209 x_start: 0.0,
210 }
211 }
212
213 pub fn with_x_scale(mut self, scale: f64) -> Self {
215 self.x_scale = scale;
216 self
217 }
218
219 pub fn with_x_start(mut self, start: f64) -> Self {
221 self.x_start = start;
222 self
223 }
224
225 pub fn validate(&self) -> Result<(), PlotError> {
227 validate_data_lengths(self.values, self.errors)?;
228
229 if self.errors.iter().any(|&err| err < 0.0) {
230 return Err(PlotError::InvalidData(
231 "Error values cannot be negative".to_string(),
232 ));
233 }
234
235 Ok(())
236 }
237}
238
239impl<'a> Plot for SimpleErrorBarsPlot<'a> {
240 fn plot(&self) {
241 if self.validate().is_err() {
242 return;
243 }
244 let Ok(count) = i32::try_from(self.values.len()) else {
245 return;
246 };
247
248 let x_data: Vec<f64> = (0..self.values.len())
250 .map(|i| self.x_start + i as f64 * self.x_scale)
251 .collect();
252
253 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
254 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
255 label_ptr,
256 x_data.as_ptr(),
257 self.values.as_ptr(),
258 self.errors.as_ptr(),
259 count,
260 0, 0,
262 std::mem::size_of::<f64>() as i32,
263 );
264 })
265 }
266
267 fn label(&self) -> &str {
268 self.label
269 }
270}
271
272impl<'ui> crate::PlotUi<'ui> {
274 pub fn error_bars_plot(
276 &self,
277 label: &str,
278 x_data: &[f64],
279 y_data: &[f64],
280 err_data: &[f64],
281 ) -> Result<(), PlotError> {
282 let plot = ErrorBarsPlot::new(label, x_data, y_data, err_data);
283 plot.validate()?;
284 plot.plot();
285 Ok(())
286 }
287
288 pub fn asymmetric_error_bars_plot(
290 &self,
291 label: &str,
292 x_data: &[f64],
293 y_data: &[f64],
294 err_neg: &[f64],
295 err_pos: &[f64],
296 ) -> Result<(), PlotError> {
297 let plot = AsymmetricErrorBarsPlot::new(label, x_data, y_data, err_neg, err_pos);
298 plot.validate()?;
299 plot.plot();
300 Ok(())
301 }
302
303 pub fn simple_error_bars_plot(
305 &self,
306 label: &str,
307 values: &[f64],
308 errors: &[f64],
309 ) -> Result<(), PlotError> {
310 let plot = SimpleErrorBarsPlot::new(label, values, errors);
311 plot.validate()?;
312 plot.plot();
313 Ok(())
314 }
315}