dear_implot/plots/
scatter.rs1use super::{Plot, PlotError, validate_data_lengths, with_plot_str_or_empty};
4use crate::{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 offset: i32,
13 stride: i32,
14}
15
16impl<'a> ScatterPlot<'a> {
17 pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
19 Self {
20 label,
21 x_data,
22 y_data,
23 flags: ScatterFlags::NONE,
24 offset: 0,
25 stride: std::mem::size_of::<f64>() as i32,
26 }
27 }
28
29 pub fn with_flags(mut self, flags: ScatterFlags) -> Self {
31 self.flags = flags;
32 self
33 }
34
35 pub fn with_offset(mut self, offset: i32) -> Self {
37 self.offset = offset;
38 self
39 }
40
41 pub fn with_stride(mut self, stride: i32) -> Self {
43 self.stride = stride;
44 self
45 }
46
47 pub fn validate(&self) -> Result<(), PlotError> {
49 validate_data_lengths(self.x_data, self.y_data)
50 }
51}
52
53impl<'a> Plot for ScatterPlot<'a> {
54 fn plot(&self) {
55 if self.validate().is_err() {
56 return; }
58 let Ok(count) = i32::try_from(self.x_data.len()) else {
59 return;
60 };
61
62 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
63 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
64 label_ptr,
65 self.x_data.as_ptr(),
66 self.y_data.as_ptr(),
67 count,
68 self.flags.bits() as i32,
69 self.offset,
70 self.stride,
71 );
72 })
73 }
74
75 fn label(&self) -> &str {
76 self.label
77 }
78}
79
80pub struct SimpleScatterPlot<'a> {
82 label: &'a str,
83 values: &'a [f64],
84 x_scale: f64,
85 x_start: f64,
86}
87
88impl<'a> SimpleScatterPlot<'a> {
89 pub fn new(label: &'a str, values: &'a [f64]) -> Self {
91 Self {
92 label,
93 values,
94 x_scale: 1.0,
95 x_start: 0.0,
96 }
97 }
98
99 pub fn with_x_scale(mut self, scale: f64) -> Self {
101 self.x_scale = scale;
102 self
103 }
104
105 pub fn with_x_start(mut self, start: f64) -> Self {
107 self.x_start = start;
108 self
109 }
110}
111
112impl<'a> Plot for SimpleScatterPlot<'a> {
113 fn plot(&self) {
114 if self.values.is_empty() {
115 return;
116 }
117 let Ok(count) = i32::try_from(self.values.len()) else {
118 return;
119 };
120
121 let x_data: Vec<f64> = (0..self.values.len())
123 .map(|i| self.x_start + i as f64 * self.x_scale)
124 .collect();
125
126 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
127 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
128 label_ptr,
129 x_data.as_ptr(),
130 self.values.as_ptr(),
131 count,
132 0,
133 0,
134 std::mem::size_of::<f64>() as i32,
135 );
136 })
137 }
138
139 fn label(&self) -> &str {
140 self.label
141 }
142}
143
144impl<'ui> crate::PlotUi<'ui> {
146 pub fn scatter_plot(
148 &self,
149 label: &str,
150 x_data: &[f64],
151 y_data: &[f64],
152 ) -> Result<(), PlotError> {
153 let plot = ScatterPlot::new(label, x_data, y_data);
154 plot.validate()?;
155 plot.plot();
156 Ok(())
157 }
158
159 pub fn simple_scatter_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
161 if values.is_empty() {
162 return Err(PlotError::EmptyData);
163 }
164 let plot = SimpleScatterPlot::new(label, values);
165 plot.plot();
166 Ok(())
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_scatter_plot_creation() {
176 let x_data = [1.0, 2.0, 3.0, 4.0];
177 let y_data = [1.0, 4.0, 2.0, 3.0];
178
179 let plot = ScatterPlot::new("test", &x_data, &y_data);
180 assert_eq!(plot.label(), "test");
181 assert!(plot.validate().is_ok());
182 }
183
184 #[test]
185 fn test_scatter_plot_validation() {
186 let x_data = [1.0, 2.0, 3.0];
187 let y_data = [1.0, 4.0]; let plot = ScatterPlot::new("test", &x_data, &y_data);
190 assert!(plot.validate().is_err());
191 }
192
193 #[test]
194 fn test_simple_scatter_plot() {
195 let values = [1.0, 2.0, 3.0, 4.0];
196 let plot = SimpleScatterPlot::new("test", &values);
197 assert_eq!(plot.label(), "test");
198 }
199}