Skip to main content

dear_implot/plots/
digital.rs

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