dear_implot/plots/
stairs.rs

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