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