1use super::{Plot, PlotError, PlotItemStyle, plot_spec_with_style, with_plot_str_slice_with_opt};
4use crate::{ItemFlags, PieChartFlags, sys};
5
6pub struct PieChartPlot<'a> {
8 label_ids: Vec<&'a str>,
9 values: &'a [f64],
10 style: PlotItemStyle,
11 center_x: f64,
12 center_y: f64,
13 radius: f64,
14 label_fmt: Option<&'a str>,
15 angle0: f64,
16 flags: PieChartFlags,
17 item_flags: ItemFlags,
18}
19
20impl<'a> super::PlotItemStyled for PieChartPlot<'a> {
21 fn style_mut(&mut self) -> &mut PlotItemStyle {
22 &mut self.style
23 }
24}
25
26impl<'a> PieChartPlot<'a> {
27 pub fn new(
36 label_ids: Vec<&'a str>,
37 values: &'a [f64],
38 center_x: f64,
39 center_y: f64,
40 radius: f64,
41 ) -> Self {
42 Self {
43 label_ids,
44 values,
45 style: PlotItemStyle::default(),
46 center_x,
47 center_y,
48 radius,
49 label_fmt: Some("%.1f"),
50 angle0: 90.0, flags: PieChartFlags::NONE,
52 item_flags: ItemFlags::NONE,
53 }
54 }
55
56 pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
59 self.label_fmt = fmt;
60 self
61 }
62
63 pub fn with_start_angle(mut self, angle: f64) -> Self {
65 self.angle0 = angle;
66 self
67 }
68
69 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
71 self.flags = flags;
72 self
73 }
74
75 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
77 self.item_flags = flags;
78 self
79 }
80
81 pub fn normalize(mut self) -> Self {
83 self.flags |= PieChartFlags::NORMALIZE;
84 self
85 }
86
87 pub fn ignore_hidden(mut self) -> Self {
89 self.flags |= PieChartFlags::IGNORE_HIDDEN;
90 self
91 }
92
93 pub fn exploding(mut self) -> Self {
95 self.flags |= PieChartFlags::EXPLODING;
96 self
97 }
98
99 pub fn no_slice_border(mut self) -> Self {
101 self.flags |= PieChartFlags::NO_SLICE_BORDER;
102 self
103 }
104
105 pub fn validate(&self) -> Result<(), PlotError> {
107 if self.values.is_empty() {
108 return Err(PlotError::EmptyData);
109 }
110
111 if self.label_ids.len() != self.values.len() {
112 return Err(PlotError::DataLengthMismatch {
113 x_len: self.label_ids.len(),
114 y_len: self.values.len(),
115 });
116 }
117
118 if self.radius <= 0.0 {
119 return Err(PlotError::InvalidData(
120 "Radius must be positive".to_string(),
121 ));
122 }
123
124 if self.values.iter().any(|&v| v < 0.0) {
126 return Err(PlotError::InvalidData(
127 "Pie chart values cannot be negative".to_string(),
128 ));
129 }
130
131 Ok(())
132 }
133}
134
135impl<'a> Plot for PieChartPlot<'a> {
136 fn plot(&self) {
137 if self.validate().is_err() {
138 return;
139 }
140 let Ok(count) = i32::try_from(self.values.len()) else {
141 return;
142 };
143 with_plot_str_slice_with_opt(
144 &self.label_ids,
145 self.label_fmt,
146 |label_ptrs, label_fmt_ptr| unsafe {
147 let spec = plot_spec_with_style(
148 self.style,
149 self.flags.bits() | self.item_flags.bits(),
150 0,
151 crate::IMPLOT_AUTO,
152 );
153 sys::ImPlot_PlotPieChart_doublePtrStr(
154 label_ptrs.as_ptr(),
155 self.values.as_ptr(),
156 count,
157 self.center_x,
158 self.center_y,
159 self.radius,
160 label_fmt_ptr,
161 self.angle0,
162 spec,
163 );
164 },
165 )
166 }
167
168 fn label(&self) -> &str {
169 "PieChart" }
171}
172
173pub struct PieChartPlotF32<'a> {
175 label_ids: Vec<&'a str>,
176 values: &'a [f32],
177 style: PlotItemStyle,
178 center_x: f64,
179 center_y: f64,
180 radius: f64,
181 label_fmt: Option<&'a str>,
182 angle0: f64,
183 flags: PieChartFlags,
184 item_flags: ItemFlags,
185}
186
187impl<'a> super::PlotItemStyled for PieChartPlotF32<'a> {
188 fn style_mut(&mut self) -> &mut PlotItemStyle {
189 &mut self.style
190 }
191}
192
193impl<'a> PieChartPlotF32<'a> {
194 pub fn new(
196 label_ids: Vec<&'a str>,
197 values: &'a [f32],
198 center_x: f64,
199 center_y: f64,
200 radius: f64,
201 ) -> Self {
202 Self {
203 label_ids,
204 values,
205 style: PlotItemStyle::default(),
206 center_x,
207 center_y,
208 radius,
209 label_fmt: Some("%.1f"),
210 angle0: 90.0,
211 flags: PieChartFlags::NONE,
212 item_flags: ItemFlags::NONE,
213 }
214 }
215
216 pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
218 self.label_fmt = fmt;
219 self
220 }
221
222 pub fn with_start_angle(mut self, angle: f64) -> Self {
224 self.angle0 = angle;
225 self
226 }
227
228 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
230 self.flags = flags;
231 self
232 }
233
234 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
236 self.item_flags = flags;
237 self
238 }
239
240 pub fn normalize(mut self) -> Self {
242 self.flags |= PieChartFlags::NORMALIZE;
243 self
244 }
245
246 pub fn ignore_hidden(mut self) -> Self {
248 self.flags |= PieChartFlags::IGNORE_HIDDEN;
249 self
250 }
251
252 pub fn exploding(mut self) -> Self {
254 self.flags |= PieChartFlags::EXPLODING;
255 self
256 }
257
258 pub fn no_slice_border(mut self) -> Self {
260 self.flags |= PieChartFlags::NO_SLICE_BORDER;
261 self
262 }
263
264 pub fn validate(&self) -> Result<(), PlotError> {
266 if self.values.is_empty() {
267 return Err(PlotError::EmptyData);
268 }
269
270 if self.label_ids.len() != self.values.len() {
271 return Err(PlotError::DataLengthMismatch {
272 x_len: self.label_ids.len(),
273 y_len: self.values.len(),
274 });
275 }
276
277 if self.radius <= 0.0 {
278 return Err(PlotError::InvalidData(
279 "Radius must be positive".to_string(),
280 ));
281 }
282
283 if self.values.iter().any(|&v| v < 0.0) {
284 return Err(PlotError::InvalidData(
285 "Pie chart values cannot be negative".to_string(),
286 ));
287 }
288
289 Ok(())
290 }
291}
292
293impl<'a> Plot for PieChartPlotF32<'a> {
294 fn plot(&self) {
295 if self.validate().is_err() {
296 return;
297 }
298 let Ok(count) = i32::try_from(self.values.len()) else {
299 return;
300 };
301 with_plot_str_slice_with_opt(
302 &self.label_ids,
303 self.label_fmt,
304 |label_ptrs, label_fmt_ptr| unsafe {
305 let spec = plot_spec_with_style(
306 self.style,
307 self.flags.bits() | self.item_flags.bits(),
308 0,
309 crate::IMPLOT_AUTO,
310 );
311 sys::ImPlot_PlotPieChart_FloatPtrStr(
312 label_ptrs.as_ptr(),
313 self.values.as_ptr(),
314 count,
315 self.center_x,
316 self.center_y,
317 self.radius,
318 label_fmt_ptr,
319 self.angle0,
320 spec,
321 );
322 },
323 )
324 }
325
326 fn label(&self) -> &str {
327 "PieChart"
328 }
329}
330
331impl<'ui> crate::PlotUi<'ui> {
333 pub fn pie_chart_plot(
335 &self,
336 label_ids: Vec<&str>,
337 values: &[f64],
338 center_x: f64,
339 center_y: f64,
340 radius: f64,
341 ) -> Result<(), PlotError> {
342 let plot = PieChartPlot::new(label_ids, values, center_x, center_y, radius);
343 plot.validate()?;
344 plot.plot();
345 Ok(())
346 }
347
348 pub fn pie_chart_plot_f32(
350 &self,
351 label_ids: Vec<&str>,
352 values: &[f32],
353 center_x: f64,
354 center_y: f64,
355 radius: f64,
356 ) -> Result<(), PlotError> {
357 let plot = PieChartPlotF32::new(label_ids, values, center_x, center_y, radius);
358 plot.validate()?;
359 plot.plot();
360 Ok(())
361 }
362
363 pub fn centered_pie_chart(
365 &self,
366 label_ids: Vec<&str>,
367 values: &[f64],
368 ) -> Result<(), PlotError> {
369 let plot = PieChartPlot::new(label_ids, values, 0.5, 0.5, 0.4);
370 plot.validate()?;
371 plot.plot();
372 Ok(())
373 }
374}