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, plot_ui: &crate::PlotUi<'_>) {
89        let Ok(count) = i32::try_from(self.x_data.len()) else {
90            return;
91        };
92        let _guard = plot_ui.bind();
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_PlotStairs_doublePtrdoublePtr(
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 StairsPlot<'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/// Simple stairs plot for f32 data
121pub struct StairsPlotF32<'a> {
122    label: &'a str,
123    x_data: &'a [f32],
124    y_data: &'a [f32],
125    style: PlotItemStyle,
126    flags: StairsFlags,
127    item_flags: ItemFlags,
128}
129
130impl<'a> super::PlotItemStyled for StairsPlotF32<'a> {
131    fn style_mut(&mut self) -> &mut PlotItemStyle {
132        &mut self.style
133    }
134}
135
136impl<'a> StairsPlotF32<'a> {
137    /// Create a new stairs 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: StairsFlags::NONE,
145            item_flags: ItemFlags::NONE,
146        }
147    }
148
149    /// Set stairs flags for customization
150    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
151        self.flags = flags;
152        self
153    }
154
155    /// Set common item flags for this plot item (applies to all plot types)
156    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
157        self.item_flags = flags;
158        self
159    }
160
161    /// Enable pre-step mode
162    pub fn pre_step(mut self) -> Self {
163        self.flags |= StairsFlags::PRE_STEP;
164        self
165    }
166
167    /// Enable shaded stairs
168    pub fn shaded(mut self) -> Self {
169        self.flags |= StairsFlags::SHADED;
170        self
171    }
172
173    /// Validate the plot data
174    pub fn validate(&self) -> Result<(), PlotError> {
175        if self.x_data.len() != self.y_data.len() {
176            return Err(PlotError::DataLengthMismatch {
177                x_len: self.x_data.len(),
178                y_len: self.y_data.len(),
179            });
180        }
181        if self.x_data.is_empty() {
182            return Err(PlotError::EmptyData);
183        }
184        Ok(())
185    }
186
187    /// Plot the stairs
188    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
189        let Ok(count) = i32::try_from(self.x_data.len()) else {
190            return;
191        };
192        let _guard = plot_ui.bind();
193        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
194            let spec = plot_spec_with_style(
195                self.style,
196                self.flags.bits() | self.item_flags.bits(),
197                PlotDataLayout::DEFAULT,
198            );
199            sys::ImPlot_PlotStairs_FloatPtrFloatPtr(
200                label_ptr,
201                self.x_data.as_ptr(),
202                self.y_data.as_ptr(),
203                count,
204                spec,
205            );
206        })
207    }
208}
209
210impl<'a> PlotData for StairsPlotF32<'a> {
211    fn label(&self) -> &str {
212        self.label
213    }
214
215    fn data_len(&self) -> usize {
216        self.x_data.len().min(self.y_data.len())
217    }
218}
219
220/// Simple stairs plot for single array data (y values only, x is auto-generated)
221pub struct SimpleStairsPlot<'a> {
222    label: &'a str,
223    y_data: &'a [f64],
224    style: PlotItemStyle,
225    flags: StairsFlags,
226    item_flags: ItemFlags,
227    x_scale: f64,
228    x_start: f64,
229}
230
231impl<'a> super::PlotItemStyled for SimpleStairsPlot<'a> {
232    fn style_mut(&mut self) -> &mut PlotItemStyle {
233        &mut self.style
234    }
235}
236
237impl<'a> SimpleStairsPlot<'a> {
238    /// Create a new simple stairs plot with only y data
239    pub fn new(label: &'a str, y_data: &'a [f64]) -> Self {
240        Self {
241            label,
242            y_data,
243            style: PlotItemStyle::default(),
244            flags: StairsFlags::NONE,
245            item_flags: ItemFlags::NONE,
246            x_scale: 1.0,
247            x_start: 0.0,
248        }
249    }
250
251    /// Set the x scale (spacing between points)
252    pub fn with_x_scale(mut self, x_scale: f64) -> Self {
253        self.x_scale = x_scale;
254        self
255    }
256
257    /// Set the x start value
258    pub fn with_x_start(mut self, x_start: f64) -> Self {
259        self.x_start = x_start;
260        self
261    }
262
263    /// Set stairs flags
264    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
265        self.flags = flags;
266        self
267    }
268
269    /// Set common item flags for this plot item (applies to all plot types)
270    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
271        self.item_flags = flags;
272        self
273    }
274
275    /// Enable pre-step mode
276    pub fn pre_step(mut self) -> Self {
277        self.flags |= StairsFlags::PRE_STEP;
278        self
279    }
280
281    /// Enable shaded stairs
282    pub fn shaded(mut self) -> Self {
283        self.flags |= StairsFlags::SHADED;
284        self
285    }
286
287    /// Validate the plot data
288    pub fn validate(&self) -> Result<(), PlotError> {
289        if self.y_data.is_empty() {
290            return Err(PlotError::EmptyData);
291        }
292        Ok(())
293    }
294
295    /// Plot the stairs
296    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
297        let Ok(count) = i32::try_from(self.y_data.len()) else {
298            return;
299        };
300        let _guard = plot_ui.bind();
301        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
302            let spec = plot_spec_with_style(
303                self.style,
304                self.flags.bits() | self.item_flags.bits(),
305                PlotDataLayout::DEFAULT,
306            );
307            sys::ImPlot_PlotStairs_doublePtrInt(
308                label_ptr,
309                self.y_data.as_ptr(),
310                count,
311                self.x_scale,
312                self.x_start,
313                spec,
314            );
315        })
316    }
317}
318
319impl<'a> PlotData for SimpleStairsPlot<'a> {
320    fn label(&self) -> &str {
321        self.label
322    }
323
324    fn data_len(&self) -> usize {
325        self.y_data.len()
326    }
327}