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 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 color_col = data.column("color");
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 base_ny = y_scale.map(|s| s.map(&Value::Float(0.0))).unwrap_or(0.0);
57
58 let group_col = color_col.or(fill_col);
60 let groups: Vec<(String, Vec<usize>)> = if let Some(gc) = group_col {
61 let mut groups: Vec<(String, Vec<usize>)> = Vec::new();
62 for (i, v) in gc.iter().enumerate() {
63 let key = v.to_group_key();
64 if let Some(entry) = groups.iter_mut().find(|(k, _)| k == &key) {
65 entry.1.push(i);
66 } else {
67 groups.push((key, vec![i]));
68 }
69 }
70 groups
71 } else {
72 vec![("".to_string(), (0..data.nrows()).collect())]
74 };
75
76 for (group_label, indices) in &groups {
77 if indices.is_empty() {
78 continue;
79 }
80 let first_idx = indices[0];
81
82 let fill_color = fill_col
84 .and_then(|fc| scales.map_color(&Aesthetic::Fill, &fc[first_idx]))
85 .or_else(|| {
86 color_col.and_then(|cc| scales.map_color(&Aesthetic::Color, &cc[first_idx]))
87 })
88 .unwrap_or(self.fill);
89
90 let line_color = color_col
92 .and_then(|cc| scales.map_color(&Aesthetic::Color, &cc[first_idx]))
93 .or_else(|| {
94 fill_col.and_then(|fc| scales.map_color(&Aesthetic::Fill, &fc[first_idx]))
95 })
96 .unwrap_or(self.color);
97
98 let mut upper: Vec<(f64, f64)> = Vec::new();
99 let mut lower: Vec<(f64, f64)> = Vec::new();
100
101 for &i in indices {
102 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
103 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
104 upper.push(coord.transform((nx, ny), &plot_area));
105 lower.push(coord.transform((nx, base_ny), &plot_area));
106 }
107
108 let mut polygon = upper.clone();
110 lower.reverse();
111 polygon.extend(lower);
112
113 if polygon.len() >= 3 {
114 backend.draw_polygon(
115 &polygon,
116 &RectStyle {
117 fill: Some(fill_color),
118 stroke: None,
119 stroke_width: 0.0,
120 alpha: self.alpha,
121 clip: true,
122 corner_radius: 0.0,
123 },
124 )?;
125 }
126
127 if upper.len() >= 2 {
129 backend.draw_line(
130 &upper,
131 &LineStyle {
132 color: line_color,
133 alpha: 1.0,
134 width: self.line_width,
135 linetype: Linetype::Solid,
136 ..Default::default()
137 },
138 )?;
139 }
140
141 let step = (indices.len() / 48).max(1);
145 for (k, &i) in indices.iter().enumerate() {
146 if k % step != 0 {
147 continue;
148 }
149 let dens = y_col[i]
150 .as_f64()
151 .map(|f| format!("{f:.4}"))
152 .unwrap_or_default();
153 let tip = if group_label.is_empty() {
154 format!("density: {dens}")
155 } else {
156 format!("{group_label}: {dens}")
157 };
158 super::set_mark(
159 backend,
160 Some(tip),
161 Some(super::tip_value(&x_col[i])),
162 super::series_key(data, i),
163 super::measured_value(data, i),
164 );
165 super::set_mark_formatted(
166 backend,
167 scales,
168 Some(&x_col[i]),
169 &Aesthetic::Y,
170 super::measured_raw(data, i),
171 );
172 backend.draw_shape(
173 upper[k],
174 0.6,
175 &PointStyle {
176 color: line_color,
177 alpha: 0.0,
178 filled: true,
179 shape: PointShape::Circle,
180 },
181 )?;
182 }
183 super::clear_mark(backend);
184 }
185
186 Ok(())
187 }
188
189 fn required_aes(&self) -> Vec<Aesthetic> {
190 vec![Aesthetic::X]
191 }
192
193 fn default_stat(&self) -> Box<dyn Stat> {
194 Box::new(StatDensity::default())
195 }
196
197 fn default_position(&self) -> Box<dyn Position> {
198 Box::new(PositionIdentity)
199 }
200
201 fn default_params(&self) -> GeomParams {
202 GeomParams::default()
203 }
204
205 fn name(&self) -> &str {
206 "density"
207 }
208
209 fn apply_element_geom(&mut self, el: &crate::theme::elements::ElementGeom) {
210 if let (true, Some(ink)) = (self.color == (50, 50, 50), el.ink) {
211 self.color = ink;
212 }
213 }
214
215 fn set_series_color(&mut self, color: (u8, u8, u8)) {
216 self.fill = color;
217 }
218}