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, 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 backend.draw_shape(
63 (px, py),
64 self.size,
65 &PointStyle {
66 color: pt_color,
67 alpha: self.alpha,
68 filled: true,
69 shape: PointShape::Circle,
70 },
71 )?;
72 }
73
74 Ok(())
75 }
76
77 fn required_aes(&self) -> Vec<Aesthetic> {
78 vec![Aesthetic::Y]
79 }
80
81 fn default_stat(&self) -> Box<dyn Stat> {
82 Box::new(StatQQ)
83 }
84
85 fn default_position(&self) -> Box<dyn Position> {
86 Box::new(PositionIdentity)
87 }
88
89 fn default_params(&self) -> GeomParams {
90 GeomParams::default()
91 }
92
93 fn name(&self) -> &str {
94 "qq"
95 }
96
97 fn set_series_color(&mut self, color: (u8, u8, u8)) {
98 self.color = color;
99 }
100}
101
102pub struct GeomQQLine {
104 pub color: (u8, u8, u8),
105 pub width: f64,
106 pub alpha: f64,
107}
108
109impl Default for GeomQQLine {
110 fn default() -> Self {
111 GeomQQLine {
112 color: (255, 0, 0),
113 width: 1.0,
114 alpha: 1.0,
115 }
116 }
117}
118
119impl Geom for GeomQQLine {
120 fn draw(
121 &self,
122 data: &DataFrame,
123 coord: &dyn Coord,
124 scales: &ScaleSet,
125 _theme: &Theme,
126 backend: &mut dyn DrawBackend,
127 ) -> Result<(), RenderError> {
128 let x_col = data
129 .column("x")
130 .ok_or(RenderError::MissingAesthetic("x".into()))?;
131 let y_col = data
132 .column("y")
133 .ok_or(RenderError::MissingAesthetic("y".into()))?;
134
135 let plot_area = backend.plot_area();
136 let x_scale = scales.get(&Aesthetic::X);
137 let y_scale = scales.get(&Aesthetic::Y);
138
139 let points: Vec<(f64, f64)> = (0..data.nrows())
140 .map(|i| {
141 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
142 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
143 coord.transform((nx, ny), &plot_area)
144 })
145 .collect();
146
147 if points.len() >= 2 {
148 backend.draw_line(
149 &points,
150 &LineStyle {
151 color: self.color,
152 alpha: self.alpha,
153 width: self.width,
154 linetype: Linetype::Dashed,
155 },
156 )?;
157 }
158
159 Ok(())
160 }
161
162 fn required_aes(&self) -> Vec<Aesthetic> {
163 vec![Aesthetic::Y]
164 }
165
166 fn default_stat(&self) -> Box<dyn Stat> {
167 Box::new(StatQQLine)
168 }
169
170 fn default_position(&self) -> Box<dyn Position> {
171 Box::new(PositionIdentity)
172 }
173
174 fn default_params(&self) -> GeomParams {
175 GeomParams::default()
176 }
177
178 fn name(&self) -> &str {
179 "qq_line"
180 }
181
182 fn set_series_color(&mut self, color: (u8, u8, u8)) {
183 self.color = color;
184 }
185}