Skip to main content

ggplot_rs/geom/
rug.rs

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
15/// Rug geometry — tick marks along axis margins.
16pub 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        };
56
57        let len = self.length;
58
59        if self.sides.contains('b') {
60            if let Some(xc) = x_col {
61                for val in xc {
62                    let nx = x_scale.map(|s| s.map(val)).unwrap_or(0.0);
63                    let (px, py_bottom) = coord.transform((nx, 0.0), &plot_area);
64                    let (_, py_up) = coord.transform((nx, len), &plot_area);
65                    backend.draw_line(&[(px, py_bottom), (px, py_up)], &style)?;
66                }
67            }
68        }
69
70        if self.sides.contains('t') {
71            if let Some(xc) = x_col {
72                for val in xc {
73                    let nx = x_scale.map(|s| s.map(val)).unwrap_or(0.0);
74                    let (px, py_top) = coord.transform((nx, 1.0), &plot_area);
75                    let (_, py_down) = coord.transform((nx, 1.0 - len), &plot_area);
76                    backend.draw_line(&[(px, py_top), (px, py_down)], &style)?;
77                }
78            }
79        }
80
81        if self.sides.contains('l') {
82            if let Some(yc) = y_col {
83                for val in yc {
84                    let ny = y_scale.map(|s| s.map(val)).unwrap_or(0.0);
85                    let (px_left, py) = coord.transform((0.0, ny), &plot_area);
86                    let (px_right, _) = coord.transform((len, ny), &plot_area);
87                    backend.draw_line(&[(px_left, py), (px_right, py)], &style)?;
88                }
89            }
90        }
91
92        if self.sides.contains('r') {
93            if let Some(yc) = y_col {
94                for val in yc {
95                    let ny = y_scale.map(|s| s.map(val)).unwrap_or(0.0);
96                    let (px_right, py) = coord.transform((1.0, ny), &plot_area);
97                    let (px_left, _) = coord.transform((1.0 - len, ny), &plot_area);
98                    backend.draw_line(&[(px_right, py), (px_left, py)], &style)?;
99                }
100            }
101        }
102
103        Ok(())
104    }
105
106    fn required_aes(&self) -> Vec<Aesthetic> {
107        vec![Aesthetic::X]
108    }
109
110    fn default_stat(&self) -> Box<dyn Stat> {
111        Box::new(StatIdentity)
112    }
113    fn default_position(&self) -> Box<dyn Position> {
114        Box::new(PositionIdentity)
115    }
116    fn default_params(&self) -> GeomParams {
117        GeomParams::default()
118    }
119    fn name(&self) -> &str {
120        "rug"
121    }
122
123    fn set_series_color(&mut self, color: (u8, u8, u8)) {
124        self.color = color;
125    }
126}