1use 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, LineStyle, Linetype, PointShape, PointStyle};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::qq::{StatQQ, StatQQBand, StatQQLine};
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15pub struct GeomQQ {
17 pub size: f64,
18 pub color: (u8, u8, u8),
19 pub alpha: f64,
20}
21
22impl Default for GeomQQ {
23 fn default() -> Self {
24 GeomQQ {
25 size: 3.0,
26 color: (0, 0, 0),
27 alpha: 1.0,
28 }
29 }
30}
31
32impl Geom for GeomQQ {
33 fn draw(
34 &self,
35 data: &DataFrame,
36 coord: &dyn Coord,
37 scales: &ScaleSet,
38 _theme: &Theme,
39 backend: &mut dyn DrawBackend,
40 ) -> Result<(), RenderError> {
41 let x_col = data
42 .column("x")
43 .ok_or(RenderError::MissingAesthetic("x".into()))?;
44 let y_col = data
45 .column("y")
46 .ok_or(RenderError::MissingAesthetic("y".into()))?;
47 let color_col = data.column("color");
48
49 let plot_area = backend.plot_area();
50 let x_scale = scales.get(&Aesthetic::X);
51 let y_scale = scales.get(&Aesthetic::Y);
52
53 for i in 0..data.nrows() {
54 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
55 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
56 let (px, py) = coord.transform((nx, ny), &plot_area);
57
58 let pt_color = color_col
59 .and_then(|cc| scales.map_color(&Aesthetic::Color, &cc[i]))
60 .unwrap_or(self.color);
61
62 let xy = format!(
65 "({}, {})",
66 super::tip_value(&x_col[i]),
67 super::tip_value(&y_col[i])
68 );
69 let series = super::series_key(data, i);
70 let tip = Some(match &series {
71 Some(s) => format!("{s}: {xy}"),
72 None => xy,
73 });
74 super::set_mark(
75 backend,
76 tip,
77 Some(super::tip_value(&x_col[i])),
78 series,
79 super::raw_value(&y_col[i]),
80 );
81 super::set_mark_formatted(
82 backend,
83 scales,
84 Some(&x_col[i]),
85 &Aesthetic::Y,
86 Some(&y_col[i]),
87 );
88
89 backend.draw_shape(
90 (px, py),
91 self.size,
92 &PointStyle {
93 color: pt_color,
94 alpha: self.alpha,
95 filled: true,
96 shape: PointShape::Circle,
97 },
98 )?;
99 }
100 super::clear_mark(backend);
101
102 Ok(())
103 }
104
105 fn required_aes(&self) -> Vec<Aesthetic> {
106 vec![Aesthetic::Y]
107 }
108
109 fn default_stat(&self) -> Box<dyn Stat> {
110 Box::new(StatQQ)
111 }
112
113 fn default_position(&self) -> Box<dyn Position> {
114 Box::new(PositionIdentity)
115 }
116
117 fn default_params(&self) -> GeomParams {
118 GeomParams::default()
119 }
120
121 fn name(&self) -> &str {
122 "qq"
123 }
124
125 fn apply_element_geom(&mut self, el: &crate::theme::elements::ElementGeom) {
126 if let (true, Some(ink)) = (self.color == (0, 0, 0), el.ink) {
127 self.color = ink;
128 }
129 }
130
131 fn set_series_color(&mut self, color: (u8, u8, u8)) {
132 self.color = color;
133 }
134}
135
136pub struct GeomQQLine {
138 pub color: (u8, u8, u8),
139 pub width: f64,
140 pub alpha: f64,
141}
142
143impl Default for GeomQQLine {
144 fn default() -> Self {
145 GeomQQLine {
146 color: (255, 0, 0),
147 width: 1.0,
148 alpha: 1.0,
149 }
150 }
151}
152
153impl Geom for GeomQQLine {
154 fn draw(
155 &self,
156 data: &DataFrame,
157 coord: &dyn Coord,
158 scales: &ScaleSet,
159 _theme: &Theme,
160 backend: &mut dyn DrawBackend,
161 ) -> Result<(), RenderError> {
162 let x_col = data
163 .column("x")
164 .ok_or(RenderError::MissingAesthetic("x".into()))?;
165 let y_col = data
166 .column("y")
167 .ok_or(RenderError::MissingAesthetic("y".into()))?;
168
169 let plot_area = backend.plot_area();
170 let x_scale = scales.get(&Aesthetic::X);
171 let y_scale = scales.get(&Aesthetic::Y);
172
173 for rows in super::support::row_groups(data) {
175 let points: Vec<(f64, f64)> = rows
176 .iter()
177 .map(|&i| {
178 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
179 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
180 coord.transform((nx, ny), &plot_area)
181 })
182 .collect();
183 let first = rows[0];
184 let color = data
185 .column("color")
186 .and_then(|c| scales.map_color(&Aesthetic::Color, &c[first]))
187 .unwrap_or(self.color);
188 let series = super::series_key(data, first);
189 super::set_mark(backend, series.clone(), None, series, None);
190 super::set_mark_formatted(backend, scales, None, &Aesthetic::Y, None);
191 if points.len() >= 2 {
192 backend.draw_line(
193 &points,
194 &LineStyle {
195 color,
196 alpha: self.alpha,
197 width: self.width,
198 linetype: Linetype::Dashed,
199 ..Default::default()
200 },
201 )?;
202 }
203 }
204 super::clear_mark(backend);
205
206 Ok(())
207 }
208
209 fn required_aes(&self) -> Vec<Aesthetic> {
210 vec![Aesthetic::Y]
211 }
212
213 fn default_stat(&self) -> Box<dyn Stat> {
214 Box::new(StatQQLine)
215 }
216
217 fn default_position(&self) -> Box<dyn Position> {
218 Box::new(PositionIdentity)
219 }
220
221 fn default_params(&self) -> GeomParams {
222 GeomParams::default()
223 }
224
225 fn name(&self) -> &str {
226 "qq_line"
227 }
228
229 fn set_series_color(&mut self, color: (u8, u8, u8)) {
230 self.color = color;
231 }
232}
233
234pub struct GeomQQBand {
239 pub fill: (u8, u8, u8),
240 pub alpha: f64,
241}
242
243impl Default for GeomQQBand {
244 fn default() -> Self {
245 GeomQQBand {
247 fill: (153, 153, 153),
248 alpha: 0.5,
249 }
250 }
251}
252
253impl Geom for GeomQQBand {
254 fn draw(
255 &self,
256 data: &DataFrame,
257 coord: &dyn Coord,
258 scales: &ScaleSet,
259 _theme: &Theme,
260 backend: &mut dyn DrawBackend,
261 ) -> Result<(), RenderError> {
262 super::ribbon::draw_bands(
263 data,
264 coord,
265 scales,
266 backend,
267 self.fill,
268 self.alpha,
269 super::ribbon::BandShape::Linear,
270 )
271 }
272
273 fn required_aes(&self) -> Vec<Aesthetic> {
274 vec![Aesthetic::Y]
275 }
276
277 fn default_stat(&self) -> Box<dyn Stat> {
278 Box::new(StatQQBand::default())
279 }
280
281 fn default_position(&self) -> Box<dyn Position> {
282 Box::new(PositionIdentity)
283 }
284
285 fn default_params(&self) -> GeomParams {
286 GeomParams::default()
287 }
288
289 fn name(&self) -> &str {
290 "qq_band"
291 }
292
293 fn allows_infinite(&self) -> bool {
294 true
295 }
296
297 fn set_series_color(&mut self, color: (u8, u8, u8)) {
298 self.fill = color;
299 }
300}