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) {
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        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
140            let spec = plot_spec_with_style(
141                self.style,
142                self.flags.bits() | self.item_flags.bits(),
143                self.layout,
144            );
145            sys::ImPlot_PlotLine_doublePtrdoublePtr(
146                label_ptr,
147                self.x_data.as_ptr(),
148                self.y_data.as_ptr(),
149                count,
150                spec,
151            );
152        })
153    }
154
155    fn label(&self) -> &str {
156        self.label
157    }
158}
159
160/// Simple line plot for quick plotting without builder pattern
161pub struct SimpleLinePlot<'a> {
162    label: &'a str,
163    values: &'a [f64],
164    style: PlotItemStyle,
165    flags: LineFlags,
166    item_flags: ItemFlags,
167    x_scale: f64,
168    x_start: f64,
169}
170
171impl<'a> super::PlotItemStyled for SimpleLinePlot<'a> {
172    fn style_mut(&mut self) -> &mut PlotItemStyle {
173        &mut self.style
174    }
175}
176
177impl<'a> SimpleLinePlot<'a> {
178    /// Create a simple line plot with Y values only (X will be indices)
179    pub fn new(label: &'a str, values: &'a [f64]) -> Self {
180        Self {
181            label,
182            values,
183            style: PlotItemStyle::default(),
184            flags: LineFlags::NONE,
185            item_flags: ItemFlags::NONE,
186            x_scale: 1.0,
187            x_start: 0.0,
188        }
189    }
190
191    /// Replace the entire item style override for this line plot.
192    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
193        self.style = style;
194        self
195    }
196
197    /// Set the line color. Use the alpha channel to control line transparency.
198    pub fn with_line_color(mut self, color: [f32; 4]) -> Self {
199        self.style = self.style.with_line_color(color);
200        self
201    }
202
203    /// Set the line width in pixels.
204    pub fn with_line_weight(mut self, weight: f32) -> Self {
205        self.style = self.style.with_line_weight(weight);
206        self
207    }
208
209    /// Set the fill color used when the line is rendered with shaded fill.
210    pub fn with_fill_color(mut self, color: [f32; 4]) -> Self {
211        self.style = self.style.with_fill_color(color);
212        self
213    }
214
215    /// Set the fill alpha multiplier used for shaded regions and marker faces.
216    pub fn with_fill_alpha(mut self, alpha: f32) -> Self {
217        self.style = self.style.with_fill_alpha(alpha);
218        self
219    }
220
221    /// Set the marker type for the line plot.
222    pub fn with_marker(mut self, marker: Marker) -> Self {
223        self.style = self.style.with_marker(marker);
224        self
225    }
226
227    /// Set the marker size in pixels.
228    pub fn with_marker_size(mut self, size: f32) -> Self {
229        self.style = self.style.with_marker_size(size);
230        self
231    }
232
233    /// Set the marker outline color.
234    pub fn with_marker_line_color(mut self, color: [f32; 4]) -> Self {
235        self.style = self.style.with_marker_line_color(color);
236        self
237    }
238
239    /// Set the marker fill color.
240    pub fn with_marker_fill_color(mut self, color: [f32; 4]) -> Self {
241        self.style = self.style.with_marker_fill_color(color);
242        self
243    }
244
245    /// Set line flags for customization
246    pub fn with_flags(mut self, flags: LineFlags) -> Self {
247        self.flags = flags;
248        self
249    }
250
251    /// Set common item flags for this plot item (applies to all plot types)
252    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
253        self.item_flags = flags;
254        self
255    }
256
257    /// Set X scale factor
258    pub fn with_x_scale(mut self, scale: f64) -> Self {
259        self.x_scale = scale;
260        self
261    }
262
263    /// Set X start value
264    pub fn with_x_start(mut self, start: f64) -> Self {
265        self.x_start = start;
266        self
267    }
268}
269
270impl<'a> Plot for SimpleLinePlot<'a> {
271    fn plot(&self) {
272        if self.values.is_empty() {
273            return;
274        }
275        let Ok(count) = i32::try_from(self.values.len()) else {
276            return;
277        };
278
279        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
280            let spec = plot_spec_with_style(
281                self.style,
282                self.flags.bits() | self.item_flags.bits(),
283                PlotDataLayout::DEFAULT,
284            );
285            sys::ImPlot_PlotLine_doublePtrInt(
286                label_ptr,
287                self.values.as_ptr(),
288                count,
289                self.x_scale,
290                self.x_start,
291                spec,
292            );
293        })
294    }
295
296    fn label(&self) -> &str {
297        self.label
298    }
299}
300
301/// Convenience functions for quick line plotting
302impl<'ui> crate::PlotUi<'ui> {
303    /// Plot a line with X and Y data
304    pub fn line_plot(&self, label: &str, x_data: &[f64], y_data: &[f64]) -> Result<(), PlotError> {
305        let plot = LinePlot::new(label, x_data, y_data);
306        plot.validate()?;
307        self.bind();
308        plot.plot();
309        Ok(())
310    }
311
312    /// Plot a simple line with Y values only (X will be indices)
313    pub fn simple_line_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
314        if values.is_empty() {
315            return Err(PlotError::EmptyData);
316        }
317        let plot = SimpleLinePlot::new(label, values);
318        self.bind();
319        plot.plot();
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}