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