1use super::{Plot, PlotError, with_plot_str_slice_with_opt};
4use crate::PieChartFlags;
5use crate::sys;
6
7pub 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 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, flags: PieChartFlags::NONE,
44 }
45 }
46
47 pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
50 self.label_fmt = fmt;
51 self
52 }
53
54 pub fn with_start_angle(mut self, angle: f64) -> Self {
56 self.angle0 = angle;
57 self
58 }
59
60 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
62 self.flags = flags;
63 self
64 }
65
66 pub fn normalize(mut self) -> Self {
68 self.flags |= PieChartFlags::NORMALIZE;
69 self
70 }
71
72 pub fn ignore_hidden(mut self) -> Self {
74 self.flags |= PieChartFlags::IGNORE_HIDDEN;
75 self
76 }
77
78 pub fn exploding(mut self) -> Self {
80 self.flags |= PieChartFlags::EXPLODING;
81 self
82 }
83
84 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 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" }
144}
145
146pub 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 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 pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
181 self.label_fmt = fmt;
182 self
183 }
184
185 pub fn with_start_angle(mut self, angle: f64) -> Self {
187 self.angle0 = angle;
188 self
189 }
190
191 pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
193 self.flags = flags;
194 self
195 }
196
197 pub fn normalize(mut self) -> Self {
199 self.flags |= PieChartFlags::NORMALIZE;
200 self
201 }
202
203 pub fn ignore_hidden(mut self) -> Self {
205 self.flags |= PieChartFlags::IGNORE_HIDDEN;
206 self
207 }
208
209 pub fn exploding(mut self) -> Self {
211 self.flags |= PieChartFlags::EXPLODING;
212 self
213 }
214
215 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
276impl<'ui> crate::PlotUi<'ui> {
278 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 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 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}