Skip to main content

dear_implot/plots/
stairs.rs

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