use super::{Plot, PlotError, PlotItemStyle, plot_spec_with_style, with_plot_str_slice_with_opt};
use crate::{ItemFlags, PieChartFlags, sys};
pub struct PieChartPlot<'a> {
label_ids: Vec<&'a str>,
values: &'a [f64],
style: PlotItemStyle,
center_x: f64,
center_y: f64,
radius: f64,
label_fmt: Option<&'a str>,
angle0: f64,
flags: PieChartFlags,
item_flags: ItemFlags,
}
impl<'a> super::PlotItemStyled for PieChartPlot<'a> {
fn style_mut(&mut self) -> &mut PlotItemStyle {
&mut self.style
}
}
impl<'a> PieChartPlot<'a> {
pub fn new(
label_ids: Vec<&'a str>,
values: &'a [f64],
center_x: f64,
center_y: f64,
radius: f64,
) -> Self {
Self {
label_ids,
values,
style: PlotItemStyle::default(),
center_x,
center_y,
radius,
label_fmt: Some("%.1f"),
angle0: 90.0, flags: PieChartFlags::NONE,
item_flags: ItemFlags::NONE,
}
}
pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
self.label_fmt = fmt;
self
}
pub fn with_start_angle(mut self, angle: f64) -> Self {
self.angle0 = angle;
self
}
pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
self.flags = flags;
self
}
pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
self.item_flags = flags;
self
}
pub fn normalize(mut self) -> Self {
self.flags |= PieChartFlags::NORMALIZE;
self
}
pub fn ignore_hidden(mut self) -> Self {
self.flags |= PieChartFlags::IGNORE_HIDDEN;
self
}
pub fn exploding(mut self) -> Self {
self.flags |= PieChartFlags::EXPLODING;
self
}
pub fn no_slice_border(mut self) -> Self {
self.flags |= PieChartFlags::NO_SLICE_BORDER;
self
}
pub fn validate(&self) -> Result<(), PlotError> {
if self.values.is_empty() {
return Err(PlotError::EmptyData);
}
if self.label_ids.len() != self.values.len() {
return Err(PlotError::DataLengthMismatch {
x_len: self.label_ids.len(),
y_len: self.values.len(),
});
}
if self.radius <= 0.0 {
return Err(PlotError::InvalidData(
"Radius must be positive".to_string(),
));
}
if self.values.iter().any(|&v| v < 0.0) {
return Err(PlotError::InvalidData(
"Pie chart values cannot be negative".to_string(),
));
}
Ok(())
}
}
impl<'a> Plot for PieChartPlot<'a> {
fn plot(&self) {
if self.validate().is_err() {
return;
}
let Ok(count) = i32::try_from(self.values.len()) else {
return;
};
with_plot_str_slice_with_opt(
&self.label_ids,
self.label_fmt,
|label_ptrs, label_fmt_ptr| unsafe {
let spec = plot_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
crate::IMPLOT_AUTO,
);
sys::ImPlot_PlotPieChart_doublePtrStr(
label_ptrs.as_ptr(),
self.values.as_ptr(),
count,
self.center_x,
self.center_y,
self.radius,
label_fmt_ptr,
self.angle0,
spec,
);
},
)
}
fn label(&self) -> &str {
"PieChart" }
}
pub struct PieChartPlotF32<'a> {
label_ids: Vec<&'a str>,
values: &'a [f32],
style: PlotItemStyle,
center_x: f64,
center_y: f64,
radius: f64,
label_fmt: Option<&'a str>,
angle0: f64,
flags: PieChartFlags,
item_flags: ItemFlags,
}
impl<'a> super::PlotItemStyled for PieChartPlotF32<'a> {
fn style_mut(&mut self) -> &mut PlotItemStyle {
&mut self.style
}
}
impl<'a> PieChartPlotF32<'a> {
pub fn new(
label_ids: Vec<&'a str>,
values: &'a [f32],
center_x: f64,
center_y: f64,
radius: f64,
) -> Self {
Self {
label_ids,
values,
style: PlotItemStyle::default(),
center_x,
center_y,
radius,
label_fmt: Some("%.1f"),
angle0: 90.0,
flags: PieChartFlags::NONE,
item_flags: ItemFlags::NONE,
}
}
pub fn with_label_format(mut self, fmt: Option<&'a str>) -> Self {
self.label_fmt = fmt;
self
}
pub fn with_start_angle(mut self, angle: f64) -> Self {
self.angle0 = angle;
self
}
pub fn with_flags(mut self, flags: PieChartFlags) -> Self {
self.flags = flags;
self
}
pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
self.item_flags = flags;
self
}
pub fn normalize(mut self) -> Self {
self.flags |= PieChartFlags::NORMALIZE;
self
}
pub fn ignore_hidden(mut self) -> Self {
self.flags |= PieChartFlags::IGNORE_HIDDEN;
self
}
pub fn exploding(mut self) -> Self {
self.flags |= PieChartFlags::EXPLODING;
self
}
pub fn no_slice_border(mut self) -> Self {
self.flags |= PieChartFlags::NO_SLICE_BORDER;
self
}
pub fn validate(&self) -> Result<(), PlotError> {
if self.values.is_empty() {
return Err(PlotError::EmptyData);
}
if self.label_ids.len() != self.values.len() {
return Err(PlotError::DataLengthMismatch {
x_len: self.label_ids.len(),
y_len: self.values.len(),
});
}
if self.radius <= 0.0 {
return Err(PlotError::InvalidData(
"Radius must be positive".to_string(),
));
}
if self.values.iter().any(|&v| v < 0.0) {
return Err(PlotError::InvalidData(
"Pie chart values cannot be negative".to_string(),
));
}
Ok(())
}
}
impl<'a> Plot for PieChartPlotF32<'a> {
fn plot(&self) {
if self.validate().is_err() {
return;
}
let Ok(count) = i32::try_from(self.values.len()) else {
return;
};
with_plot_str_slice_with_opt(
&self.label_ids,
self.label_fmt,
|label_ptrs, label_fmt_ptr| unsafe {
let spec = plot_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
crate::IMPLOT_AUTO,
);
sys::ImPlot_PlotPieChart_FloatPtrStr(
label_ptrs.as_ptr(),
self.values.as_ptr(),
count,
self.center_x,
self.center_y,
self.radius,
label_fmt_ptr,
self.angle0,
spec,
);
},
)
}
fn label(&self) -> &str {
"PieChart"
}
}
impl<'ui> crate::PlotUi<'ui> {
pub fn pie_chart_plot(
&self,
label_ids: Vec<&str>,
values: &[f64],
center_x: f64,
center_y: f64,
radius: f64,
) -> Result<(), PlotError> {
let plot = PieChartPlot::new(label_ids, values, center_x, center_y, radius);
plot.validate()?;
plot.plot();
Ok(())
}
pub fn pie_chart_plot_f32(
&self,
label_ids: Vec<&str>,
values: &[f32],
center_x: f64,
center_y: f64,
radius: f64,
) -> Result<(), PlotError> {
let plot = PieChartPlotF32::new(label_ids, values, center_x, center_y, radius);
plot.validate()?;
plot.plot();
Ok(())
}
pub fn centered_pie_chart(
&self,
label_ids: Vec<&str>,
values: &[f64],
) -> Result<(), PlotError> {
let plot = PieChartPlot::new(label_ids, values, 0.5, 0.5, 0.4);
plot.validate()?;
plot.plot();
Ok(())
}
}