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, plot_ui: &crate::PlotUi<'_>) {
140 if self.validate().is_err() {
141 return;
142 }
143 let Ok(count) = i32::try_from(self.values.len()) else {
144 return;
145 };
146 let _guard = plot_ui.bind();
147 with_plot_str_slice_with_opt(
148 &self.label_ids,
149 self.label_fmt,
150 |label_ptrs, label_fmt_ptr| unsafe {
151 let spec = plot_spec_with_style(
152 self.style,
153 self.flags.bits() | self.item_flags.bits(),
154 PlotDataLayout::DEFAULT,
155 );
156 sys::ImPlot_PlotPieChart_doublePtrStr(
157 label_ptrs.as_ptr(),
158 self.values.as_ptr(),
159 count,
160 self.center_x,
161 self.center_y,
162 self.radius,
163 label_fmt_ptr,
164 self.angle0,
165 spec,
166 );
167 },
168 )
169 }
170
171 fn label(&self) -> &str {
172 "PieChart" }
174}
175
176pub struct PieChartPlotF32<'a> {
178 label_ids: Vec<&'a str>,
179 values: &'a [f32],
180 style: PlotItemStyle,
181 center_x: f64,
182 center_y: f64,
183 radius: f64,
184 label_fmt: Option<&'a str>,
185 angle0: f64,
186 flags: PieChartFlags,
187 item_flags: ItemFlags,
188}
189
190impl<'a> super::PlotItemStyled for PieChartPlotF32<'a> {
191 fn style_mut(&mut self) -> &mut PlotItemStyle {
192 &mut self.style
193 }
194}
195
196impl<'a> PieChartPlotF32<'a> {
197 pub fn new(
199 label_ids: Vec<&'a str>,
200 values: &'a [f32],
201 center_x: f64,
202 center_y: f64,
203 radius: f64,
204 ) -> Self {
205 Self {
206 label_ids,
207 values,
208 style: PlotItemStyle::default(),
209 center_x,
210 center_y,
211 radius,
212 label_fmt: Some("%.1f"),
213 angle0: 90.0,
214 flags: PieChartFlags::NONE,
215 item_flags: ItemFlags::NONE,
216 }
217 }
218
219 pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
221 self.label_fmt = fmt;
222 self
223 }
224
225 pub fn with_start_angle(mut self, angle: f64) -> Self {
227 self.angle0 = angle;
228 self
229 }
230
231 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
233 self.flags = flags;
234 self
235 }
236
237 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
239 self.item_flags = flags;
240 self
241 }
242
243 pub fn normalize(mut self) -> Self {
245 self.flags |= PieChartFlags::NORMALIZE;
246 self
247 }
248
249 pub fn ignore_hidden(mut self) -> Self {
251 self.flags |= PieChartFlags::IGNORE_HIDDEN;
252 self
253 }
254
255 pub fn exploding(mut self) -> Self {
257 self.flags |= PieChartFlags::EXPLODING;
258 self
259 }
260
261 pub fn no_slice_border(mut self) -> Self {
263 self.flags |= PieChartFlags::NO_SLICE_BORDER;
264 self
265 }
266
267 pub fn validate(&self) -> Result<(), PlotError> {
269 if self.values.is_empty() {
270 return Err(PlotError::EmptyData);
271 }
272
273 if self.label_ids.len() != self.values.len() {
274 return Err(PlotError::DataLengthMismatch {
275 x_len: self.label_ids.len(),
276 y_len: self.values.len(),
277 });
278 }
279
280 if self.radius <= 0.0 {
281 return Err(PlotError::InvalidData(
282 "Radius must be positive".to_string(),
283 ));
284 }
285
286 if self.values.iter().any(|&v| v < 0.0) {
287 return Err(PlotError::InvalidData(
288 "Pie chart values cannot be negative".to_string(),
289 ));
290 }
291
292 Ok(())
293 }
294}
295
296impl<'a> Plot for PieChartPlotF32<'a> {
297 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
298 if self.validate().is_err() {
299 return;
300 }
301 let Ok(count) = i32::try_from(self.values.len()) else {
302 return;
303 };
304 let _guard = plot_ui.bind();
305 with_plot_str_slice_with_opt(
306 &self.label_ids,
307 self.label_fmt,
308 |label_ptrs, label_fmt_ptr| unsafe {
309 let spec = plot_spec_with_style(
310 self.style,
311 self.flags.bits() | self.item_flags.bits(),
312 PlotDataLayout::DEFAULT,
313 );
314 sys::ImPlot_PlotPieChart_FloatPtrStr(
315 label_ptrs.as_ptr(),
316 self.values.as_ptr(),
317 count,
318 self.center_x,
319 self.center_y,
320 self.radius,
321 label_fmt_ptr,
322 self.angle0,
323 spec,
324 );
325 },
326 )
327 }
328
329 fn label(&self) -> &str {
330 "PieChart"
331 }
332}
333
334impl<'ui> crate::PlotUi<'ui> {
336 pub fn pie_chart_plot(
338 &self,
339 label_ids: Vec<&str>,
340 values: &[f64],
341 center_x: f64,
342 center_y: f64,
343 radius: f64,
344 ) -> Result<(), PlotError> {
345 let plot = PieChartPlot::new(label_ids, values, center_x, center_y, radius);
346 plot.validate()?;
347 plot.plot(self);
348 Ok(())
349 }
350
351 pub fn pie_chart_plot_f32(
353 &self,
354 label_ids: Vec<&str>,
355 values: &[f32],
356 center_x: f64,
357 center_y: f64,
358 radius: f64,
359 ) -> Result<(), PlotError> {
360 let plot = PieChartPlotF32::new(label_ids, values, center_x, center_y, radius);
361 plot.validate()?;
362 plot.plot(self);
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 plot.plot(self);
375 Ok(())
376 }
377}