Skip to main content

dear_implot/plots/
stairs.rs

1//! Stairs 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::{ItemFlags, StairsFlags, sys};
8
9/// Builder for stairs plots with extensive customization options
10pub struct StairsPlot<'a> {
11    label: &'a str,
12    x_data: &'a [f64],
13    y_data: &'a [f64],
14    style: PlotItemStyle,
15    flags: StairsFlags,
16    item_flags: ItemFlags,
17    offset: i32,
18    stride: i32,
19}
20
21impl<'a> super::PlotItemStyled for StairsPlot<'a> {
22    fn style_mut(&mut self) -> &mut PlotItemStyle {
23        &mut self.style
24    }
25}
26
27impl<'a> StairsPlot<'a> {
28    /// Create a new stairs plot with the given label and data
29    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
30        Self {
31            label,
32            x_data,
33            y_data,
34            style: PlotItemStyle::default(),
35            flags: StairsFlags::NONE,
36            item_flags: ItemFlags::NONE,
37            offset: 0,
38            stride: std::mem::size_of::<f64>() as i32,
39        }
40    }
41
42    /// Set stairs flags for customization
43    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
44        self.flags = flags;
45        self
46    }
47
48    /// Set common item flags for this plot item (applies to all plot types)
49    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
50        self.item_flags = flags;
51        self
52    }
53
54    /// Enable pre-step mode (step before the point instead of after)
55    pub fn pre_step(mut self) -> Self {
56        self.flags |= StairsFlags::PRE_STEP;
57        self
58    }
59
60    /// Enable shaded stairs (fill area under stairs)
61    pub fn shaded(mut self) -> Self {
62        self.flags |= StairsFlags::SHADED;
63        self
64    }
65
66    /// Set data offset for partial plotting
67    pub fn with_offset(mut self, offset: i32) -> Self {
68        self.offset = offset;
69        self
70    }
71
72    /// Set data stride for non-contiguous data
73    pub fn with_stride(mut self, stride: i32) -> Self {
74        self.stride = stride;
75        self
76    }
77
78    /// Validate the plot data
79    pub fn validate(&self) -> Result<(), PlotError> {
80        validate_data_lengths(self.x_data, self.y_data)
81    }
82
83    /// Plot the stairs
84    pub fn plot(self) {
85        let Ok(count) = i32::try_from(self.x_data.len()) else {
86            return;
87        };
88        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
89            let spec = plot_spec_with_style(
90                self.style,
91                self.flags.bits() | self.item_flags.bits(),
92                self.offset,
93                self.stride,
94            );
95            sys::ImPlot_PlotStairs_doublePtrdoublePtr(
96                label_ptr,
97                self.x_data.as_ptr(),
98                self.y_data.as_ptr(),
99                count,
100                spec,
101            );
102        })
103    }
104}
105
106impl<'a> PlotData for StairsPlot<'a> {
107    fn label(&self) -> &str {
108        self.label
109    }
110
111    fn data_len(&self) -> usize {
112        self.x_data.len().min(self.y_data.len())
113    }
114}
115
116/// Simple stairs plot for f32 data
117pub struct StairsPlotF32<'a> {
118    label: &'a str,
119    x_data: &'a [f32],
120    y_data: &'a [f32],
121    style: PlotItemStyle,
122    flags: StairsFlags,
123    item_flags: ItemFlags,
124}
125
126impl<'a> super::PlotItemStyled for StairsPlotF32<'a> {
127    fn style_mut(&mut self) -> &mut PlotItemStyle {
128        &mut self.style
129    }
130}
131
132impl<'a> StairsPlotF32<'a> {
133    /// Create a new stairs plot with f32 data
134    pub fn new(label: &'a str, x_data: &'a [f32], y_data: &'a [f32]) -> Self {
135        Self {
136            label,
137            x_data,
138            y_data,
139            style: PlotItemStyle::default(),
140            flags: StairsFlags::NONE,
141            item_flags: ItemFlags::NONE,
142        }
143    }
144
145    /// Set stairs flags for customization
146    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
147        self.flags = flags;
148        self
149    }
150
151    /// Set common item flags for this plot item (applies to all plot types)
152    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
153        self.item_flags = flags;
154        self
155    }
156
157    /// Enable pre-step mode
158    pub fn pre_step(mut self) -> Self {
159        self.flags |= StairsFlags::PRE_STEP;
160        self
161    }
162
163    /// Enable shaded stairs
164    pub fn shaded(mut self) -> Self {
165        self.flags |= StairsFlags::SHADED;
166        self
167    }
168
169    /// Validate the plot data
170    pub fn validate(&self) -> Result<(), PlotError> {
171        if self.x_data.len() != self.y_data.len() {
172            return Err(PlotError::DataLengthMismatch {
173                x_len: self.x_data.len(),
174                y_len: self.y_data.len(),
175            });
176        }
177        if self.x_data.is_empty() {
178            return Err(PlotError::EmptyData);
179        }
180        Ok(())
181    }
182
183    /// Plot the stairs
184    pub fn plot(self) {
185        let Ok(count) = i32::try_from(self.x_data.len()) else {
186            return;
187        };
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                0,
193                std::mem::size_of::<f32>() as i32,
194            );
195            sys::ImPlot_PlotStairs_FloatPtrFloatPtr(
196                label_ptr,
197                self.x_data.as_ptr(),
198                self.y_data.as_ptr(),
199                count,
200                spec,
201            );
202        })
203    }
204}
205
206impl<'a> PlotData for StairsPlotF32<'a> {
207    fn label(&self) -> &str {
208        self.label
209    }
210
211    fn data_len(&self) -> usize {
212        self.x_data.len().min(self.y_data.len())
213    }
214}
215
216/// Simple stairs plot for single array data (y values only, x is auto-generated)
217pub struct SimpleStairsPlot<'a> {
218    label: &'a str,
219    y_data: &'a [f64],
220    style: PlotItemStyle,
221    flags: StairsFlags,
222    item_flags: ItemFlags,
223    x_scale: f64,
224    x_start: f64,
225}
226
227impl<'a> super::PlotItemStyled for SimpleStairsPlot<'a> {
228    fn style_mut(&mut self) -> &mut PlotItemStyle {
229        &mut self.style
230    }
231}
232
233impl<'a> SimpleStairsPlot<'a> {
234    /// Create a new simple stairs plot with only y data
235    pub fn new(label: &'a str, y_data: &'a [f64]) -> Self {
236        Self {
237            label,
238            y_data,
239            style: PlotItemStyle::default(),
240            flags: StairsFlags::NONE,
241            item_flags: ItemFlags::NONE,
242            x_scale: 1.0,
243            x_start: 0.0,
244        }
245    }
246
247    /// Set the x scale (spacing between points)
248    pub fn with_x_scale(mut self, x_scale: f64) -> Self {
249        self.x_scale = x_scale;
250        self
251    }
252
253    /// Set the x start value
254    pub fn with_x_start(mut self, x_start: f64) -> Self {
255        self.x_start = x_start;
256        self
257    }
258
259    /// Set stairs flags
260    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
261        self.flags = flags;
262        self
263    }
264
265    /// Set common item flags for this plot item (applies to all plot types)
266    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
267        self.item_flags = flags;
268        self
269    }
270
271    /// Enable pre-step mode
272    pub fn pre_step(mut self) -> Self {
273        self.flags |= StairsFlags::PRE_STEP;
274        self
275    }
276
277    /// Enable shaded stairs
278    pub fn shaded(mut self) -> Self {
279        self.flags |= StairsFlags::SHADED;
280        self
281    }
282
283    /// Validate the plot data
284    pub fn validate(&self) -> Result<(), PlotError> {
285        if self.y_data.is_empty() {
286            return Err(PlotError::EmptyData);
287        }
288        Ok(())
289    }
290
291    /// Plot the stairs
292    pub fn plot(self) {
293        let Ok(count) = i32::try_from(self.y_data.len()) else {
294            return;
295        };
296        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
297            let spec = plot_spec_with_style(
298                self.style,
299                self.flags.bits() | self.item_flags.bits(),
300                0,
301                std::mem::size_of::<f64>() as i32,
302            );
303            sys::ImPlot_PlotStairs_doublePtrInt(
304                label_ptr,
305                self.y_data.as_ptr(),
306                count,
307                self.x_scale,
308                self.x_start,
309                spec,
310            );
311        })
312    }
313}
314
315impl<'a> PlotData for SimpleStairsPlot<'a> {
316    fn label(&self) -> &str {
317        self.label
318    }
319
320    fn data_len(&self) -> usize {
321        self.y_data.len()
322    }
323}