Skip to main content

dear_implot/plots/
digital.rs

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