1use super::{Plot, PlotError, with_plot_str_or_empty};
4use crate::sys;
5use crate::{BinMethod, HistogramFlags};
6
7pub struct HistogramPlot<'a> {
9 label: &'a str,
10 values: &'a [f64],
11 bins: i32,
12 bar_scale: f64,
13 range: Option<sys::ImPlotRange>,
14 flags: HistogramFlags,
15}
16
17impl<'a> HistogramPlot<'a> {
18 pub fn new(label: &'a str, values: &'a [f64]) -> Self {
20 Self {
21 label,
22 values,
23 bins: BinMethod::Sturges as i32,
24 bar_scale: 1.0,
25 range: None, flags: HistogramFlags::NONE,
27 }
28 }
29
30 pub fn with_bins(mut self, bins: i32) -> Self {
37 self.bins = bins;
38 self
39 }
40
41 pub fn with_bar_scale(mut self, scale: f64) -> Self {
43 self.bar_scale = scale;
44 self
45 }
46
47 pub fn with_range(mut self, min: f64, max: f64) -> Self {
50 self.range = Some(sys::ImPlotRange { Min: min, Max: max });
51 self
52 }
53
54 pub fn with_range_struct(mut self, range: sys::ImPlotRange) -> Self {
56 self.range = Some(range);
57 self
58 }
59
60 pub fn with_flags(mut self, flags: HistogramFlags) -> Self {
62 self.flags = flags;
63 self
64 }
65
66 pub fn horizontal(mut self) -> Self {
68 self.flags |= HistogramFlags::HORIZONTAL;
69 self
70 }
71
72 pub fn cumulative(mut self) -> Self {
74 self.flags |= HistogramFlags::CUMULATIVE;
75 self
76 }
77
78 pub fn density(mut self) -> Self {
80 self.flags |= HistogramFlags::DENSITY;
81 self
82 }
83
84 pub fn no_outliers(mut self) -> Self {
86 self.flags |= HistogramFlags::NO_OUTLIERS;
87 self
88 }
89
90 pub fn validate(&self) -> Result<(), PlotError> {
92 if self.values.is_empty() {
93 return Err(PlotError::EmptyData);
94 }
95 Ok(())
96 }
97}
98
99impl<'a> Plot for HistogramPlot<'a> {
100 fn plot(&self) {
101 if self.validate().is_err() {
102 return;
103 }
104 let Ok(count) = i32::try_from(self.values.len()) else {
105 return;
106 };
107
108 let range = if let Some(range) = &self.range {
109 *range
110 } else {
111 sys::ImPlotRange { Min: 0.0, Max: 0.0 }
112 };
113
114 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
115 sys::ImPlot_PlotHistogram_doublePtr(
116 label_ptr,
117 self.values.as_ptr(),
118 count,
119 self.bins,
120 self.bar_scale,
121 range,
122 self.flags.bits() as i32,
123 );
124 })
125 }
126
127 fn label(&self) -> &str {
128 self.label
129 }
130}
131
132pub struct Histogram2DPlot<'a> {
134 label: &'a str,
135 x_values: &'a [f64],
136 y_values: &'a [f64],
137 x_bins: i32,
138 y_bins: i32,
139 range: Option<sys::ImPlotRect>,
140 flags: HistogramFlags,
141}
142
143impl<'a> Histogram2DPlot<'a> {
144 pub fn new(label: &'a str, x_values: &'a [f64], y_values: &'a [f64]) -> Self {
146 Self {
147 label,
148 x_values,
149 y_values,
150 x_bins: BinMethod::Sturges as i32,
151 y_bins: BinMethod::Sturges as i32,
152 range: None, flags: HistogramFlags::NONE,
154 }
155 }
156
157 pub fn with_bins(mut self, x_bins: i32, y_bins: i32) -> Self {
159 self.x_bins = x_bins;
160 self.y_bins = y_bins;
161 self
162 }
163
164 pub fn with_range(mut self, x_min: f64, x_max: f64, y_min: f64, y_max: f64) -> Self {
166 self.range = Some(sys::ImPlotRect {
167 X: sys::ImPlotRange {
168 Min: x_min,
169 Max: x_max,
170 },
171 Y: sys::ImPlotRange {
172 Min: y_min,
173 Max: y_max,
174 },
175 });
176 self
177 }
178
179 pub fn with_range_struct(mut self, range: sys::ImPlotRect) -> Self {
181 self.range = Some(range);
182 self
183 }
184
185 pub fn with_flags(mut self, flags: HistogramFlags) -> Self {
187 self.flags = flags;
188 self
189 }
190
191 pub fn density(mut self) -> Self {
193 self.flags |= HistogramFlags::DENSITY;
194 self
195 }
196
197 pub fn no_outliers(mut self) -> Self {
199 self.flags |= HistogramFlags::NO_OUTLIERS;
200 self
201 }
202
203 pub fn column_major(mut self) -> Self {
205 self.flags |= HistogramFlags::COL_MAJOR;
206 self
207 }
208
209 pub fn validate(&self) -> Result<(), PlotError> {
211 super::validate_data_lengths(self.x_values, self.y_values)
212 }
213}
214
215impl<'a> Plot for Histogram2DPlot<'a> {
216 fn plot(&self) {
217 if self.validate().is_err() {
218 return;
219 }
220 let Ok(count) = i32::try_from(self.x_values.len()) else {
221 return;
222 };
223
224 let range = if let Some(range) = &self.range {
225 *range
226 } else {
227 sys::ImPlotRect {
228 X: sys::ImPlotRange { Min: 0.0, Max: 0.0 },
229 Y: sys::ImPlotRange { Min: 0.0, Max: 0.0 },
230 }
231 };
232
233 with_plot_str_or_empty(self.label, |label_ptr| unsafe {
234 sys::ImPlot_PlotHistogram2D_doublePtr(
235 label_ptr,
236 self.x_values.as_ptr(),
237 self.y_values.as_ptr(),
238 count,
239 self.x_bins,
240 self.y_bins,
241 range,
242 self.flags.bits() as i32,
243 );
244 })
245 }
246
247 fn label(&self) -> &str {
248 self.label
249 }
250}
251
252impl<'ui> crate::PlotUi<'ui> {
254 pub fn histogram_plot(&self, label: &str, values: &[f64]) -> Result<(), PlotError> {
256 let plot = HistogramPlot::new(label, values);
257 plot.validate()?;
258 plot.plot();
259 Ok(())
260 }
261
262 pub fn histogram_plot_with_bins(
264 &self,
265 label: &str,
266 values: &[f64],
267 bins: i32,
268 ) -> Result<(), PlotError> {
269 let plot = HistogramPlot::new(label, values).with_bins(bins);
270 plot.validate()?;
271 plot.plot();
272 Ok(())
273 }
274
275 pub fn histogram_2d_plot(
277 &self,
278 label: &str,
279 x_values: &[f64],
280 y_values: &[f64],
281 ) -> Result<(), PlotError> {
282 let plot = Histogram2DPlot::new(label, x_values, y_values);
283 plot.validate()?;
284 plot.plot();
285 Ok(())
286 }
287
288 pub fn histogram_2d_plot_with_bins(
290 &self,
291 label: &str,
292 x_values: &[f64],
293 y_values: &[f64],
294 x_bins: i32,
295 y_bins: i32,
296 ) -> Result<(), PlotError> {
297 let plot = Histogram2DPlot::new(label, x_values, y_values).with_bins(x_bins, y_bins);
298 plot.validate()?;
299 plot.plot();
300 Ok(())
301 }
302}