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};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::identity::StatIdentity;
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15pub struct GeomHline {
17 pub yintercept: f64,
18 pub color: (u8, u8, u8),
19 pub width: f64,
20 pub linetype: Linetype,
21 pub alpha: f64,
22}
23
24impl GeomHline {
25 pub fn new(yintercept: f64) -> Self {
26 GeomHline {
27 yintercept,
28 color: (0, 0, 0),
29 width: 1.0,
30 linetype: Linetype::Dashed,
31 alpha: 1.0,
32 }
33 }
34}
35
36impl Geom for GeomHline {
37 fn draw(
38 &self,
39 _data: &DataFrame,
40 coord: &dyn Coord,
41 scales: &ScaleSet,
42 _theme: &Theme,
43 backend: &mut dyn DrawBackend,
44 ) -> Result<(), RenderError> {
45 let plot_area = backend.plot_area();
46 let y_scale = scales.get(&Aesthetic::Y);
47
48 let ny = y_scale
49 .map(|s| s.map(&Value::Float(self.yintercept)))
50 .unwrap_or(0.5);
51
52 let (left, y_px) = coord.transform((0.0, ny), &plot_area);
53 let (right, _) = coord.transform((1.0, ny), &plot_area);
54
55 backend.draw_line(
56 &[(left, y_px), (right, y_px)],
57 &LineStyle {
58 color: self.color,
59 alpha: self.alpha,
60 width: self.width,
61 linetype: self.linetype,
62 },
63 )?;
64
65 Ok(())
66 }
67
68 fn required_aes(&self) -> Vec<Aesthetic> {
69 vec![]
70 }
71 fn default_stat(&self) -> Box<dyn Stat> {
72 Box::new(StatIdentity)
73 }
74 fn default_position(&self) -> Box<dyn Position> {
75 Box::new(PositionIdentity)
76 }
77 fn default_params(&self) -> GeomParams {
78 GeomParams::default()
79 }
80 fn name(&self) -> &str {
81 "hline"
82 }
83}
84
85pub struct GeomVline {
87 pub xintercept: f64,
88 pub color: (u8, u8, u8),
89 pub width: f64,
90 pub linetype: Linetype,
91 pub alpha: f64,
92}
93
94impl GeomVline {
95 pub fn new(xintercept: f64) -> Self {
96 GeomVline {
97 xintercept,
98 color: (0, 0, 0),
99 width: 1.0,
100 linetype: Linetype::Dashed,
101 alpha: 1.0,
102 }
103 }
104}
105
106impl Geom for GeomVline {
107 fn draw(
108 &self,
109 _data: &DataFrame,
110 coord: &dyn Coord,
111 scales: &ScaleSet,
112 _theme: &Theme,
113 backend: &mut dyn DrawBackend,
114 ) -> Result<(), RenderError> {
115 let plot_area = backend.plot_area();
116 let x_scale = scales.get(&Aesthetic::X);
117
118 let nx = x_scale
119 .map(|s| s.map(&Value::Float(self.xintercept)))
120 .unwrap_or(0.5);
121
122 let (x_px, top) = coord.transform((nx, 1.0), &plot_area);
123 let (_, bottom) = coord.transform((nx, 0.0), &plot_area);
124
125 backend.draw_line(
126 &[(x_px, top), (x_px, bottom)],
127 &LineStyle {
128 color: self.color,
129 alpha: self.alpha,
130 width: self.width,
131 linetype: self.linetype,
132 },
133 )?;
134
135 Ok(())
136 }
137
138 fn required_aes(&self) -> Vec<Aesthetic> {
139 vec![]
140 }
141 fn default_stat(&self) -> Box<dyn Stat> {
142 Box::new(StatIdentity)
143 }
144 fn default_position(&self) -> Box<dyn Position> {
145 Box::new(PositionIdentity)
146 }
147 fn default_params(&self) -> GeomParams {
148 GeomParams::default()
149 }
150 fn name(&self) -> &str {
151 "vline"
152 }
153}
154
155pub struct GeomAbline {
157 pub slope: f64,
158 pub intercept: f64,
159 pub color: (u8, u8, u8),
160 pub width: f64,
161 pub linetype: Linetype,
162 pub alpha: f64,
163}
164
165impl GeomAbline {
166 pub fn new(slope: f64, intercept: f64) -> Self {
167 GeomAbline {
168 slope,
169 intercept,
170 color: (0, 0, 0),
171 width: 1.0,
172 linetype: Linetype::Dashed,
173 alpha: 1.0,
174 }
175 }
176}
177
178impl Geom for GeomAbline {
179 fn draw(
180 &self,
181 _data: &DataFrame,
182 coord: &dyn Coord,
183 _scales: &ScaleSet,
184 _theme: &Theme,
185 backend: &mut dyn DrawBackend,
186 ) -> Result<(), RenderError> {
187 let plot_area = backend.plot_area();
188
189 let n_pts = 50;
191 let points: Vec<(f64, f64)> = (0..=n_pts)
192 .filter_map(|i| {
193 let nx = i as f64 / n_pts as f64;
194 let ny = self.slope * nx + self.intercept;
195 if (-0.1..=1.1).contains(&ny) {
196 Some(coord.transform((nx, ny), &plot_area))
197 } else {
198 None
199 }
200 })
201 .collect();
202
203 if points.len() >= 2 {
204 backend.draw_line(
205 &points,
206 &LineStyle {
207 color: self.color,
208 alpha: self.alpha,
209 width: self.width,
210 linetype: self.linetype,
211 },
212 )?;
213 }
214
215 Ok(())
216 }
217
218 fn required_aes(&self) -> Vec<Aesthetic> {
219 vec![]
220 }
221 fn default_stat(&self) -> Box<dyn Stat> {
222 Box::new(StatIdentity)
223 }
224 fn default_position(&self) -> Box<dyn Position> {
225 Box::new(PositionIdentity)
226 }
227 fn default_params(&self) -> GeomParams {
228 GeomParams::default()
229 }
230 fn name(&self) -> &str {
231 "abline"
232 }
233}