Skip to main content

dear_implot/plots/
line.rs

1//! Line plot implementation
2
3use super::{
4    Plot, PlotError, PlotItemStyle, plot_spec_with_style, validate_data_lengths,
5    with_plot_str_or_empty,
6};
7use crate::{ItemFlags, LineFlags, Marker, sys};
8
9/// Builder for line plots with extensive customization options
10pub struct LinePlot<'a> {
11    label: &'a str,
12    x_data: &'a [f64],
13    y_data: &'a [f64],
14    style: PlotItemStyle,
15    flags: LineFlags,
16    item_flags: ItemFlags,
17    offset: i32,
18    stride: i32,
19}
20
21impl<'a> super::PlotItemStyled for LinePlot<'a> {
22    fn style_mut(&mut self) -> &mut PlotItemStyle {
23        &mut self.style
24    }
25}
26
27impl<'a> LinePlot<'a> {
28    /// Create a new line plot with the given label and data
29    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
30        Self {
31            label,
32            x_data,
33            y_data,
34            style: PlotItemStyle::default(),
35            flags: LineFlags::NONE,
36            item_flags: ItemFlags::NONE,
37            offset: 0,
38            stride: std::mem::size_of::<f64>() as i32,
39        }
40    }
41
42    /// Replace the entire item style override for this line plot.
43    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
44        self.style = style;
45        self
46    }
47
48    /// Set the line color. Use the alpha channel to control line transparency.
49    pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
50        self.style = self.style.with_line_color(color);
51        self
52    }
53
54    /// Set the line width in pixels.
55    pub fn with_line_weight(mut self, weight: f32) -> Self {
56        self.style = self.style.with_line_weight(weight);
57        self
58    }
59
60    /// Set the fill color used when the line is rendered with shaded fill.
61    pub fn with_fill_color(mut self, color: [f32; 4]) -> Self {
62        self.style = self.style.with_fill_color(color);
63        self
64    }
65
66    /// Set the fill alpha multiplier used for shaded regions and marker faces.
67    pub fn with_fill_alpha(mut self, alpha: f32) -> Self {
68        self.style = self.style.with_fill_alpha(alpha);
69        self
70    }
71
72    /// Set the marker type for the line plot.
73    pub fn with_marker(mut self, marker: Marker) -> Self {
74        self.style = self.style.with_marker(marker);
75        self
76    }
77
78    /// Set the marker size in pixels.
79    pub fn with_marker_size(mut self, size: f32) -> Self {
80        self.style = self.style.with_marker_size(size);
81        self
82    }
83
84    /// Set the marker outline color.
85    pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
86        self.style = self.style.with_marker_line_color(color);
87        self
88    }
89
90    /// Set the marker fill color.
91    pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
92        self.style = self.style.with_marker_fill_color(color);
93        self
94    }
95
96    /// Set line flags for customization
97    pub fn with_flags(mut self, flags: LineFlags) -> Self {
98        self.flags = flags;
99        self
100    }
101
102    /// Set common item flags for this plot item (applies to all plot types)
103    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
104        self.item_flags = flags;
105        self
106    }
107
108    /// Set data offset for partial plotting
109    pub fn with_offset(mut self, offset: i32) -> Self {
110        self.offset = offset;
111        self
112    }
113
114    /// Set data stride for non-contiguous data
115    pub fn with_stride(mut self, stride: i32) -> Self {
116        self.stride = stride;
117        self
118    }
119
120    /// Validate the plot data
121    pub fn validate(&self) -> Result<(), PlotError> {
122        validate_data_lengths(self.x_data, self.y_data)
123    }
124}
125
126impl<'a> Plot for LinePlot<'a> {
127    fn plot(&self) {
128        if self.validate().is_err() {
129            return; // Skip plotting if data is invalid
130        }
131        let Ok(count) = i32::try_from(self.x_data.len()) else {
132            return;
133        };
134
135        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
136            let spec = plot_spec_with_style(
137                self.style,
138                self.flags.bits() | self.item_flags.bits(),
139                self.offset,
140                self.stride,
141            );
142            sys::ImPlot_PlotLine_doublePtrdoublePtr(
143                label_ptr,
144                self.x_data.as_ptr(),
145                self.y_data.as_ptr(),
146                count,
147                spec,
148            );
149        })
150    }
151
152    fn label(&self) -> &str {
153        self.label
154    }
155}
156
157/// Simple line plot for quick plotting without builder pattern
158pub struct SimpleLinePlot<'a> {
159    label: &'a str,
160    values: &'a [f64],
161    style: PlotItemStyle,
162    flags: LineFlags,
163    item_flags: ItemFlags,
164    x_scale: f64,
165    x_start: f64,
166}
167
168impl<'a> super::PlotItemStyled for SimpleLinePlot<'a> {
169    fn style_mut(&mut self) -> &mut PlotItemStyle {
170        &mut self.style
171    }
172}
173
174impl<'a> SimpleLinePlot<'a> {
175    /// Create a simple line plot with Y values only (X will be indices)
176    pub fn new(label: &'a str, values: &'a [f64]) -> Self {
177        Self {
178            label,
179            values,
180            style: PlotItemStyle::default(),
181            flags: LineFlags::NONE,
182            item_flags: ItemFlags::NONE,
183            x_scale: 1.0,
184            x_start: 0.0,
185        }
186    }
187
188    /// Replace the entire item style override for this line plot.
189    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
190        self.style = style;
191        self
192    }
193
194    /// Set the line color. Use the alpha channel to control line transparency.
195    pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
196        self.style = self.style.with_line_color(color);
197        self
198    }
199
200    /// Set the line width in pixels.
201    pub fn with_line_weight(mut self, weight: f32) -> Self {
202        self.style = self.style.with_line_weight(weight);
203        self
204    }
205
206    /// Set the fill color used when the line is rendered with shaded fill.
207    pub fn with_fill_color(mut self, color: [f32; 4]) -> Self {
208        self.style = self.style.with_fill_color(color);
209        self
210    }
211
212    /// Set the fill alpha multiplier used for shaded regions and marker faces.
213    pub fn with_fill_alpha(mut self, alpha: f32) -> Self {
214        self.style = self.style.with_fill_alpha(alpha);
215        self
216    }
217
218    /// Set the marker type for the line plot.
219    pub fn with_marker(mut self, marker: Marker) -> Self {
220        self.style = self.style.with_marker(marker);
221        self
222    }
223
224    /// Set the marker size in pixels.
225    pub fn with_marker_size(mut self, size: f32) -> Self {
226        self.style = self.style.with_marker_size(size);
227        self
228    }
229
230    /// Set the marker outline color.
231    pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
232        self.style = self.style.with_marker_line_color(color);
233        self
234    }
235
236    /// Set the marker fill color.
237    pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
238        self.style = self.style.with_marker_fill_color(color);
239        self
240    }
241
242    /// Set line flags for customization
243    pub fn with_flags(mut self, flags: LineFlags) -> Self {
244        self.flags = flags;
245        self
246    }
247
248    /// Set common item flags for this plot item (applies to all plot types)
249    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
250        self.item_flags = flags;
251        self
252    }
253
254    /// Set X scale factor
255    pub fn with_x_scale(mut self, scale: f64) -> Self {
256        self.x_scale = scale;
257        self
258    }
259
260    /// Set X start value
261    pub fn with_x_start(mut self, start: f64) -> Self {
262        self.x_start = start;
263        self
264    }
265}
266
267impl<'a> Plot for SimpleLinePlot<'a> {
268    fn plot(&self) {
269        if self.values.is_empty() {
270            return;
271        }
272        let Ok(count) = i32::try_from(self.values.len()) else {
273            return;
274        };
275
276        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
277            let spec = plot_spec_with_style(
278                self.style,
279                self.flags.bits() | self.item_flags.bits(),
280                0,
281                std::mem::size_of::<f64>() as i32,
282            );
283            sys::ImPlot_PlotLine_doublePtrInt(
284                label_ptr,
285                self.values.as_ptr(),
286                count,
287                self.x_scale,
288                self.x_start,
289                spec,
290            );
291        })
292    }
293
294    fn label(&self) -> &str {
295        self.label
296    }
297}
298
299/// Convenience functions for quick line plotting
300impl<'ui> crate::PlotUi<'ui> {
301    /// Plot a line with X and Y data
302    pub fn line_plot(&self, label: &str, x_data: &[f64], y_data: &[f64]) -> Result<(), PlotError> {
303        let plot = LinePlot::new(label, x_data, y_data);
304        plot.validate()?;
305        plot.plot();
306        Ok(())
307    }
308
309    /// Plot a simple line with Y values only (X will be indices)
310    pub fn simple_line_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
311        if values.is_empty() {
312            return Err(PlotError::EmptyData);
313        }
314        let plot = SimpleLinePlot::new(label, values);
315        plot.plot();
316        Ok(())
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323
324    #[test]
325    fn test_line_plot_creation() {
326        let x_data = [1.0, 2.0, 3.0, 4.0];
327        let y_data = [1.0, 4.0, 2.0, 3.0];
328
329        let plot = LinePlot::new("test", &x_data, &y_data);
330        assert_eq!(plot.label(), "test");
331        assert!(plot.validate().is_ok());
332    }
333
334    #[test]
335    fn test_line_plot_validation() {
336        let x_data = [1.0, 2.0, 3.0];
337        let y_data = [1.0, 4.0]; // Different length
338
339        let plot = LinePlot::new("test", &x_data, &y_data);
340        assert!(plot.validate().is_err());
341    }
342
343    #[test]
344    fn test_simple_line_plot() {
345        let values = [1.0, 2.0, 3.0, 4.0];
346        let plot = SimpleLinePlot::new("test", &values)
347            .with_flags(LineFlags::LOOP)
348            .with_item_flags(ItemFlags::NO_LEGEND);
349        assert_eq!(plot.label(), "test");
350        assert_eq!(plot.flags.bits(), LineFlags::LOOP.bits());
351        assert_eq!(plot.item_flags, ItemFlags::NO_LEGEND);
352    }
353
354    #[test]
355    fn test_line_plot_style_builders() {
356        let x_data = [1.0, 2.0, 3.0, 4.0];
357        let y_data = [1.0, 4.0, 2.0, 3.0];
358
359        let plot = LinePlot::new("styled", &x_data, &y_data)
360            .with_line_color([0.1, 0.2, 0.3, 0.4])
361            .with_line_weight(2.5)
362            .with_fill_color([0.4, 0.3, 0.2, 0.1])
363            .with_fill_alpha(0.6)
364            .with_marker(Marker::Circle)
365            .with_marker_size(7.0)
366            .with_marker_line_color([0.9, 0.8, 0.7, 0.6])
367            .with_marker_fill_color([0.6, 0.7, 0.8, 0.9]);
368
369        assert_eq!(
370            plot.style.line_color,
371            Some(sys::ImVec4_c {
372                x: 0.1,
373                y: 0.2,
374                z: 0.3,
375                w: 0.4,
376            })
377        );
378        assert_eq!(plot.style.line_weight, Some(2.5));
379        assert_eq!(
380            plot.style.fill_color,
381            Some(sys::ImVec4_c {
382                x: 0.4,
383                y: 0.3,
384                z: 0.2,
385                w: 0.1,
386            })
387        );
388        assert_eq!(plot.style.fill_alpha, Some(0.6));
389        assert_eq!(plot.style.marker, Some(Marker::Circle as sys::ImPlotMarker));
390        assert_eq!(plot.style.marker_size, Some(7.0));
391        assert_eq!(
392            plot.style.marker_line_color,
393            Some(sys::ImVec4_c {
394                x: 0.9,
395                y: 0.8,
396                z: 0.7,
397                w: 0.6,
398            })
399        );
400        assert_eq!(
401            plot.style.marker_fill_color,
402            Some(sys::ImVec4_c {
403                x: 0.6,
404                y: 0.7,
405                z: 0.8,
406                w: 0.9,
407            })
408        );
409    }
410}