1use super::{Plot, PlotError, plot_spec_from, validate_data_lengths, with_plot_str_or_empty};
4use crate::{ItemFlags, ScatterFlags, sys};
5
6pub struct ScatterPlot<'a> {
8 label: &'a str,
9 x_data: &'a [f64],
10 y_data: &'a [f64],
11 flags: ScatterFlags,
12 item_flags: ItemFlags,
13 offset: i32,
14 stride: i32,
15}
16
17impl<'a> ScatterPlot<'a> {
18 pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
20 Self {
21 label,
22 x_data,
23 y_data,
24 flags: ScatterFlags::NONE,
25 item_flags: ItemFlags::NONE,
26 offset: 0,
27 stride: std::mem::size_of::<f64>() as i32,
28 }
29 }
30
31 pub fn with_flags(mut self, flags: ScatterFlags) -> Self {
33 self.flags = flags;
34 self
35 }
36
37 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
39 self.item_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 validate(&self) -> Result<(), PlotError> {
57 validate_data_lengths(self.x_data, self.y_data)
58 }
59}
60
61impl<'a> Plot for ScatterPlot<'a> {
62 fn plot(&self) {
63 if self.validate().is_err() {
64 return; }
66 let Ok(count) = i32::try_from(self.x_data.len()) else {
67 return;
68 };
69
70 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
71 let spec = plot_spec_from(
72 self.flags.bits() | self.item_flags.bits(),
73 self.offset,
74 self.stride,
75 );
76 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
77 label_ptr,
78 self.x_data.as_ptr(),
79 self.y_data.as_ptr(),
80 count,
81 spec,
82 );
83 })
84 }
85
86 fn label(&self) -> &str {
87 self.label
88 }
89}
90
91pub struct SimpleScatterPlot<'a> {
93 label: &'a str,
94 values: &'a [f64],
95 x_scale: f64,
96 x_start: f64,
97}
98
99impl<'a> SimpleScatterPlot<'a> {
100 pub fn new(label: &'a str, values: &'a [f64]) -> Self {
102 Self {
103 label,
104 values,
105 x_scale: 1.0,
106 x_start: 0.0,
107 }
108 }
109
110 pub fn with_x_scale(mut self, scale: f64) -> Self {
112 self.x_scale = scale;
113 self
114 }
115
116 pub fn with_x_start(mut self, start: f64) -> Self {
118 self.x_start = start;
119 self
120 }
121}
122
123impl<'a> Plot for SimpleScatterPlot<'a> {
124 fn plot(&self) {
125 if self.values.is_empty() {
126 return;
127 }
128 let Ok(count) = i32::try_from(self.values.len()) else {
129 return;
130 };
131
132 let x_data: Vec<f64> = (0..self.values.len())
134 .map(|i| self.x_start + i as f64 * self.x_scale)
135 .collect();
136
137 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
138 let spec = plot_spec_from(0, 0, std::mem::size_of::<f64>() as i32);
139 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
140 label_ptr,
141 x_data.as_ptr(),
142 self.values.as_ptr(),
143 count,
144 spec,
145 );
146 })
147 }
148
149 fn label(&self) -> &str {
150 self.label
151 }
152}
153
154impl<'ui> crate::PlotUi<'ui> {
156 pub fn scatter_plot(
158 &self,
159 label: &str,
160 x_data: &[f64],
161 y_data: &[f64],
162 ) -> Result<(), PlotError> {
163 let plot = ScatterPlot::new(label, x_data, y_data);
164 plot.validate()?;
165 plot.plot();
166 Ok(())
167 }
168
169 pub fn simple_scatter_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
171 if values.is_empty() {
172 return Err(PlotError::EmptyData);
173 }
174 let plot = SimpleScatterPlot::new(label, values);
175 plot.plot();
176 Ok(())
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_scatter_plot_creation() {
186 let x_data = [1.0, 2.0, 3.0, 4.0];
187 let y_data = [1.0, 4.0, 2.0, 3.0];
188
189 let plot = ScatterPlot::new("test", &x_data, &y_data);
190 assert_eq!(plot.label(), "test");
191 assert!(plot.validate().is_ok());
192 }
193
194 #[test]
195 fn test_scatter_plot_validation() {
196 let x_data = [1.0, 2.0, 3.0];
197 let y_data = [1.0, 4.0]; let plot = ScatterPlot::new("test", &x_data, &y_data);
200 assert!(plot.validate().is_err());
201 }
202
203 #[test]
204 fn test_simple_scatter_plot() {
205 let values = [1.0, 2.0, 3.0, 4.0];
206 let plot = SimpleScatterPlot::new("test", &values);
207 assert_eq!(plot.label(), "test");
208 }
209}