1use crate::aes::Aesthetic;
2use crate::coord::Coord;
3use crate::data::{DataFrame, Value};
4use crate::position::identity::PositionIdentity;
5use crate::position::Position;
6use crate::render::backend::{DrawBackend, LineStyle, Linetype, PointShape, PointStyle, RectStyle};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::density::StatDensity;
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15pub struct GeomDensity {
17 pub fill: (u8, u8, u8),
18 pub color: (u8, u8, u8),
19 pub alpha: f64,
20 pub line_width: f64,
21}
22
23impl Default for GeomDensity {
24 fn default() -> Self {
25 GeomDensity {
26 fill: (97, 156, 255),
27 color: (50, 50, 50),
28 alpha: 0.3,
29 line_width: 1.0,
30 }
31 }
32}
33
34impl Geom for GeomDensity {
35 fn key_fill(&self) -> Option<((u8, u8, u8), f64)> {
36 Some((self.fill, self.alpha))
37 }
38
39 fn draw(
40 &self,
41 data: &DataFrame,
42 coord: &dyn Coord,
43 scales: &ScaleSet,
44 _theme: &Theme,
45 backend: &mut dyn DrawBackend,
46 ) -> Result<(), RenderError> {
47 let x_col = data
48 .column("x")
49 .ok_or(RenderError::MissingAesthetic("x".into()))?;
50 let y_col = data
51 .column("y")
52 .ok_or(RenderError::MissingAesthetic("y".into()))?;
53 let fill_col = data.column("fill");
54 let color_col = data.column("color");
55
56 let plot_area = backend.plot_area();
57 let x_scale = scales.get(&Aesthetic::X);
58 let y_scale = scales.get(&Aesthetic::Y);
59
60 let base_ny = y_scale.map(|s| s.map(&Value::Float(0.0))).unwrap_or(0.0);
61
62 let group_col = color_col.or(fill_col);
64 let groups: Vec<(String, Vec<usize>)> = if let Some(gc) = group_col {
65 let mut groups: Vec<(String, Vec<usize>)> = Vec::new();
66 for (i, v) in gc.iter().enumerate() {
67 let key = v.to_group_key();
68 if let Some(entry) = groups.iter_mut().find(|(k, _)| k == &key) {
69 entry.1.push(i);
70 } else {
71 groups.push((key, vec![i]));
72 }
73 }
74 groups
75 } else {
76 vec![("".to_string(), (0..data.nrows()).collect())]
78 };
79
80 for (group_label, indices) in &groups {
81 if indices.is_empty() {
82 continue;
83 }
84 let first_idx = indices[0];
85
86 let fill_color = fill_col
88 .and_then(|fc| scales.map_color(&Aesthetic::Fill, &fc[first_idx]))
89 .or_else(|| {
90 color_col.and_then(|cc| scales.map_color(&Aesthetic::Color, &cc[first_idx]))
91 })
92 .unwrap_or(self.fill);
93
94 let line_color = color_col
96 .and_then(|cc| scales.map_color(&Aesthetic::Color, &cc[first_idx]))
97 .or_else(|| {
98 fill_col.and_then(|fc| scales.map_color(&Aesthetic::Fill, &fc[first_idx]))
99 })
100 .unwrap_or(self.color);
101
102 let mut upper: Vec<(f64, f64)> = Vec::new();
103 let mut lower: Vec<(f64, f64)> = Vec::new();
104
105 for &i in indices {
106 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
107 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
108 upper.push(coord.transform((nx, ny), &plot_area));
109 lower.push(coord.transform((nx, base_ny), &plot_area));
110 }
111
112 let mut polygon = upper.clone();
114 lower.reverse();
115 polygon.extend(lower);
116
117 if polygon.len() >= 3 {
118 backend.draw_polygon(
119 &polygon,
120 &RectStyle {
121 fill: Some(fill_color),
122 stroke: None,
123 stroke_width: 0.0,
124 alpha: self.alpha,
125 clip: true,
126 corner_radius: 0.0,
127 },
128 )?;
129 }
130
131 if upper.len() >= 2 {
133 backend.draw_line(
134 &upper,
135 &LineStyle {
136 color: line_color,
137 alpha: 1.0,
138 width: self.line_width,
139 linetype: Linetype::Solid,
140 ..Default::default()
141 },
142 )?;
143 }
144
145 let step = (indices.len() / 48).max(1);
149 for (k, &i) in indices.iter().enumerate() {
150 if k % step != 0 {
151 continue;
152 }
153 let dens = y_col[i]
154 .as_f64()
155 .map(|f| format!("{f:.4}"))
156 .unwrap_or_default();
157 let tip = if group_label.is_empty() {
158 format!("density: {dens}")
159 } else {
160 format!("{group_label}: {dens}")
161 };
162 super::set_mark(
163 backend,
164 Some(tip),
165 Some(super::tip_value(&x_col[i])),
166 super::series_key(data, i),
167 super::measured_value(data, i),
168 );
169 super::set_mark_formatted(
170 backend,
171 scales,
172 Some(&x_col[i]),
173 &Aesthetic::Y,
174 super::measured_raw(data, i),
175 );
176 backend.draw_shape(
177 upper[k],
178 0.6,
179 &PointStyle {
180 color: line_color,
181 alpha: 0.0,
182 filled: true,
183 shape: PointShape::Circle,
184 },
185 )?;
186 }
187 super::clear_mark(backend);
188 }
189
190 Ok(())
191 }
192
193 fn required_aes(&self) -> Vec<Aesthetic> {
194 vec![Aesthetic::X]
195 }
196
197 fn default_stat(&self) -> Box<dyn Stat> {
198 Box::new(StatDensity::default())
199 }
200
201 fn default_position(&self) -> Box<dyn Position> {
202 Box::new(PositionIdentity)
203 }
204
205 fn default_params(&self) -> GeomParams {
206 GeomParams::default()
207 }
208
209 fn name(&self) -> &str {
210 "density"
211 }
212
213 fn apply_element_geom(&mut self, el: &crate::theme::elements::ElementGeom) {
214 if let (true, Some(ink)) = (self.color == (50, 50, 50), el.ink) {
215 self.color = ink;
216 }
217 }
218
219 fn set_series_color(&mut self, color: (u8, u8, u8)) {
220 self.fill = color;
221 }
222}