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 plot.plot();
287 Ok(())
288 }
289
290 pub fn simple_scatter_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
292 if values.is_empty() {
293 return Err(PlotError::EmptyData);
294 }
295 let plot = SimpleScatterPlot::new(label, values);
296 plot.plot();
297 Ok(())
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn test_scatter_plot_creation() {
307 let x_data = [1.0, 2.0, 3.0, 4.0];
308 let y_data = [1.0, 4.0, 2.0, 3.0];
309
310 let plot = ScatterPlot::new("test", &x_data, &y_data);
311 assert_eq!(plot.label(), "test");
312 assert!(plot.validate().is_ok());
313 }
314
315 #[test]
316 fn test_scatter_plot_validation() {
317 let x_data = [1.0, 2.0, 3.0];
318 let y_data = [1.0, 4.0]; let plot = ScatterPlot::new("test", &x_data, &y_data);
321 assert!(plot.validate().is_err());
322 }
323
324 #[test]
325 fn test_simple_scatter_plot() {
326 let values = [1.0, 2.0, 3.0, 4.0];
327 let plot = SimpleScatterPlot::new("test", &values)
328 .with_flags(ScatterFlags::NO_CLIP)
329 .with_item_flags(ItemFlags::NO_FIT);
330 assert_eq!(plot.label(), "test");
331 assert_eq!(plot.flags.bits(), ScatterFlags::NO_CLIP.bits());
332 assert_eq!(plot.item_flags, ItemFlags::NO_FIT);
333 }
334
335 #[test]
336 fn test_scatter_plot_style_builders() {
337 let x_data = [1.0, 2.0, 3.0, 4.0];
338 let y_data = [1.0, 4.0, 2.0, 3.0];
339
340 let plot = ScatterPlot::new("styled", &x_data, &y_data)
341 .with_line_color([0.1, 0.2, 0.3, 0.4])
342 .with_line_weight(1.5)
343 .with_marker(Marker::Square)
344 .with_marker_size(8.0)
345 .with_marker_line_color([0.9, 0.8, 0.7, 0.6])
346 .with_marker_fill_color([0.6, 0.7, 0.8, 0.9]);
347
348 assert_eq!(
349 plot.style.line_color,
350 Some(sys::ImVec4_c {
351 x: 0.1,
352 y: 0.2,
353 z: 0.3,
354 w: 0.4,
355 })
356 );
357 assert_eq!(plot.style.line_weight, Some(1.5));
358 assert_eq!(plot.style.marker, Some(Marker::Square as sys::ImPlotMarker));
359 assert_eq!(plot.style.marker_size, Some(8.0));
360 assert_eq!(
361 plot.style.marker_line_color,
362 Some(sys::ImVec4_c {
363 x: 0.9,
364 y: 0.8,
365 z: 0.7,
366 w: 0.6,
367 })
368 );
369 assert_eq!(
370 plot.style.marker_fill_color,
371 Some(sys::ImVec4_c {
372 x: 0.6,
373 y: 0.7,
374 z: 0.8,
375 w: 0.9,
376 })
377 );
378 }
379}