Skip to main content

dear_implot/plots/
line.rs

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