dear_implot/plots/
shaded.rs

1//! Shaded area plot implementation
2
3use super::{Plot, PlotError, safe_cstring, validate_data_lengths};
4use crate::{ShadedFlags, sys};
5
6/// Builder for shaded area plots
7pub struct ShadedPlot<'a> {
8    label: &'a str,
9    x_data: &'a [f64],
10    y_data: &'a [f64],
11    y_ref: f64,
12    flags: ShadedFlags,
13    offset: i32,
14    stride: i32,
15}
16
17impl<'a> ShadedPlot<'a> {
18    /// Create a new shaded plot between a line and a reference Y value
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            y_ref: 0.0, // Default reference line at Y=0
25            flags: ShadedFlags::NONE,
26            offset: 0,
27            stride: std::mem::size_of::<f64>() as i32,
28        }
29    }
30
31    /// Set the reference Y value for shading
32    /// The area will be filled between the line and this Y value
33    pub fn with_y_ref(mut self, y_ref: f64) -> Self {
34        self.y_ref = y_ref;
35        self
36    }
37
38    /// Set shaded flags for customization
39    pub fn with_flags(mut self, flags: ShadedFlags) -> Self {
40        self.flags = flags;
41        self
42    }
43
44    /// Set data offset for partial plotting
45    pub fn with_offset(mut self, offset: i32) -> Self {
46        self.offset = offset;
47        self
48    }
49
50    /// Set data stride for non-contiguous data
51    pub fn with_stride(mut self, stride: i32) -> Self {
52        self.stride = stride;
53        self
54    }
55
56    /// Validate the plot data
57    pub fn validate(&self) -> Result<(), PlotError> {
58        validate_data_lengths(self.x_data, self.y_data)
59    }
60}
61
62impl<'a> Plot for ShadedPlot<'a> {
63    fn plot(&self) {
64        if self.validate().is_err() {
65            return;
66        }
67
68        let label_cstr = safe_cstring(self.label);
69
70        unsafe {
71            sys::ImPlot_PlotShaded_doublePtrdoublePtrInt(
72                label_cstr.as_ptr(),
73                self.x_data.as_ptr(),
74                self.y_data.as_ptr(),
75                self.x_data.len() as i32,
76                self.y_ref,
77                self.flags.bits() as sys::ImPlotShadedFlags,
78                self.offset,
79                self.stride,
80            );
81        }
82    }
83
84    fn label(&self) -> &str {
85        self.label
86    }
87}
88
89/// Builder for shaded area plots between two lines
90pub struct ShadedBetweenPlot<'a> {
91    label: &'a str,
92    x_data: &'a [f64],
93    y1_data: &'a [f64],
94    y2_data: &'a [f64],
95    flags: ShadedFlags,
96}
97
98impl<'a> ShadedBetweenPlot<'a> {
99    /// Create a new shaded plot between two lines
100    pub fn new(label: &'a str, x_data: &'a [f64], y1_data: &'a [f64], y2_data: &'a [f64]) -> Self {
101        Self {
102            label,
103            x_data,
104            y1_data,
105            y2_data,
106            flags: ShadedFlags::NONE,
107        }
108    }
109
110    /// Set shaded flags for customization
111    pub fn with_flags(mut self, flags: ShadedFlags) -> Self {
112        self.flags = flags;
113        self
114    }
115
116    /// Validate the plot data
117    pub fn validate(&self) -> Result<(), PlotError> {
118        validate_data_lengths(self.x_data, self.y1_data)?;
119        validate_data_lengths(self.x_data, self.y2_data)?;
120        Ok(())
121    }
122}
123
124impl<'a> Plot for ShadedBetweenPlot<'a> {
125    fn plot(&self) {
126        if self.validate().is_err() {
127            return;
128        }
129
130        let label_cstr = safe_cstring(self.label);
131
132        // Note: This would require a different wrapper function for shaded between two lines
133        // For now, we'll use the single line version with the first Y data
134        unsafe {
135            sys::ImPlot_PlotShaded_doublePtrdoublePtrdoublePtr(
136                label_cstr.as_ptr(),
137                self.x_data.as_ptr(),
138                self.y1_data.as_ptr(),
139                self.y2_data.as_ptr(),
140                self.x_data.len() as i32,
141                self.flags.bits() as sys::ImPlotShadedFlags,
142                0,
143                std::mem::size_of::<f64>() as i32,
144            );
145        }
146    }
147
148    fn label(&self) -> &str {
149        self.label
150    }
151}
152
153/// Simple shaded plot for quick plotting without builder pattern
154pub struct SimpleShadedPlot<'a> {
155    label: &'a str,
156    values: &'a [f64],
157    y_ref: f64,
158    x_scale: f64,
159    x_start: f64,
160}
161
162impl<'a> SimpleShadedPlot<'a> {
163    /// Create a simple shaded plot with Y values only (X will be indices)
164    pub fn new(label: &'a str, values: &'a [f64]) -> Self {
165        Self {
166            label,
167            values,
168            y_ref: 0.0,
169            x_scale: 1.0,
170            x_start: 0.0,
171        }
172    }
173
174    /// Set the reference Y value for shading
175    pub fn with_y_ref(mut self, y_ref: f64) -> Self {
176        self.y_ref = y_ref;
177        self
178    }
179
180    /// Set X scale factor
181    pub fn with_x_scale(mut self, scale: f64) -> Self {
182        self.x_scale = scale;
183        self
184    }
185
186    /// Set X start value
187    pub fn with_x_start(mut self, start: f64) -> Self {
188        self.x_start = start;
189        self
190    }
191}
192
193impl<'a> Plot for SimpleShadedPlot<'a> {
194    fn plot(&self) {
195        if self.values.is_empty() {
196            return;
197        }
198
199        let label_cstr = safe_cstring(self.label);
200
201        // Create temporary X data
202        let x_data: Vec<f64> = (0..self.values.len())
203            .map(|i| self.x_start + i as f64 * self.x_scale)
204            .collect();
205
206        unsafe {
207            sys::ImPlot_PlotShaded_doublePtrdoublePtrInt(
208                label_cstr.as_ptr(),
209                x_data.as_ptr(),
210                self.values.as_ptr(),
211                self.values.len() as i32,
212                self.y_ref,
213                0, // flags
214                0,
215                std::mem::size_of::<f64>() as i32,
216            );
217        }
218    }
219
220    fn label(&self) -> &str {
221        self.label
222    }
223}
224
225/// Convenience functions for quick shaded plotting
226impl<'ui> crate::PlotUi<'ui> {
227    /// Plot a shaded area between a line and Y=0
228    pub fn shaded_plot(
229        &self,
230        label: &str,
231        x_data: &[f64],
232        y_data: &[f64],
233    ) -> Result<(), PlotError> {
234        let plot = ShadedPlot::new(label, x_data, y_data);
235        plot.validate()?;
236        plot.plot();
237        Ok(())
238    }
239
240    /// Plot a shaded area between a line and a reference Y value
241    pub fn shaded_plot_with_ref(
242        &self,
243        label: &str,
244        x_data: &[f64],
245        y_data: &[f64],
246        y_ref: f64,
247    ) -> Result<(), PlotError> {
248        let plot = ShadedPlot::new(label, x_data, y_data).with_y_ref(y_ref);
249        plot.validate()?;
250        plot.plot();
251        Ok(())
252    }
253
254    /// Plot a shaded area between two lines
255    pub fn shaded_between_plot(
256        &self,
257        label: &str,
258        x_data: &[f64],
259        y1_data: &[f64],
260        y2_data: &[f64],
261    ) -> Result<(), PlotError> {
262        let plot = ShadedBetweenPlot::new(label, x_data, y1_data, y2_data);
263        plot.validate()?;
264        plot.plot();
265        Ok(())
266    }
267
268    /// Plot a simple shaded area with Y values only (X will be indices)
269    pub fn simple_shaded_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
270        if values.is_empty() {
271            return Err(PlotError::EmptyData);
272        }
273        let plot = SimpleShadedPlot::new(label, values);
274        plot.plot();
275        Ok(())
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    #[test]
284    fn test_shaded_plot_creation() {
285        let x_data = [1.0, 2.0, 3.0, 4.0];
286        let y_data = [1.0, 4.0, 2.0, 3.0];
287
288        let plot = ShadedPlot::new("test", &x_data, &y_data);
289        assert_eq!(plot.label(), "test");
290        assert!(plot.validate().is_ok());
291    }
292
293    #[test]
294    fn test_shaded_plot_validation() {
295        let x_data = [1.0, 2.0, 3.0];
296        let y_data = [1.0, 4.0]; // Different length
297
298        let plot = ShadedPlot::new("test", &x_data, &y_data);
299        assert!(plot.validate().is_err());
300    }
301
302    #[test]
303    fn test_shaded_between_plot() {
304        let x_data = [1.0, 2.0, 3.0, 4.0];
305        let y1_data = [1.0, 2.0, 3.0, 4.0];
306        let y2_data = [2.0, 3.0, 4.0, 5.0];
307
308        let plot = ShadedBetweenPlot::new("test", &x_data, &y1_data, &y2_data);
309        assert_eq!(plot.label(), "test");
310        assert!(plot.validate().is_ok());
311    }
312}