1use super::{Plot, PlotError, safe_cstring, validate_data_lengths};
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
84 let label_cstr = safe_cstring(self.label);
85
86 unsafe {
87 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
88 label_cstr.as_ptr(),
89 self.x_data.as_ptr(),
90 self.y_data.as_ptr(),
91 self.err_data.as_ptr(),
92 self.x_data.len() as i32,
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
170 let label_cstr = safe_cstring(self.label);
171
172 unsafe {
173 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrdoublePtr(
174 label_cstr.as_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 self.x_data.len() as i32,
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
245 let label_cstr = safe_cstring(self.label);
246
247 let x_data: Vec<f64> = (0..self.values.len())
249 .map(|i| self.x_start + i as f64 * self.x_scale)
250 .collect();
251
252 unsafe {
253 sys::ImPlot_PlotErrorBars_doublePtrdoublePtrdoublePtrInt(
254 label_cstr.as_ptr(),
255 x_data.as_ptr(),
256 self.values.as_ptr(),
257 self.errors.as_ptr(),
258 self.values.len() as i32,
259 0, 0,
261 std::mem::size_of::<f64>() as i32,
262 );
263 }
264 }
265
266 fn label(&self) -> &str {
267 self.label
268 }
269}
270
271impl<'ui> crate::PlotUi<'ui> {
273 pub fn error_bars_plot(
275 &self,
276 label: &str,
277 x_data: &[f64],
278 y_data: &[f64],
279 err_data: &[f64],
280 ) -> Result<(), PlotError> {
281 let plot = ErrorBarsPlot::new(label, x_data, y_data, err_data);
282 plot.validate()?;
283 plot.plot();
284 Ok(())
285 }
286
287 pub fn asymmetric_error_bars_plot(
289 &self,
290 label: &str,
291 x_data: &[f64],
292 y_data: &[f64],
293 err_neg: &[f64],
294 err_pos: &[f64],
295 ) -> Result<(), PlotError> {
296 let plot = AsymmetricErrorBarsPlot::new(label, x_data, y_data, err_neg, err_pos);
297 plot.validate()?;
298 plot.plot();
299 Ok(())
300 }
301
302 pub fn simple_error_bars_plot(
304 &self,
305 label: &str,
306 values: &[f64],
307 errors: &[f64],
308 ) -> Result<(), PlotError> {
309 let plot = SimpleErrorBarsPlot::new(label, values, errors);
310 plot.validate()?;
311 plot.plot();
312 Ok(())
313 }
314}