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 unsafe fn with_data_layout(mut self, layout: PlotDataLayout) -> Self {
101 self.layout = layout;
102 self
103 }
104
105 pub fn with_offset(mut self, offset: PlotDataOffset) -> Self {
107 self.layout = self.layout.with_offset(offset);
108 self
109 }
110
111 pub unsafe fn with_stride(mut self, stride: PlotDataStride) -> Self {
118 self.layout = self.layout.with_stride(stride);
119 self
120 }
121
122 pub fn validate(&self) -> Result<(), PlotError> {
124 validate_data_lengths(self.x_data, self.y_data)
125 }
126}
127
128impl<'a> Plot for ScatterPlot<'a> {
129 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
130 if self.validate().is_err() {
131 return; }
133 let Ok(count) = i32::try_from(self.x_data.len()) else {
134 return;
135 };
136
137 plot_ui.with_bound_context(|| {
138 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
139 let spec = plot_spec_with_style(
140 self.style,
141 self.flags.bits() | self.item_flags.bits(),
142 self.layout,
143 );
144 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
145 label_ptr,
146 self.x_data.as_ptr(),
147 self.y_data.as_ptr(),
148 count,
149 spec,
150 );
151 })
152 })
153 }
154
155 fn label(&self) -> &str {
156 self.label
157 }
158}
159
160pub struct SimpleScatterPlot<'a> {
162 label: &'a str,
163 values: &'a [f64],
164 style: PlotItemStyle,
165 flags: ScatterFlags,
166 item_flags: ItemFlags,
167 x_scale: f64,
168 x_start: f64,
169}
170
171impl<'a> super::PlotItemStyled for SimpleScatterPlot<'a> {
172 fn style_mut(&mut self) -> &mut PlotItemStyle {
173 &mut self.style
174 }
175}
176
177impl<'a> SimpleScatterPlot<'a> {
178 pub fn new(label: &'a str, values: &'a [f64]) -> Self {
180 Self {
181 label,
182 values,
183 style: PlotItemStyle::default(),
184 flags: ScatterFlags::NONE,
185 item_flags: ItemFlags::NONE,
186 x_scale: 1.0,
187 x_start: 0.0,
188 }
189 }
190
191 pub fn with_style(mut self, style: PlotItemStyle) -> Self {
193 self.style = style;
194 self
195 }
196
197 pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
199 self.style = self.style.with_line_color(color);
200 self
201 }
202
203 pub fn with_line_weight(mut self, weight: f32) -> Self {
205 self.style = self.style.with_line_weight(weight);
206 self
207 }
208
209 pub fn with_marker(mut self, marker: Marker) -> Self {
211 self.style = self.style.with_marker(marker);
212 self
213 }
214
215 pub fn with_marker_size(mut self, size: f32) -> Self {
217 self.style = self.style.with_marker_size(size);
218 self
219 }
220
221 pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
223 self.style = self.style.with_marker_line_color(color);
224 self
225 }
226
227 pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
229 self.style = self.style.with_marker_fill_color(color);
230 self
231 }
232
233 pub fn with_flags(mut self, flags: ScatterFlags) -> Self {
235 self.flags = flags;
236 self
237 }
238
239 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
241 self.item_flags = flags;
242 self
243 }
244
245 pub fn with_x_scale(mut self, scale: f64) -> Self {
247 self.x_scale = scale;
248 self
249 }
250
251 pub fn with_x_start(mut self, start: f64) -> Self {
253 self.x_start = start;
254 self
255 }
256}
257
258impl<'a> Plot for SimpleScatterPlot<'a> {
259 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
260 if self.values.is_empty() {
261 return;
262 }
263 let Ok(count) = i32::try_from(self.values.len()) else {
264 return;
265 };
266
267 plot_ui.with_bound_context(|| {
268 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
269 let spec = plot_spec_with_style(
270 self.style,
271 self.flags.bits() | self.item_flags.bits(),
272 PlotDataLayout::DEFAULT,
273 );
274 sys::ImPlot_PlotScatter_doublePtrInt(
275 label_ptr,
276 self.values.as_ptr(),
277 count,
278 self.x_scale,
279 self.x_start,
280 spec,
281 );
282 })
283 })
284 }
285
286 fn label(&self) -> &str {
287 self.label
288 }
289}
290
291impl<'ui> crate::PlotUi<'ui> {
293 pub fn scatter_plot(
295 &self,
296 label: &str,
297 x_data: &[f64],
298 y_data: &[f64],
299 ) -> Result<(), PlotError> {
300 let plot = ScatterPlot::new(label, x_data, y_data);
301 plot.validate()?;
302 plot.plot(self);
303 Ok(())
304 }
305
306 pub fn simple_scatter_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
308 if values.is_empty() {
309 return Err(PlotError::EmptyData);
310 }
311 let plot = SimpleScatterPlot::new(label, values);
312 plot.plot(self);
313 Ok(())
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn test_scatter_plot_creation() {
323 let x_data = [1.0, 2.0, 3.0, 4.0];
324 let y_data = [1.0, 4.0, 2.0, 3.0];
325
326 let plot = ScatterPlot::new("test", &x_data, &y_data);
327 assert_eq!(plot.label(), "test");
328 assert!(plot.validate().is_ok());
329 }
330
331 #[test]
332 fn test_scatter_plot_validation() {
333 let x_data = [1.0, 2.0, 3.0];
334 let y_data = [1.0, 4.0]; let plot = ScatterPlot::new("test", &x_data, &y_data);
337 assert!(plot.validate().is_err());
338 }
339
340 #[test]
341 fn test_simple_scatter_plot() {
342 let values = [1.0, 2.0, 3.0, 4.0];
343 let plot = SimpleScatterPlot::new("test", &values)
344 .with_flags(ScatterFlags::NO_CLIP)
345 .with_item_flags(ItemFlags::NO_FIT);
346 assert_eq!(plot.label(), "test");
347 assert_eq!(plot.flags.bits(), ScatterFlags::NO_CLIP.bits());
348 assert_eq!(plot.item_flags, ItemFlags::NO_FIT);
349 }
350
351 #[test]
352 fn test_scatter_plot_style_builders() {
353 let x_data = [1.0, 2.0, 3.0, 4.0];
354 let y_data = [1.0, 4.0, 2.0, 3.0];
355
356 let plot = ScatterPlot::new("styled", &x_data, &y_data)
357 .with_line_color([0.1, 0.2, 0.3, 0.4])
358 .with_line_weight(1.5)
359 .with_marker(Marker::Square)
360 .with_marker_size(8.0)
361 .with_marker_line_color([0.9, 0.8, 0.7, 0.6])
362 .with_marker_fill_color([0.6, 0.7, 0.8, 0.9]);
363
364 assert_eq!(
365 plot.style.line_color,
366 Some(sys::ImVec4_c {
367 x: 0.1,
368 y: 0.2,
369 z: 0.3,
370 w: 0.4,
371 })
372 );
373 assert_eq!(plot.style.line_weight, Some(1.5));
374 assert_eq!(plot.style.marker, Some(Marker::Square as sys::ImPlotMarker));
375 assert_eq!(plot.style.marker_size, Some(8.0));
376 assert_eq!(
377 plot.style.marker_line_color,
378 Some(sys::ImVec4_c {
379 x: 0.9,
380 y: 0.8,
381 z: 0.7,
382 w: 0.6,
383 })
384 );
385 assert_eq!(
386 plot.style.marker_fill_color,
387 Some(sys::ImVec4_c {
388 x: 0.6,
389 y: 0.7,
390 z: 0.8,
391 w: 0.9,
392 })
393 );
394 }
395}