Skip to main content

dear_implot/plots/
polygon.rs

1//! Polygon 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, PolygonFlags, sys};
8
9/// Builder for polygon plots.
10pub struct PolygonPlot<'a> {
11    label: &'a str,
12    x_data: &'a [f64],
13    y_data: &'a [f64],
14    style: PlotItemStyle,
15    flags: PolygonFlags,
16    item_flags: ItemFlags,
17    layout: PlotDataLayout,
18}
19
20impl<'a> super::PlotItemStyled for PolygonPlot<'a> {
21    fn style_mut(&mut self) -> &mut PlotItemStyle {
22        &mut self.style
23    }
24}
25
26impl<'a> PolygonPlot<'a> {
27    /// Create a new polygon plot with the given label and vertices.
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: PolygonFlags::NONE,
35            item_flags: ItemFlags::NONE,
36            layout: PlotDataLayout::DEFAULT,
37        }
38    }
39
40    /// Replace the entire item style override for this polygon 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 polygon outline 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.
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.
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 polygon-specific flags.
71    pub fn with_flags(mut self, flags: PolygonFlags) -> Self {
72        self.flags = flags;
73        self
74    }
75
76    /// Set common item flags for this plot item.
77    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
78        self.item_flags = flags;
79        self
80    }
81
82    /// Set the data layout used to read polygon vertices.
83    ///
84    /// # Safety
85    ///
86    /// Every sample address computed from `layout` must refer to an initialized, properly aligned
87    /// `f64` within both coordinate allocations retained by this builder.
88    pub unsafe fn with_data_layout(mut self, layout: PlotDataLayout) -> Self {
89        self.layout = layout;
90        self
91    }
92
93    /// Set the sample-index offset used to read polygon vertices.
94    pub fn with_offset(mut self, offset: PlotDataOffset) -> Self {
95        self.layout = self.layout.with_offset(offset);
96        self
97    }
98
99    /// Set the byte stride used to read polygon vertices.
100    ///
101    /// # Safety
102    ///
103    /// Every strided sample read must remain initialized, aligned, and within both coordinate
104    /// allocations retained by this builder.
105    pub unsafe fn with_stride(mut self, stride: PlotDataStride) -> Self {
106        self.layout = self.layout.with_stride(stride);
107        self
108    }
109
110    /// Validate the polygon data.
111    pub fn validate(&self) -> Result<(), PlotError> {
112        validate_data_lengths(self.x_data, self.y_data)
113    }
114}
115
116impl<'a> Plot for PolygonPlot<'a> {
117    fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
118        if self.validate().is_err() {
119            return;
120        }
121        let Ok(count) = i32::try_from(self.x_data.len()) else {
122            return;
123        };
124
125        plot_ui.with_bound_context(|| {
126            with_plot_str_or_empty(self.label, |label_ptr| unsafe {
127                let spec = plot_spec_with_style(
128                    self.style,
129                    self.flags.bits() | self.item_flags.bits(),
130                    self.layout,
131                );
132                sys::ImPlot_PlotPolygon_doublePtr(
133                    label_ptr,
134                    self.x_data.as_ptr(),
135                    self.y_data.as_ptr(),
136                    count,
137                    spec,
138                );
139            })
140        })
141    }
142
143    fn label(&self) -> &str {
144        self.label
145    }
146}
147
148/// Convenience functions for quick polygon plotting.
149impl<'ui> crate::PlotUi<'ui> {
150    /// Plot a polygon with X and Y vertex data.
151    pub fn polygon_plot(
152        &self,
153        label: &str,
154        x_data: &[f64],
155        y_data: &[f64],
156    ) -> Result<(), PlotError> {
157        let plot = PolygonPlot::new(label, x_data, y_data);
158        plot.validate()?;
159        plot.plot(self);
160        Ok(())
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167
168    #[test]
169    fn polygon_plot_creation() {
170        let x_data = [0.0, 1.0, 1.0, 0.0];
171        let y_data = [0.0, 0.0, 1.0, 1.0];
172
173        let plot = PolygonPlot::new("poly", &x_data, &y_data);
174        assert_eq!(plot.label(), "poly");
175        assert!(plot.validate().is_ok());
176    }
177
178    #[test]
179    fn polygon_plot_validation() {
180        let x_data = [0.0, 1.0, 1.0];
181        let y_data = [0.0, 0.0];
182
183        let plot = PolygonPlot::new("poly", &x_data, &y_data);
184        assert!(plot.validate().is_err());
185    }
186}