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 ..Default::default()
186 },
187 )?;
188 }
189 }
190 super::clear_mark(backend);
191
192 Ok(())
193 }
194
195 fn required_aes(&self) -> Vec<Aesthetic> {
196 vec![Aesthetic::Y]
197 }
198
199 fn default_stat(&self) -> Box<dyn Stat> {
200 Box::new(StatQQLine)
201 }
202
203 fn default_position(&self) -> Box<dyn Position> {
204 Box::new(PositionIdentity)
205 }
206
207 fn default_params(&self) -> GeomParams {
208 GeomParams::default()
209 }
210
211 fn name(&self) -> &str {
212 "qq_line"
213 }
214
215 fn set_series_color(&mut self, color: (u8, u8, u8)) {
216 self.color = color;
217 }
218}
219
220pub struct GeomQQBand {
225 pub fill: (u8, u8, u8),
226 pub alpha: f64,
227}
228
229impl Default for GeomQQBand {
230 fn default() -> Self {
231 GeomQQBand {
233 fill: (153, 153, 153),
234 alpha: 0.5,
235 }
236 }
237}
238
239impl Geom for GeomQQBand {
240 fn draw(
241 &self,
242 data: &DataFrame,
243 coord: &dyn Coord,
244 scales: &ScaleSet,
245 _theme: &Theme,
246 backend: &mut dyn DrawBackend,
247 ) -> Result<(), RenderError> {
248 super::ribbon::draw_bands(
249 data,
250 coord,
251 scales,
252 backend,
253 self.fill,
254 self.alpha,
255 super::ribbon::BandShape::Linear,
256 )
257 }
258
259 fn required_aes(&self) -> Vec<Aesthetic> {
260 vec![Aesthetic::Y]
261 }
262
263 fn default_stat(&self) -> Box<dyn Stat> {
264 Box::new(StatQQBand::default())
265 }
266
267 fn default_position(&self) -> Box<dyn Position> {
268 Box::new(PositionIdentity)
269 }
270
271 fn default_params(&self) -> GeomParams {
272 GeomParams::default()
273 }
274
275 fn name(&self) -> &str {
276 "qq_band"
277 }
278
279 fn allows_infinite(&self) -> bool {
280 true
281 }
282
283 fn set_series_color(&mut self, color: (u8, u8, u8)) {
284 self.fill = color;
285 }
286}