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
82 backend.draw_shape(
83 (px, py),
84 self.size,
85 &PointStyle {
86 color: pt_color,
87 alpha: self.alpha,
88 filled: true,
89 shape: PointShape::Circle,
90 },
91 )?;
92 }
93 super::clear_mark(backend);
94
95 Ok(())
96 }
97
98 fn required_aes(&self) -> Vec<Aesthetic> {
99 vec![Aesthetic::Y]
100 }
101
102 fn default_stat(&self) -> Box<dyn Stat> {
103 Box::new(StatQQ)
104 }
105
106 fn default_position(&self) -> Box<dyn Position> {
107 Box::new(PositionIdentity)
108 }
109
110 fn default_params(&self) -> GeomParams {
111 GeomParams::default()
112 }
113
114 fn name(&self) -> &str {
115 "qq"
116 }
117
118 fn set_series_color(&mut self, color: (u8, u8, u8)) {
119 self.color = color;
120 }
121}
122
123pub struct GeomQQLine {
125 pub color: (u8, u8, u8),
126 pub width: f64,
127 pub alpha: f64,
128}
129
130impl Default for GeomQQLine {
131 fn default() -> Self {
132 GeomQQLine {
133 color: (255, 0, 0),
134 width: 1.0,
135 alpha: 1.0,
136 }
137 }
138}
139
140impl Geom for GeomQQLine {
141 fn draw(
142 &self,
143 data: &DataFrame,
144 coord: &dyn Coord,
145 scales: &ScaleSet,
146 _theme: &Theme,
147 backend: &mut dyn DrawBackend,
148 ) -> Result<(), RenderError> {
149 let x_col = data
150 .column("x")
151 .ok_or(RenderError::MissingAesthetic("x".into()))?;
152 let y_col = data
153 .column("y")
154 .ok_or(RenderError::MissingAesthetic("y".into()))?;
155
156 let plot_area = backend.plot_area();
157 let x_scale = scales.get(&Aesthetic::X);
158 let y_scale = scales.get(&Aesthetic::Y);
159
160 for rows in super::support::row_groups(data) {
162 let points: Vec<(f64, f64)> = rows
163 .iter()
164 .map(|&i| {
165 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
166 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
167 coord.transform((nx, ny), &plot_area)
168 })
169 .collect();
170 let first = rows[0];
171 let color = data
172 .column("color")
173 .and_then(|c| scales.map_color(&Aesthetic::Color, &c[first]))
174 .unwrap_or(self.color);
175 let series = super::series_key(data, first);
176 super::set_mark(backend, series.clone(), None, series, None);
177 if points.len() >= 2 {
178 backend.draw_line(
179 &points,
180 &LineStyle {
181 color,
182 alpha: self.alpha,
183 width: self.width,
184 linetype: Linetype::Dashed,
185 },
186 )?;
187 }
188 }
189 super::clear_mark(backend);
190
191 Ok(())
192 }
193
194 fn required_aes(&self) -> Vec<Aesthetic> {
195 vec![Aesthetic::Y]
196 }
197
198 fn default_stat(&self) -> Box<dyn Stat> {
199 Box::new(StatQQLine)
200 }
201
202 fn default_position(&self) -> Box<dyn Position> {
203 Box::new(PositionIdentity)
204 }
205
206 fn default_params(&self) -> GeomParams {
207 GeomParams::default()
208 }
209
210 fn name(&self) -> &str {
211 "qq_line"
212 }
213
214 fn set_series_color(&mut self, color: (u8, u8, u8)) {
215 self.color = color;
216 }
217}
218
219pub struct GeomQQBand {
224 pub fill: (u8, u8, u8),
225 pub alpha: f64,
226}
227
228impl Default for GeomQQBand {
229 fn default() -> Self {
230 GeomQQBand {
232 fill: (153, 153, 153),
233 alpha: 0.5,
234 }
235 }
236}
237
238impl Geom for GeomQQBand {
239 fn draw(
240 &self,
241 data: &DataFrame,
242 coord: &dyn Coord,
243 scales: &ScaleSet,
244 _theme: &Theme,
245 backend: &mut dyn DrawBackend,
246 ) -> Result<(), RenderError> {
247 super::ribbon::draw_bands(
248 data,
249 coord,
250 scales,
251 backend,
252 self.fill,
253 self.alpha,
254 super::ribbon::BandShape::Linear,
255 )
256 }
257
258 fn required_aes(&self) -> Vec<Aesthetic> {
259 vec![Aesthetic::Y]
260 }
261
262 fn default_stat(&self) -> Box<dyn Stat> {
263 Box::new(StatQQBand::default())
264 }
265
266 fn default_position(&self) -> Box<dyn Position> {
267 Box::new(PositionIdentity)
268 }
269
270 fn default_params(&self) -> GeomParams {
271 GeomParams::default()
272 }
273
274 fn name(&self) -> &str {
275 "qq_band"
276 }
277
278 fn allows_infinite(&self) -> bool {
279 true
280 }
281
282 fn set_series_color(&mut self, color: (u8, u8, u8)) {
283 self.fill = color;
284 }
285}