ggplot_rs/geom/
histogram.rs1use crate::aes::Aesthetic;
2use crate::coord::Coord;
3use crate::data::DataFrame;
4use crate::position::identity::PositionIdentity;
5use crate::position::Position;
6use crate::render::backend::{DrawBackend, RectStyle};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::bin::StatBin;
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15pub struct GeomHistogram {
17 pub bins: usize,
18 pub binwidth: Option<f64>,
19 pub fill: (u8, u8, u8),
20 pub color: (u8, u8, u8),
21 pub alpha: f64,
22}
23
24impl GeomHistogram {
25 pub fn with_binwidth(mut self, width: f64) -> Self {
27 self.binwidth = Some(width);
28 self
29 }
30
31 pub fn with_bins(mut self, bins: usize) -> Self {
33 self.bins = bins;
34 self.binwidth = None;
35 self
36 }
37}
38
39impl Default for GeomHistogram {
40 fn default() -> Self {
41 GeomHistogram {
42 bins: 30,
43 binwidth: None,
44 fill: (97, 156, 255),
45 color: (50, 50, 50),
46 alpha: 1.0,
47 }
48 }
49}
50
51impl Geom for GeomHistogram {
52 fn draw(
53 &self,
54 data: &DataFrame,
55 coord: &dyn Coord,
56 scales: &ScaleSet,
57 _theme: &Theme,
58 backend: &mut dyn DrawBackend,
59 ) -> Result<(), RenderError> {
60 let xmin_col = data.column("xmin");
61 let xmax_col = data.column("xmax");
62 let y_col = data
63 .column("y")
64 .ok_or(RenderError::MissingAesthetic("y".into()))?;
65
66 let plot_area = backend.plot_area();
67 let x_scale = scales.get(&Aesthetic::X);
68 let y_scale = scales.get(&Aesthetic::Y);
69
70 for i in 0..data.nrows() {
71 let (left_n, right_n) = match (xmin_col, xmax_col) {
72 (Some(xmin), Some(xmax)) => {
73 let ln = x_scale.map(|s| s.map(&xmin[i])).unwrap_or(0.0);
74 let rn = x_scale.map(|s| s.map(&xmax[i])).unwrap_or(1.0);
75 (ln, rn)
76 }
77 _ => {
78 let x_col = data
80 .column("x")
81 .ok_or(RenderError::MissingAesthetic("x".into()))?;
82 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
83 (nx - 0.02, nx + 0.02)
84 }
85 };
86
87 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
88 let ny_base = y_scale
89 .map(|s| s.map(&crate::data::Value::Float(0.0)))
90 .unwrap_or(0.0);
91
92 let (left_px, top_px) = coord.transform((left_n, ny), &plot_area);
93 let (right_px, bottom_px) = coord.transform((right_n, ny_base), &plot_area);
94
95 backend.draw_rect(
96 (left_px, top_px.min(bottom_px)),
97 (right_px, top_px.max(bottom_px)),
98 &RectStyle {
99 fill: Some(self.fill),
100 stroke: Some(self.color),
101 stroke_width: 0.5,
102 alpha: self.alpha,
103 clip: true,
104 },
105 )?;
106 }
107
108 Ok(())
109 }
110
111 fn required_aes(&self) -> Vec<Aesthetic> {
112 vec![Aesthetic::X]
113 }
114
115 fn default_stat(&self) -> Box<dyn Stat> {
116 let mut stat = StatBin {
117 bins: self.bins,
118 binwidth: self.binwidth,
119 };
120 if let Some(bw) = self.binwidth {
121 stat = stat.with_binwidth(bw);
122 }
123 Box::new(stat)
124 }
125
126 fn default_position(&self) -> Box<dyn Position> {
127 Box::new(PositionIdentity)
128 }
129
130 fn default_params(&self) -> GeomParams {
131 GeomParams::default()
132 }
133
134 fn name(&self) -> &str {
135 "histogram"
136 }
137
138 fn set_series_color(&mut self, color: (u8, u8, u8)) {
139 self.fill = color;
140 }
141}