dear_implot/plots/
digital.rs

1//! Digital plot implementation
2
3use super::{PlotData, PlotError, validate_data_lengths, with_plot_str_or_empty};
4use crate::DigitalFlags;
5use crate::sys;
6
7/// Builder for digital plots with extensive customization options
8///
9/// Digital plots are used to display digital signals (0/1, high/low, etc.)
10/// They do not respond to y drag or zoom, and are always referenced to the bottom of the plot.
11pub struct DigitalPlot<'a> {
12    label: &'a str,
13    x_data: &'a [f64],
14    y_data: &'a [f64],
15    flags: DigitalFlags,
16    offset: i32,
17    stride: i32,
18}
19
20impl<'a> DigitalPlot<'a> {
21    /// Create a new digital plot with the given label and data
22    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
23        Self {
24            label,
25            x_data,
26            y_data,
27            flags: DigitalFlags::NONE,
28            offset: 0,
29            stride: std::mem::size_of::<f64>() as i32,
30        }
31    }
32
33    /// Set digital flags for customization
34    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
35        self.flags = flags;
36        self
37    }
38
39    /// Set data offset for partial plotting
40    pub fn with_offset(mut self, offset: i32) -> Self {
41        self.offset = offset;
42        self
43    }
44
45    /// Set data stride for non-contiguous data
46    pub fn with_stride(mut self, stride: i32) -> Self {
47        self.stride = stride;
48        self
49    }
50
51    /// Validate the plot data
52    pub fn validate(&self) -> Result<(), PlotError> {
53        validate_data_lengths(self.x_data, self.y_data)?;
54
55        // Digital plots should have binary-like data (0/1, but we allow any values)
56        // The validation is mainly for data length consistency
57        Ok(())
58    }
59
60    /// Plot the digital signal
61    pub fn plot(self) {
62        let Ok(count) = i32::try_from(self.x_data.len()) else {
63            return;
64        };
65        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
66            sys::ImPlot_PlotDigital_doublePtr(
67                label_ptr,
68                self.x_data.as_ptr(),
69                self.y_data.as_ptr(),
70                count,
71                self.flags.bits() as i32,
72                self.offset,
73                self.stride,
74            );
75        })
76    }
77}
78
79impl<'a> PlotData for DigitalPlot<'a> {
80    fn label(&self) -> &str {
81        self.label
82    }
83
84    fn data_len(&self) -> usize {
85        self.x_data.len().min(self.y_data.len())
86    }
87}
88
89/// Digital plot for f32 data
90pub struct DigitalPlotF32<'a> {
91    label: &'a str,
92    x_data: &'a [f32],
93    y_data: &'a [f32],
94    flags: DigitalFlags,
95}
96
97impl<'a> DigitalPlotF32<'a> {
98    /// Create a new digital plot with f32 data
99    pub fn new(label: &'a str, x_data: &'a [f32], y_data: &'a [f32]) -> Self {
100        Self {
101            label,
102            x_data,
103            y_data,
104            flags: DigitalFlags::NONE,
105        }
106    }
107
108    /// Set digital flags for customization
109    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
110        self.flags = flags;
111        self
112    }
113
114    /// Validate the plot data
115    pub fn validate(&self) -> Result<(), PlotError> {
116        if self.x_data.len() != self.y_data.len() {
117            return Err(PlotError::DataLengthMismatch {
118                x_len: self.x_data.len(),
119                y_len: self.y_data.len(),
120            });
121        }
122        if self.x_data.is_empty() {
123            return Err(PlotError::EmptyData);
124        }
125        Ok(())
126    }
127
128    /// Plot the digital signal
129    pub fn plot(self) {
130        let Ok(count) = i32::try_from(self.x_data.len()) else {
131            return;
132        };
133        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
134            sys::ImPlot_PlotDigital_FloatPtr(
135                label_ptr,
136                self.x_data.as_ptr(),
137                self.y_data.as_ptr(),
138                count,
139                self.flags.bits() as i32,
140                0,
141                std::mem::size_of::<f32>() as i32,
142            );
143        })
144    }
145}
146
147impl<'a> PlotData for DigitalPlotF32<'a> {
148    fn label(&self) -> &str {
149        self.label
150    }
151
152    fn data_len(&self) -> usize {
153        self.x_data.len().min(self.y_data.len())
154    }
155}
156
157/// Simple digital plot for single array data (y values only, x is auto-generated)
158pub struct SimpleDigitalPlot<'a> {
159    label: &'a str,
160    y_data: &'a [f64],
161    flags: DigitalFlags,
162    x_scale: f64,
163    x_start: f64,
164}
165
166impl<'a> SimpleDigitalPlot<'a> {
167    /// Create a new simple digital plot with only y data
168    pub fn new(label: &'a str, y_data: &'a [f64]) -> Self {
169        Self {
170            label,
171            y_data,
172            flags: DigitalFlags::NONE,
173            x_scale: 1.0,
174            x_start: 0.0,
175        }
176    }
177
178    /// Set the x scale (spacing between points)
179    pub fn with_x_scale(mut self, x_scale: f64) -> Self {
180        self.x_scale = x_scale;
181        self
182    }
183
184    /// Set the x start value
185    pub fn with_x_start(mut self, x_start: f64) -> Self {
186        self.x_start = x_start;
187        self
188    }
189
190    /// Set digital flags
191    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
192        self.flags = flags;
193        self
194    }
195
196    /// Validate the plot data
197    pub fn validate(&self) -> Result<(), PlotError> {
198        if self.y_data.is_empty() {
199            return Err(PlotError::EmptyData);
200        }
201        Ok(())
202    }
203
204    /// Plot the digital signal
205    pub fn plot(self) {
206        let Ok(count) = i32::try_from(self.y_data.len()) else {
207            return;
208        };
209        // Generate x data
210        let x_data: Vec<f64> = (0..self.y_data.len())
211            .map(|i| self.x_start + i as f64 * self.x_scale)
212            .collect();
213
214        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
215            sys::ImPlot_PlotDigital_doublePtr(
216                label_ptr,
217                x_data.as_ptr(),
218                self.y_data.as_ptr(),
219                count,
220                self.flags.bits() as i32,
221                0,
222                std::mem::size_of::<f64>() as i32,
223            );
224        })
225    }
226}
227
228impl<'a> PlotData for SimpleDigitalPlot<'a> {
229    fn label(&self) -> &str {
230        self.label
231    }
232
233    fn data_len(&self) -> usize {
234        self.y_data.len()
235    }
236}
237
238/// Digital plot for boolean data (true/false converted to 1.0/0.0)
239pub struct BooleanDigitalPlot<'a> {
240    label: &'a str,
241    x_data: &'a [f64],
242    y_data: &'a [bool],
243    flags: DigitalFlags,
244}
245
246impl<'a> BooleanDigitalPlot<'a> {
247    /// Create a new digital plot with boolean data
248    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [bool]) -> Self {
249        Self {
250            label,
251            x_data,
252            y_data,
253            flags: DigitalFlags::NONE,
254        }
255    }
256
257    /// Set digital flags for customization
258    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
259        self.flags = flags;
260        self
261    }
262
263    /// Validate the plot data
264    pub fn validate(&self) -> Result<(), PlotError> {
265        if self.x_data.len() != self.y_data.len() {
266            return Err(PlotError::DataLengthMismatch {
267                x_len: self.x_data.len(),
268                y_len: self.y_data.len(),
269            });
270        }
271        if self.x_data.is_empty() {
272            return Err(PlotError::EmptyData);
273        }
274        Ok(())
275    }
276
277    /// Plot the digital signal
278    pub fn plot(self) {
279        let Ok(count) = i32::try_from(self.x_data.len()) else {
280            return;
281        };
282        // Convert boolean data to f64
283        let y_data_f64: Vec<f64> = self
284            .y_data
285            .iter()
286            .map(|&b| if b { 1.0 } else { 0.0 })
287            .collect();
288
289        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
290            sys::ImPlot_PlotDigital_doublePtr(
291                label_ptr,
292                self.x_data.as_ptr(),
293                y_data_f64.as_ptr(),
294                count,
295                self.flags.bits() as i32,
296                0,
297                std::mem::size_of::<f64>() as i32,
298            );
299        })
300    }
301}
302
303impl<'a> PlotData for BooleanDigitalPlot<'a> {
304    fn label(&self) -> &str {
305        self.label
306    }
307
308    fn data_len(&self) -> usize {
309        self.x_data.len().min(self.y_data.len())
310    }
311}