1use super::{
4 Plot, PlotError, PlotItemStyle, plot_spec_with_style, validate_data_lengths,
5 with_plot_str_or_empty,
6};
7use crate::{ItemFlags, Marker, ScatterFlags, sys};
8
9pub struct ScatterPlot<'a> {
11 label: &'a str,
12 x_data: &'a [f64],
13 y_data: &'a [f64],
14 style: PlotItemStyle,
15 flags: ScatterFlags,
16 item_flags: ItemFlags,
17 offset: i32,
18 stride: i32,
19}
20
21impl<'a> super::PlotItemStyled for ScatterPlot<'a> {
22 fn style_mut(&mut self) -> &mut PlotItemStyle {
23 &mut self.style
24 }
25}
26
27impl<'a> ScatterPlot<'a> {
28 pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
30 Self {
31 label,
32 x_data,
33 y_data,
34 style: PlotItemStyle::default(),
35 flags: ScatterFlags::NONE,
36 item_flags: ItemFlags::NONE,
37 offset: 0,
38 stride: std::mem::size_of::<f64>() as i32,
39 }
40 }
41
42 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
44 self.style = style;
45 self
46 }
47
48 pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
50 self.style = self.style.with_line_color(color);
51 self
52 }
53
54 pub fn with_line_weight(mut self, weight: f32) -> Self {
56 self.style = self.style.with_line_weight(weight);
57 self
58 }
59
60 pub fn with_marker(mut self, marker: Marker) -> Self {
62 self.style = self.style.with_marker(marker);
63 self
64 }
65
66 pub fn with_marker_size(mut self, size: f32) -> Self {
68 self.style = self.style.with_marker_size(size);
69 self
70 }
71
72 pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
74 self.style = self.style.with_marker_line_color(color);
75 self
76 }
77
78 pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
80 self.style = self.style.with_marker_fill_color(color);
81 self
82 }
83
84 pub fn with_flags(mut self, flags: ScatterFlags) -> Self {
86 self.flags = flags;
87 self
88 }
89
90 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
92 self.item_flags = flags;
93 self
94 }
95
96 pub fn with_offset(mut self, offset: i32) -> Self {
98 self.offset = offset;
99 self
100 }
101
102 pub fn with_stride(mut self, stride: i32) -> Self {
104 self.stride = stride;
105 self
106 }
107
108 pub fn validate(&self) -> Result<(), PlotError> {
110 validate_data_lengths(self.x_data, self.y_data)
111 }
112}
113
114impl<'a> Plot for ScatterPlot<'a> {
115 fn plot(&self) {
116 if self.validate().is_err() {
117 return; }
119 let Ok(count) = i32::try_from(self.x_data.len()) else {
120 return;
121 };
122
123 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
124 let spec = plot_spec_with_style(
125 self.style,
126 self.flags.bits() | self.item_flags.bits(),
127 self.offset,
128 self.stride,
129 );
130 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
131 label_ptr,
132 self.x_data.as_ptr(),
133 self.y_data.as_ptr(),
134 count,
135 spec,
136 );
137 })
138 }
139
140 fn label(&self) -> &str {
141 self.label
142 }
143}
144
145pub struct SimpleScatterPlot<'a> {
147 label: &'a str,
148 values: &'a [f64],
149 style: PlotItemStyle,
150 flags: ScatterFlags,
151 item_flags: ItemFlags,
152 x_scale: f64,
153 x_start: f64,
154}
155
156impl<'a> super::PlotItemStyled for SimpleScatterPlot<'a> {
157 fn style_mut(&mut self) -> &mut PlotItemStyle {
158 &mut self.style
159 }
160}
161
162impl<'a> SimpleScatterPlot<'a> {
163 pub fn new(label: &'a str, values: &'a [f64]) -> Self {
165 Self {
166 label,
167 values,
168 style: PlotItemStyle::default(),
169 flags: ScatterFlags::NONE,
170 item_flags: ItemFlags::NONE,
171 x_scale: 1.0,
172 x_start: 0.0,
173 }
174 }
175
176 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
178 self.style = style;
179 self
180 }
181
182 pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
184 self.style = self.style.with_line_color(color);
185 self
186 }
187
188 pub fn with_line_weight(mut self, weight: f32) -> Self {
190 self.style = self.style.with_line_weight(weight);
191 self
192 }
193
194 pub fn with_marker(mut self, marker: Marker) -> Self {
196 self.style = self.style.with_marker(marker);
197 self
198 }
199
200 pub fn with_marker_size(mut self, size: f32) -> Self {
202 self.style = self.style.with_marker_size(size);
203 self
204 }
205
206 pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
208 self.style = self.style.with_marker_line_color(color);
209 self
210 }
211
212 pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
214 self.style = self.style.with_marker_fill_color(color);
215 self
216 }
217
218 pub fn with_flags(mut self, flags: ScatterFlags) -> Self {
220 self.flags = flags;
221 self
222 }
223
224 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
226 self.item_flags = flags;
227 self
228 }
229
230 pub fn with_x_scale(mut self, scale: f64) -> Self {
232 self.x_scale = scale;
233 self
234 }
235
236 pub fn with_x_start(mut self, start: f64) -> Self {
238 self.x_start = start;
239 self
240 }
241}
242
243impl<'a> Plot for SimpleScatterPlot<'a> {
244 fn plot(&self) {
245 if self.values.is_empty() {
246 return;
247 }
248 let Ok(count) = i32::try_from(self.values.len()) else {
249 return;
250 };
251
252 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
253 let spec = plot_spec_with_style(
254 self.style,
255 self.flags.bits() | self.item_flags.bits(),
256 0,
257 std::mem::size_of::<f64>() as i32,
258 );
259 sys::ImPlot_PlotScatter_doublePtrInt(
260 label_ptr,
261 self.values.as_ptr(),
262 count,
263 self.x_scale,
264 self.x_start,
265 spec,
266 );
267 })
268 }
269
270 fn label(&self) -> &str {
271 self.label
272 }
273}
274
275impl<'ui> crate::PlotUi<'ui> {
277 pub fn scatter_plot(
279 &self,
280 label: &str,
281 x_data: &[f64],
282 y_data: &[f64],
283 ) -> Result<(), PlotError> {
284 let plot = ScatterPlot::new(label, x_data, y_data);
285 plot.validate()?;
286 self.bind();
287 plot.plot();
288 Ok(())
289 }
290
291 pub fn simple_scatter_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
293 if values.is_empty() {
294 return Err(PlotError::EmptyData);
295 }
296 let plot = SimpleScatterPlot::new(label, values);
297 self.bind();
298 plot.plot();
299 Ok(())
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_scatter_plot_creation() {
309 let x_data = [1.0, 2.0, 3.0, 4.0];
310 let y_data = [1.0, 4.0, 2.0, 3.0];
311
312 let plot = ScatterPlot::new("test", &x_data, &y_data);
313 assert_eq!(plot.label(), "test");
314 assert!(plot.validate().is_ok());
315 }
316
317 #[test]
318 fn test_scatter_plot_validation() {
319 let x_data = [1.0, 2.0, 3.0];
320 let y_data = [1.0, 4.0]; let plot = ScatterPlot::new("test", &x_data, &y_data);
323 assert!(plot.validate().is_err());
324 }
325
326 #[test]
327 fn test_simple_scatter_plot() {
328 let values = [1.0, 2.0, 3.0, 4.0];
329 let plot = SimpleScatterPlot::new("test", &values)
330 .with_flags(ScatterFlags::NO_CLIP)
331 .with_item_flags(ItemFlags::NO_FIT);
332 assert_eq!(plot.label(), "test");
333 assert_eq!(plot.flags.bits(), ScatterFlags::NO_CLIP.bits());
334 assert_eq!(plot.item_flags, ItemFlags::NO_FIT);
335 }
336
337 #[test]
338 fn test_scatter_plot_style_builders() {
339 let x_data = [1.0, 2.0, 3.0, 4.0];
340 let y_data = [1.0, 4.0, 2.0, 3.0];
341
342 let plot = ScatterPlot::new("styled", &x_data, &y_data)
343 .with_line_color([0.1, 0.2, 0.3, 0.4])
344 .with_line_weight(1.5)
345 .with_marker(Marker::Square)
346 .with_marker_size(8.0)
347 .with_marker_line_color([0.9, 0.8, 0.7, 0.6])
348 .with_marker_fill_color([0.6, 0.7, 0.8, 0.9]);
349
350 assert_eq!(
351 plot.style.line_color,
352 Some(sys::ImVec4_c {
353 x: 0.1,
354 y: 0.2,
355 z: 0.3,
356 w: 0.4,
357 })
358 );
359 assert_eq!(plot.style.line_weight, Some(1.5));
360 assert_eq!(plot.style.marker, Some(Marker::Square as sys::ImPlotMarker));
361 assert_eq!(plot.style.marker_size, Some(8.0));
362 assert_eq!(
363 plot.style.marker_line_color,
364 Some(sys::ImVec4_c {
365 x: 0.9,
366 y: 0.8,
367 z: 0.7,
368 w: 0.6,
369 })
370 );
371 assert_eq!(
372 plot.style.marker_fill_color,
373 Some(sys::ImVec4_c {
374 x: 0.6,
375 y: 0.7,
376 z: 0.8,
377 w: 0.9,
378 })
379 );
380 }
381}