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 let cnt = super::tip_value(&y_col[i]);
97 let tip = match (xmin_col, xmax_col) {
98 (Some(lo), Some(hi)) => format!(
99 "[{}, {}): {cnt}",
100 super::tip_value(&lo[i]),
101 super::tip_value(&hi[i])
102 ),
103 _ => format!("count: {cnt}"),
104 };
105 super::set_mark(
106 backend,
107 Some(tip),
108 data.column("x").map(|c| super::tip_value(&c[i])),
109 super::series_key(data, i),
110 super::measured_value(data, i),
111 );
112
113 backend.draw_rect(
114 (left_px, top_px.min(bottom_px)),
115 (right_px, top_px.max(bottom_px)),
116 &RectStyle {
117 fill: Some(self.fill),
118 stroke: Some(self.color),
119 stroke_width: 0.5,
120 alpha: self.alpha,
121 clip: true,
122 },
123 )?;
124 }
125 super::clear_mark(backend);
126
127 Ok(())
128 }
129
130 fn required_aes(&self) -> Vec<Aesthetic> {
131 vec![Aesthetic::X]
132 }
133
134 fn default_stat(&self) -> Box<dyn Stat> {
135 let mut stat = StatBin {
136 bins: self.bins,
137 binwidth: self.binwidth,
138 };
139 if let Some(bw) = self.binwidth {
140 stat = stat.with_binwidth(bw);
141 }
142 Box::new(stat)
143 }
144
145 fn default_position(&self) -> Box<dyn Position> {
146 Box::new(PositionIdentity)
147 }
148
149 fn default_params(&self) -> GeomParams {
150 GeomParams::default()
151 }
152
153 fn name(&self) -> &str {
154 "histogram"
155 }
156
157 fn include_zero_baseline(&self) -> bool {
158 true
159 }
160
161 fn set_series_color(&mut self, color: (u8, u8, u8)) {
162 self.fill = color;
163 }
164}