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, plot_ui: &crate::PlotUi<'_>) {
90        let Ok(count) = i32::try_from(self.x_data.len()) else {
91            return;
92        };
93        let _guard = plot_ui.bind();
94        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
95            let spec = plot_spec_with_style(
96                self.style,
97                self.flags.bits() | self.item_flags.bits(),
98                self.layout,
99            );
100            sys::ImPlot_PlotDigital_doublePtr(
101                label_ptr,
102                self.x_data.as_ptr(),
103                self.y_data.as_ptr(),
104                count,
105                spec,
106            );
107        })
108    }
109}
110
111impl<'a> PlotData for DigitalPlot<'a> {
112    fn label(&self) -> &str {
113        self.label
114    }
115
116    fn data_len(&self) -> usize {
117        self.x_data.len().min(self.y_data.len())
118    }
119}
120
121/// Digital plot for f32 data
122pub struct DigitalPlotF32<'a> {
123    label: &'a str,
124    x_data: &'a [f32],
125    y_data: &'a [f32],
126    style: PlotItemStyle,
127    flags: DigitalFlags,
128    item_flags: ItemFlags,
129}
130
131impl<'a> super::PlotItemStyled for DigitalPlotF32<'a> {
132    fn style_mut(&mut self) -> &mut PlotItemStyle {
133        &mut self.style
134    }
135}
136
137impl<'a> DigitalPlotF32<'a> {
138    /// Create a new digital plot with f32 data
139    pub fn new(label: &'a str, x_data: &'a [f32], y_data: &'a [f32]) -> Self {
140        Self {
141            label,
142            x_data,
143            y_data,
144            style: PlotItemStyle::default(),
145            flags: DigitalFlags::NONE,
146            item_flags: ItemFlags::NONE,
147        }
148    }
149
150    /// Set ImPlotSpec-backed style overrides for this digital plot.
151    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
152        self.style = style;
153        self
154    }
155
156    /// Set digital flags for customization
157    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
158        self.flags = flags;
159        self
160    }
161
162    /// Set common item flags for this plot item (applies to all plot types)
163    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
164        self.item_flags = flags;
165        self
166    }
167
168    /// Validate the plot data
169    pub fn validate(&self) -> Result<(), PlotError> {
170        if self.x_data.len() != self.y_data.len() {
171            return Err(PlotError::DataLengthMismatch {
172                x_len: self.x_data.len(),
173                y_len: self.y_data.len(),
174            });
175        }
176        if self.x_data.is_empty() {
177            return Err(PlotError::EmptyData);
178        }
179        Ok(())
180    }
181
182    /// Plot the digital signal
183    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
184        let Ok(count) = i32::try_from(self.x_data.len()) else {
185            return;
186        };
187        let _guard = plot_ui.bind();
188        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
189            let spec = plot_spec_with_style(
190                self.style,
191                self.flags.bits() | self.item_flags.bits(),
192                PlotDataLayout::DEFAULT,
193            );
194            sys::ImPlot_PlotDigital_FloatPtr(
195                label_ptr,
196                self.x_data.as_ptr(),
197                self.y_data.as_ptr(),
198                count,
199                spec,
200            );
201        })
202    }
203}
204
205impl<'a> PlotData for DigitalPlotF32<'a> {
206    fn label(&self) -> &str {
207        self.label
208    }
209
210    fn data_len(&self) -> usize {
211        self.x_data.len().min(self.y_data.len())
212    }
213}
214
215/// Simple digital plot for single array data (y values only, x is auto-generated)
216pub struct SimpleDigitalPlot<'a> {
217    label: &'a str,
218    y_data: &'a [f64],
219    style: PlotItemStyle,
220    flags: DigitalFlags,
221    item_flags: ItemFlags,
222    x_scale: f64,
223    x_start: f64,
224}
225
226impl<'a> super::PlotItemStyled for SimpleDigitalPlot<'a> {
227    fn style_mut(&mut self) -> &mut PlotItemStyle {
228        &mut self.style
229    }
230}
231
232impl<'a> SimpleDigitalPlot<'a> {
233    /// Create a new simple digital plot with only y data
234    pub fn new(label: &'a str, y_data: &'a [f64]) -> Self {
235        Self {
236            label,
237            y_data,
238            style: PlotItemStyle::default(),
239            flags: DigitalFlags::NONE,
240            item_flags: ItemFlags::NONE,
241            x_scale: 1.0,
242            x_start: 0.0,
243        }
244    }
245
246    /// Set the x scale (spacing between points)
247    pub fn with_x_scale(mut self, x_scale: f64) -> Self {
248        self.x_scale = x_scale;
249        self
250    }
251
252    /// Set the x start value
253    pub fn with_x_start(mut self, x_start: f64) -> Self {
254        self.x_start = x_start;
255        self
256    }
257
258    /// Set digital flags
259    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
260        self.flags = flags;
261        self
262    }
263
264    /// Set ImPlotSpec-backed style overrides for this digital plot.
265    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
266        self.style = style;
267        self
268    }
269
270    /// Set common item flags for this plot item (applies to all plot types)
271    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
272        self.item_flags = flags;
273        self
274    }
275
276    /// Validate the plot data
277    pub fn validate(&self) -> Result<(), PlotError> {
278        if self.y_data.is_empty() {
279            return Err(PlotError::EmptyData);
280        }
281        Ok(())
282    }
283
284    /// Plot the digital signal
285    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
286        let Ok(count) = i32::try_from(self.y_data.len()) else {
287            return;
288        };
289        // Generate x data
290        let x_data: Vec<f64> = (0..self.y_data.len())
291            .map(|i| self.x_start + i as f64 * self.x_scale)
292            .collect();
293
294        let _guard = plot_ui.bind();
295        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
296            let spec = plot_spec_with_style(
297                self.style,
298                self.flags.bits() | self.item_flags.bits(),
299                PlotDataLayout::DEFAULT,
300            );
301            sys::ImPlot_PlotDigital_doublePtr(
302                label_ptr,
303                x_data.as_ptr(),
304                self.y_data.as_ptr(),
305                count,
306                spec,
307            );
308        })
309    }
310}
311
312impl<'a> PlotData for SimpleDigitalPlot<'a> {
313    fn label(&self) -> &str {
314        self.label
315    }
316
317    fn data_len(&self) -> usize {
318        self.y_data.len()
319    }
320}
321
322/// Digital plot for boolean data (true/false converted to 1.0/0.0)
323pub struct BooleanDigitalPlot<'a> {
324    label: &'a str,
325    x_data: &'a [f64],
326    y_data: &'a [bool],
327    style: PlotItemStyle,
328    flags: DigitalFlags,
329    item_flags: ItemFlags,
330}
331
332impl<'a> super::PlotItemStyled for BooleanDigitalPlot<'a> {
333    fn style_mut(&mut self) -> &mut PlotItemStyle {
334        &mut self.style
335    }
336}
337
338impl<'a> BooleanDigitalPlot<'a> {
339    /// Create a new digital plot with boolean data
340    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [bool]) -> Self {
341        Self {
342            label,
343            x_data,
344            y_data,
345            style: PlotItemStyle::default(),
346            flags: DigitalFlags::NONE,
347            item_flags: ItemFlags::NONE,
348        }
349    }
350
351    /// Set ImPlotSpec-backed style overrides for this digital plot.
352    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
353        self.style = style;
354        self
355    }
356
357    /// Set digital flags for customization
358    pub fn with_flags(mut self, flags: DigitalFlags) -> Self {
359        self.flags = flags;
360        self
361    }
362
363    /// Set common item flags for this plot item (applies to all plot types)
364    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
365        self.item_flags = flags;
366        self
367    }
368
369    /// Validate the plot data
370    pub fn validate(&self) -> Result<(), PlotError> {
371        if self.x_data.len() != self.y_data.len() {
372            return Err(PlotError::DataLengthMismatch {
373                x_len: self.x_data.len(),
374                y_len: self.y_data.len(),
375            });
376        }
377        if self.x_data.is_empty() {
378            return Err(PlotError::EmptyData);
379        }
380        Ok(())
381    }
382
383    /// Plot the digital signal
384    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
385        let Ok(count) = i32::try_from(self.x_data.len()) else {
386            return;
387        };
388        // Convert boolean data to f64
389        let y_data_f64: Vec<f64> = self
390            .y_data
391            .iter()
392            .map(|&b| if b { 1.0 } else { 0.0 })
393            .collect();
394
395        let _guard = plot_ui.bind();
396        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
397            let spec = plot_spec_with_style(
398                self.style,
399                self.flags.bits() | self.item_flags.bits(),
400                PlotDataLayout::DEFAULT,
401            );
402            sys::ImPlot_PlotDigital_doublePtr(
403                label_ptr,
404                self.x_data.as_ptr(),
405                y_data_f64.as_ptr(),
406                count,
407                spec,
408            );
409        })
410    }
411}
412
413impl<'a> PlotData for BooleanDigitalPlot<'a> {
414    fn label(&self) -> &str {
415        self.label
416    }
417
418    fn data_len(&self) -> usize {
419        self.x_data.len().min(self.y_data.len())
420    }
421}