Skip to main content

dear_implot/plots/
stairs.rs

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