1use crate::aes::Aesthetic;
2use crate::coord::Coord;
3use crate::data::DataFrame;
4use crate::position::stack::PositionStack;
5use crate::position::Position;
6use crate::render::backend::{DrawBackend, RectStyle};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::count::StatCount;
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15pub struct GeomBar {
17 pub fill: (u8, u8, u8),
18 pub color: (u8, u8, u8),
19 pub alpha: f64,
20 pub width: f64,
21}
22
23impl Default for GeomBar {
24 fn default() -> Self {
25 GeomBar {
26 fill: (97, 156, 255),
27 color: (50, 50, 50),
28 alpha: 1.0,
29 width: 0.9,
30 }
31 }
32}
33
34impl Geom for GeomBar {
35 fn draw(
36 &self,
37 data: &DataFrame,
38 coord: &dyn Coord,
39 scales: &ScaleSet,
40 _theme: &Theme,
41 backend: &mut dyn DrawBackend,
42 ) -> Result<(), RenderError> {
43 let x_col = data
44 .column("x")
45 .ok_or(RenderError::MissingAesthetic("x".into()))?;
46 let y_col = data
47 .column("y")
48 .ok_or(RenderError::MissingAesthetic("y".into()))?;
49 let fill_col = data.column("fill");
50 let ymin_col = data.column("ymin");
51
52 let plot_area = backend.plot_area();
53 let x_scale = scales.get(&Aesthetic::X);
54 let y_scale = scales.get(&Aesthetic::Y);
55
56 let x_is_discrete = x_scale.map(|s| s.is_discrete()).unwrap_or(false);
57
58 let half_width = if x_is_discrete {
61 let n_breaks = x_scale.map(|s| s.breaks().len()).unwrap_or(1);
63 self.width / n_breaks.max(1) as f64 / 2.0
64 } else {
65 super::continuous_bar_half_width(
66 x_col.iter().filter_map(|v| x_scale.map(|s| s.map(v))),
67 self.width,
68 0.02,
69 )
70 };
71
72 let x_mapper = super::support::XMapper::new(data, scales);
75 let dodge_n = data.column(crate::position::DODGE_N_COL);
76
77 for i in 0..data.nrows() {
78 let nx = x_mapper.map(&x_col[i], i);
79 let half_width = match dodge_n.and_then(|c| c[i].as_f64()) {
80 Some(n) if n > 1.0 && x_is_discrete => half_width / n,
81 _ => half_width,
82 };
83 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
84
85 let ny_base = ymin_col
86 .and_then(|c| c[i].as_f64())
87 .and_then(|v| y_scale.map(|s| s.map(&crate::data::Value::Float(v))))
88 .unwrap_or_else(|| {
89 y_scale
90 .map(|s| s.map(&crate::data::Value::Float(0.0)))
91 .unwrap_or(0.0)
92 });
93
94 let (fr, fg, fb) = if let Some(fc) = fill_col {
95 scales
96 .map_color(&Aesthetic::Fill, &fc[i])
97 .unwrap_or(self.fill)
98 } else {
99 self.fill
100 };
101 let style = RectStyle {
102 fill: Some((fr, fg, fb)),
103 stroke: Some(self.color),
104 stroke_width: 0.5,
105 alpha: self.alpha,
106 clip: !coord.is_polar(),
107 };
108
109 let raw = super::measured_value(data, i);
111 let series = super::series_key(data, i);
112 let tip = raw.as_ref().map(|v| match &series {
113 Some(s) => format!("{s}: {v}"),
114 None => v.clone(),
115 });
116 super::set_mark(backend, tip, Some(super::tip_value(&x_col[i])), series, raw);
117
118 if coord.is_polar() {
119 let points = super::col::polar_sector(
120 coord,
121 &plot_area,
122 nx - half_width,
123 nx + half_width,
124 ny_base,
125 ny,
126 );
127 backend.draw_polygon(&points, &style)?;
128 } else {
129 let (left_px, top_px) = coord.transform((nx - half_width, ny), &plot_area);
130 let (right_px, bottom_px) = coord.transform((nx + half_width, ny_base), &plot_area);
131 backend.draw_rect(
132 (left_px, top_px.min(bottom_px)),
133 (right_px, top_px.max(bottom_px)),
134 &style,
135 )?;
136 }
137 }
138 super::clear_mark(backend);
139
140 Ok(())
141 }
142
143 fn required_aes(&self) -> Vec<Aesthetic> {
144 vec![Aesthetic::X]
145 }
146
147 fn default_stat(&self) -> Box<dyn Stat> {
148 Box::new(StatCount)
149 }
150
151 fn default_position(&self) -> Box<dyn Position> {
152 Box::new(PositionStack)
153 }
154
155 fn default_params(&self) -> GeomParams {
156 GeomParams::default()
157 }
158
159 fn name(&self) -> &str {
160 "bar"
161 }
162
163 fn include_zero_baseline(&self) -> bool {
164 true
165 }
166
167 fn set_series_color(&mut self, color: (u8, u8, u8)) {
168 self.fill = color;
169 }
170}