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};
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 GeomRug {
17 pub color: (u8, u8, u8),
18 pub alpha: f64,
19 pub length: f64,
20 pub sides: String,
21}
22
23impl Default for GeomRug {
24 fn default() -> Self {
25 GeomRug {
26 color: (0, 0, 0),
27 alpha: 0.5,
28 length: 0.03,
29 sides: "bl".to_string(),
30 }
31 }
32}
33
34impl Geom for GeomRug {
35 fn draw(
36 &self,
37 data: &DataFrame,
38 coord: &dyn Coord,
39 scales: &ScaleSet,
40 _theme: &Theme,
41 backend: &mut dyn DrawBackend,
42 ) -> Result<(), RenderError> {
43 let x_col = data.column("x");
44 let y_col = data.column("y");
45
46 let plot_area = backend.plot_area();
47 let x_scale = scales.get(&Aesthetic::X);
48 let y_scale = scales.get(&Aesthetic::Y);
49
50 let style = LineStyle {
51 color: self.color,
52 alpha: self.alpha,
53 width: 0.5,
54 linetype: Linetype::Solid,
55 ..Default::default()
56 };
57
58 let len = self.length;
59
60 if self.sides.contains('b') {
61 if let Some(xc) = x_col {
62 for val in xc {
63 let nx = x_scale.map(|s| s.map(val)).unwrap_or(0.0);
64 let (px, py_bottom) = coord.transform((nx, 0.0), &plot_area);
65 let (_, py_up) = coord.transform((nx, len), &plot_area);
66 backend.draw_line(&[(px, py_bottom), (px, py_up)], &style)?;
67 }
68 }
69 }
70
71 if self.sides.contains('t') {
72 if let Some(xc) = x_col {
73 for val in xc {
74 let nx = x_scale.map(|s| s.map(val)).unwrap_or(0.0);
75 let (px, py_top) = coord.transform((nx, 1.0), &plot_area);
76 let (_, py_down) = coord.transform((nx, 1.0 - len), &plot_area);
77 backend.draw_line(&[(px, py_top), (px, py_down)], &style)?;
78 }
79 }
80 }
81
82 if self.sides.contains('l') {
83 if let Some(yc) = y_col {
84 for val in yc {
85 let ny = y_scale.map(|s| s.map(val)).unwrap_or(0.0);
86 let (px_left, py) = coord.transform((0.0, ny), &plot_area);
87 let (px_right, _) = coord.transform((len, ny), &plot_area);
88 backend.draw_line(&[(px_left, py), (px_right, py)], &style)?;
89 }
90 }
91 }
92
93 if self.sides.contains('r') {
94 if let Some(yc) = y_col {
95 for val in yc {
96 let ny = y_scale.map(|s| s.map(val)).unwrap_or(0.0);
97 let (px_right, py) = coord.transform((1.0, ny), &plot_area);
98 let (px_left, _) = coord.transform((1.0 - len, ny), &plot_area);
99 backend.draw_line(&[(px_right, py), (px_left, py)], &style)?;
100 }
101 }
102 }
103
104 Ok(())
105 }
106
107 fn required_aes(&self) -> Vec<Aesthetic> {
108 vec![Aesthetic::X]
109 }
110
111 fn default_stat(&self) -> Box<dyn Stat> {
112 Box::new(StatIdentity)
113 }
114 fn default_position(&self) -> Box<dyn Position> {
115 Box::new(PositionIdentity)
116 }
117 fn default_params(&self) -> GeomParams {
118 GeomParams::default()
119 }
120 fn name(&self) -> &str {
121 "rug"
122 }
123
124 fn apply_element_geom(&mut self, el: &crate::theme::elements::ElementGeom) {
125 if let (true, Some(ink)) = (self.color == (0, 0, 0), el.ink) {
126 self.color = ink;
127 }
128 }
129
130 fn set_series_color(&mut self, color: (u8, u8, u8)) {
131 self.color = color;
132 }
133}