dear_implot/plots/
stairs.rs

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