Skip to main content

dear_implot/plots/
digital.rs

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