1use super::{
4 Plot, PlotDataLayout, PlotError, PlotItemStyle, plot_spec_with_style,
5 with_plot_str_slice_with_opt,
6};
7use crate::{FloatFormat, FloatFormatError, ItemFlags, PieChartFlags, sys};
8use std::borrow::Cow;
9
10pub struct PieChartPlot<'a, F = &'static str> {
12 label_ids: Vec<&'a str>,
13 values: &'a [f64],
14 style: PlotItemStyle,
15 center_x: f64,
16 center_y: f64,
17 radius: f64,
18 label_fmt: Option<F>,
19 angle0: f64,
20 flags: PieChartFlags,
21 item_flags: ItemFlags,
22}
23
24impl<F> super::PlotItemStyled for PieChartPlot<'_, F> {
25 fn style_mut(&mut self) -> &mut PlotItemStyle {
26 &mut self.style
27 }
28}
29
30impl<'a> PieChartPlot<'a> {
31 pub fn new(
40 label_ids: Vec<&'a str>,
41 values: &'a [f64],
42 center_x: f64,
43 center_y: f64,
44 radius: f64,
45 ) -> Self {
46 Self {
47 label_ids,
48 values,
49 style: PlotItemStyle::default(),
50 center_x,
51 center_y,
52 radius,
53 label_fmt: Some("%.1f"),
54 angle0: 90.0, flags: PieChartFlags::NONE,
56 item_flags: ItemFlags::NONE,
57 }
58 }
59}
60
61impl<'a, F: AsRef<str>> PieChartPlot<'a, F> {
62 pub fn with_label_format<'fmt>(
64 self,
65 format: FloatFormat<'fmt>,
66 ) -> PieChartPlot<'a, FloatFormat<'fmt>> {
67 PieChartPlot {
68 label_ids: self.label_ids,
69 values: self.values,
70 style: self.style,
71 center_x: self.center_x,
72 center_y: self.center_y,
73 radius: self.radius,
74 label_fmt: Some(format),
75 angle0: self.angle0,
76 flags: self.flags,
77 item_flags: self.item_flags,
78 }
79 }
80
81 pub fn try_label_format<'fmt>(
83 self,
84 format: impl Into<Cow<'fmt, str>>,
85 ) -> Result<PieChartPlot<'a, FloatFormat<'fmt>>, FloatFormatError> {
86 Ok(self.with_label_format(FloatFormat::new(format)?))
87 }
88
89 pub fn without_value_labels(mut self) -> Self {
91 self.label_fmt = None;
92 self
93 }
94
95 pub fn with_start_angle(mut self, angle: f64) -> Self {
97 self.angle0 = angle;
98 self
99 }
100
101 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
103 self.flags = flags;
104 self
105 }
106
107 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
109 self.item_flags = flags;
110 self
111 }
112
113 pub fn normalize(mut self) -> Self {
115 self.flags |= PieChartFlags::NORMALIZE;
116 self
117 }
118
119 pub fn ignore_hidden(mut self) -> Self {
121 self.flags |= PieChartFlags::IGNORE_HIDDEN;
122 self
123 }
124
125 pub fn exploding(mut self) -> Self {
127 self.flags |= PieChartFlags::EXPLODING;
128 self
129 }
130
131 pub fn no_slice_border(mut self) -> Self {
133 self.flags |= PieChartFlags::NO_SLICE_BORDER;
134 self
135 }
136
137 pub fn validate(&self) -> Result<(), PlotError> {
139 if self.values.is_empty() {
140 return Err(PlotError::EmptyData);
141 }
142
143 if self.label_ids.len() != self.values.len() {
144 return Err(PlotError::DataLengthMismatch {
145 x_len: self.label_ids.len(),
146 y_len: self.values.len(),
147 });
148 }
149
150 if self.radius <= 0.0 {
151 return Err(PlotError::InvalidData(
152 "Radius must be positive".to_string(),
153 ));
154 }
155
156 if self.values.iter().any(|&v| v < 0.0) {
158 return Err(PlotError::InvalidData(
159 "Pie chart values cannot be negative".to_string(),
160 ));
161 }
162
163 Ok(())
164 }
165}
166
167impl<F: AsRef<str>> Plot for PieChartPlot<'_, F> {
168 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
169 if self.validate().is_err() {
170 return;
171 }
172 let Ok(count) = i32::try_from(self.values.len()) else {
173 return;
174 };
175 plot_ui.with_bound_context(|| {
176 with_plot_str_slice_with_opt(
177 &self.label_ids,
178 self.label_fmt.as_ref().map(AsRef::as_ref),
179 |label_ptrs, label_fmt_ptr| unsafe {
180 let spec = plot_spec_with_style(
181 self.style,
182 self.flags.bits() | self.item_flags.bits(),
183 PlotDataLayout::DEFAULT,
184 );
185 sys::ImPlot_PlotPieChart_doublePtrStr(
186 label_ptrs.as_ptr(),
187 self.values.as_ptr(),
188 count,
189 self.center_x,
190 self.center_y,
191 self.radius,
192 label_fmt_ptr,
193 self.angle0,
194 spec,
195 );
196 },
197 )
198 })
199 }
200
201 fn label(&self) -> &str {
202 "PieChart" }
204}
205
206pub struct PieChartPlotF32<'a, F = &'static str> {
208 label_ids: Vec<&'a str>,
209 values: &'a [f32],
210 style: PlotItemStyle,
211 center_x: f64,
212 center_y: f64,
213 radius: f64,
214 label_fmt: Option<F>,
215 angle0: f64,
216 flags: PieChartFlags,
217 item_flags: ItemFlags,
218}
219
220impl<F> super::PlotItemStyled for PieChartPlotF32<'_, F> {
221 fn style_mut(&mut self) -> &mut PlotItemStyle {
222 &mut self.style
223 }
224}
225
226impl<'a> PieChartPlotF32<'a> {
227 pub fn new(
229 label_ids: Vec<&'a str>,
230 values: &'a [f32],
231 center_x: f64,
232 center_y: f64,
233 radius: f64,
234 ) -> Self {
235 Self {
236 label_ids,
237 values,
238 style: PlotItemStyle::default(),
239 center_x,
240 center_y,
241 radius,
242 label_fmt: Some("%.1f"),
243 angle0: 90.0,
244 flags: PieChartFlags::NONE,
245 item_flags: ItemFlags::NONE,
246 }
247 }
248}
249
250impl<'a, F: AsRef<str>> PieChartPlotF32<'a, F> {
251 pub fn with_label_format<'fmt>(
253 self,
254 format: FloatFormat<'fmt>,
255 ) -> PieChartPlotF32<'a, FloatFormat<'fmt>> {
256 PieChartPlotF32 {
257 label_ids: self.label_ids,
258 values: self.values,
259 style: self.style,
260 center_x: self.center_x,
261 center_y: self.center_y,
262 radius: self.radius,
263 label_fmt: Some(format),
264 angle0: self.angle0,
265 flags: self.flags,
266 item_flags: self.item_flags,
267 }
268 }
269
270 pub fn try_label_format<'fmt>(
272 self,
273 format: impl Into<Cow<'fmt, str>>,
274 ) -> Result<PieChartPlotF32<'a, FloatFormat<'fmt>>, FloatFormatError> {
275 Ok(self.with_label_format(FloatFormat::new(format)?))
276 }
277
278 pub fn without_value_labels(mut self) -> Self {
280 self.label_fmt = None;
281 self
282 }
283
284 pub fn with_start_angle(mut self, angle: f64) -> Self {
286 self.angle0 = angle;
287 self
288 }
289
290 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
292 self.flags = flags;
293 self
294 }
295
296 pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
298 self.item_flags = flags;
299 self
300 }
301
302 pub fn normalize(mut self) -> Self {
304 self.flags |= PieChartFlags::NORMALIZE;
305 self
306 }
307
308 pub fn ignore_hidden(mut self) -> Self {
310 self.flags |= PieChartFlags::IGNORE_HIDDEN;
311 self
312 }
313
314 pub fn exploding(mut self) -> Self {
316 self.flags |= PieChartFlags::EXPLODING;
317 self
318 }
319
320 pub fn no_slice_border(mut self) -> Self {
322 self.flags |= PieChartFlags::NO_SLICE_BORDER;
323 self
324 }
325
326 pub fn validate(&self) -> Result<(), PlotError> {
328 if self.values.is_empty() {
329 return Err(PlotError::EmptyData);
330 }
331
332 if self.label_ids.len() != self.values.len() {
333 return Err(PlotError::DataLengthMismatch {
334 x_len: self.label_ids.len(),
335 y_len: self.values.len(),
336 });
337 }
338
339 if self.radius <= 0.0 {
340 return Err(PlotError::InvalidData(
341 "Radius must be positive".to_string(),
342 ));
343 }
344
345 if self.values.iter().any(|&v| v < 0.0) {
346 return Err(PlotError::InvalidData(
347 "Pie chart values cannot be negative".to_string(),
348 ));
349 }
350
351 Ok(())
352 }
353}
354
355impl<F: AsRef<str>> Plot for PieChartPlotF32<'_, F> {
356 fn plot(&self, plot_ui: &crate::PlotUi<'_>) {
357 if self.validate().is_err() {
358 return;
359 }
360 let Ok(count) = i32::try_from(self.values.len()) else {
361 return;
362 };
363 plot_ui.with_bound_context(|| {
364 with_plot_str_slice_with_opt(
365 &self.label_ids,
366 self.label_fmt.as_ref().map(AsRef::as_ref),
367 |label_ptrs, label_fmt_ptr| unsafe {
368 let spec = plot_spec_with_style(
369 self.style,
370 self.flags.bits() | self.item_flags.bits(),
371 PlotDataLayout::DEFAULT,
372 );
373 sys::ImPlot_PlotPieChart_FloatPtrStr(
374 label_ptrs.as_ptr(),
375 self.values.as_ptr(),
376 count,
377 self.center_x,
378 self.center_y,
379 self.radius,
380 label_fmt_ptr,
381 self.angle0,
382 spec,
383 );
384 },
385 )
386 })
387 }
388
389 fn label(&self) -> &str {
390 "PieChart"
391 }
392}
393
394impl<'ui> crate::PlotUi<'ui> {
396 pub fn pie_chart_plot(
398 &self,
399 label_ids: Vec<&str>,
400 values: &[f64],
401 center_x: f64,
402 center_y: f64,
403 radius: f64,
404 ) -> Result<(), PlotError> {
405 let plot = PieChartPlot::new(label_ids, values, center_x, center_y, radius);
406 plot.validate()?;
407 plot.plot(self);
408 Ok(())
409 }
410
411 pub fn pie_chart_plot_f32(
413 &self,
414 label_ids: Vec<&str>,
415 values: &[f32],
416 center_x: f64,
417 center_y: f64,
418 radius: f64,
419 ) -> Result<(), PlotError> {
420 let plot = PieChartPlotF32::new(label_ids, values, center_x, center_y, radius);
421 plot.validate()?;
422 plot.plot(self);
423 Ok(())
424 }
425
426 pub fn centered_pie_chart(
428 &self,
429 label_ids: Vec<&str>,
430 values: &[f64],
431 ) -> Result<(), PlotError> {
432 let plot = PieChartPlot::new(label_ids, values, 0.5, 0.5, 0.4);
433 plot.validate()?;
434 plot.plot(self);
435 Ok(())
436 }
437}