dear_implot/plots/
pie.rs

1//! Pie chart plot implementation
2
3use super::{Plot, PlotError, with_plot_str_slice_with_opt};
4use crate::PieChartFlags;
5use crate::sys;
6
7/// Builder for pie chart plots
8pub struct PieChartPlot<'a> {
9    label_ids: Vec<&'a str>,
10    values: &'a [f64],
11    center_x: f64,
12    center_y: f64,
13    radius: f64,
14    label_fmt: Option<&'a str>,
15    angle0: f64,
16    flags: PieChartFlags,
17}
18
19impl<'a> PieChartPlot<'a> {
20    /// Create a new pie chart plot
21    ///
22    /// # Arguments
23    /// * `label_ids` - Labels for each slice of the pie
24    /// * `values` - Values for each slice
25    /// * `center_x` - X coordinate of the pie center in plot units
26    /// * `center_y` - Y coordinate of the pie center in plot units
27    /// * `radius` - Radius of the pie in plot units
28    pub fn new(
29        label_ids: Vec<&'a str>,
30        values: &'a [f64],
31        center_x: f64,
32        center_y: f64,
33        radius: f64,
34    ) -> Self {
35        Self {
36            label_ids,
37            values,
38            center_x,
39            center_y,
40            radius,
41            label_fmt: Some("%.1f"),
42            angle0: 90.0, // Start angle in degrees
43            flags: PieChartFlags::NONE,
44        }
45    }
46
47    /// Set the label format for slice values (e.g., "%.1f", "%.0f%%")
48    /// Set to None to disable labels
49    pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
50        self.label_fmt = fmt;
51        self
52    }
53
54    /// Set the starting angle in degrees (default: 90.0)
55    pub fn with_start_angle(mut self, angle: f64) -> Self {
56        self.angle0 = angle;
57        self
58    }
59
60    /// Set pie chart flags for customization
61    pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
62        self.flags = flags;
63        self
64    }
65
66    /// Normalize the pie chart values (force full circle even if sum < 1.0)
67    pub fn normalize(mut self) -> Self {
68        self.flags |= PieChartFlags::NORMALIZE;
69        self
70    }
71
72    /// Ignore hidden slices when drawing (as if they were not there)
73    pub fn ignore_hidden(mut self) -> Self {
74        self.flags |= PieChartFlags::IGNORE_HIDDEN;
75        self
76    }
77
78    /// Enable exploding effect for legend-hovered slices
79    pub fn exploding(mut self) -> Self {
80        self.flags |= PieChartFlags::EXPLODING;
81        self
82    }
83
84    /// Validate the plot data
85    pub fn validate(&self) -> Result<(), PlotError> {
86        if self.values.is_empty() {
87            return Err(PlotError::EmptyData);
88        }
89
90        if self.label_ids.len() != self.values.len() {
91            return Err(PlotError::DataLengthMismatch {
92                x_len: self.label_ids.len(),
93                y_len: self.values.len(),
94            });
95        }
96
97        if self.radius <= 0.0 {
98            return Err(PlotError::InvalidData(
99                "Radius must be positive".to_string(),
100            ));
101        }
102
103        // Check for negative values
104        if self.values.iter().any(|&v| v < 0.0) {
105            return Err(PlotError::InvalidData(
106                "Pie chart values cannot be negative".to_string(),
107            ));
108        }
109
110        Ok(())
111    }
112}
113
114impl<'a> Plot for PieChartPlot<'a> {
115    fn plot(&self) {
116        if self.validate().is_err() {
117            return;
118        }
119        let Ok(count) = i32::try_from(self.values.len()) else {
120            return;
121        };
122        with_plot_str_slice_with_opt(
123            &self.label_ids,
124            self.label_fmt,
125            |label_ptrs, label_fmt_ptr| unsafe {
126                sys::ImPlot_PlotPieChart_doublePtrStr(
127                    label_ptrs.as_ptr(),
128                    self.values.as_ptr(),
129                    count,
130                    self.center_x,
131                    self.center_y,
132                    self.radius,
133                    label_fmt_ptr,
134                    self.angle0,
135                    self.flags.bits() as i32,
136                );
137            },
138        )
139    }
140
141    fn label(&self) -> &str {
142        "PieChart" // Pie charts don't have a single label
143    }
144}
145
146/// Float version of pie chart for better performance with f32 data
147pub struct PieChartPlotF32<'a> {
148    label_ids: Vec<&'a str>,
149    values: &'a [f32],
150    center_x: f64,
151    center_y: f64,
152    radius: f64,
153    label_fmt: Option<&'a str>,
154    angle0: f64,
155    flags: PieChartFlags,
156}
157
158impl<'a> PieChartPlotF32<'a> {
159    /// Create a new f32 pie chart plot
160    pub fn new(
161        label_ids: Vec<&'a str>,
162        values: &'a [f32],
163        center_x: f64,
164        center_y: f64,
165        radius: f64,
166    ) -> Self {
167        Self {
168            label_ids,
169            values,
170            center_x,
171            center_y,
172            radius,
173            label_fmt: Some("%.1f"),
174            angle0: 90.0,
175            flags: PieChartFlags::NONE,
176        }
177    }
178
179    /// Set the label format for slice values
180    pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
181        self.label_fmt = fmt;
182        self
183    }
184
185    /// Set the starting angle in degrees
186    pub fn with_start_angle(mut self, angle: f64) -> Self {
187        self.angle0 = angle;
188        self
189    }
190
191    /// Set pie chart flags for customization
192    pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
193        self.flags = flags;
194        self
195    }
196
197    /// Normalize the pie chart values
198    pub fn normalize(mut self) -> Self {
199        self.flags |= PieChartFlags::NORMALIZE;
200        self
201    }
202
203    /// Ignore hidden slices when drawing
204    pub fn ignore_hidden(mut self) -> Self {
205        self.flags |= PieChartFlags::IGNORE_HIDDEN;
206        self
207    }
208
209    /// Enable exploding effect for legend-hovered slices
210    pub fn exploding(mut self) -> Self {
211        self.flags |= PieChartFlags::EXPLODING;
212        self
213    }
214
215    /// Validate the plot data
216    pub fn validate(&self) -> Result<(), PlotError> {
217        if self.values.is_empty() {
218            return Err(PlotError::EmptyData);
219        }
220
221        if self.label_ids.len() != self.values.len() {
222            return Err(PlotError::DataLengthMismatch {
223                x_len: self.label_ids.len(),
224                y_len: self.values.len(),
225            });
226        }
227
228        if self.radius <= 0.0 {
229            return Err(PlotError::InvalidData(
230                "Radius must be positive".to_string(),
231            ));
232        }
233
234        if self.values.iter().any(|&v| v < 0.0) {
235            return Err(PlotError::InvalidData(
236                "Pie chart values cannot be negative".to_string(),
237            ));
238        }
239
240        Ok(())
241    }
242}
243
244impl<'a> Plot for PieChartPlotF32<'a> {
245    fn plot(&self) {
246        if self.validate().is_err() {
247            return;
248        }
249        let Ok(count) = i32::try_from(self.values.len()) else {
250            return;
251        };
252        with_plot_str_slice_with_opt(
253            &self.label_ids,
254            self.label_fmt,
255            |label_ptrs, label_fmt_ptr| unsafe {
256                sys::ImPlot_PlotPieChart_FloatPtrStr(
257                    label_ptrs.as_ptr(),
258                    self.values.as_ptr(),
259                    count,
260                    self.center_x,
261                    self.center_y,
262                    self.radius,
263                    label_fmt_ptr,
264                    self.angle0,
265                    self.flags.bits() as i32,
266                );
267            },
268        )
269    }
270
271    fn label(&self) -> &str {
272        "PieChart"
273    }
274}
275
276/// Convenience functions for quick pie chart plotting
277impl<'ui> crate::PlotUi<'ui> {
278    /// Plot a pie chart with f64 data
279    pub fn pie_chart_plot(
280        &self,
281        label_ids: Vec<&str>,
282        values: &[f64],
283        center_x: f64,
284        center_y: f64,
285        radius: f64,
286    ) -> Result<(), PlotError> {
287        let plot = PieChartPlot::new(label_ids, values, center_x, center_y, radius);
288        plot.validate()?;
289        plot.plot();
290        Ok(())
291    }
292
293    /// Plot a pie chart with f32 data
294    pub fn pie_chart_plot_f32(
295        &self,
296        label_ids: Vec<&str>,
297        values: &[f32],
298        center_x: f64,
299        center_y: f64,
300        radius: f64,
301    ) -> Result<(), PlotError> {
302        let plot = PieChartPlotF32::new(label_ids, values, center_x, center_y, radius);
303        plot.validate()?;
304        plot.plot();
305        Ok(())
306    }
307
308    /// Plot a centered pie chart (center at 0.5, 0.5 with radius 0.4)
309    pub fn centered_pie_chart(
310        &self,
311        label_ids: Vec<&str>,
312        values: &[f64],
313    ) -> Result<(), PlotError> {
314        let plot = PieChartPlot::new(label_ids, values, 0.5, 0.5, 0.4);
315        plot.validate()?;
316        plot.plot();
317        Ok(())
318    }
319}