ggplot_rs/geom/
bracket.rs1use 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, FontFace, LineStyle, Linetype, TextAnchor, TextStyle};
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 GeomBracket {
20 pub color: (u8, u8, u8),
21 pub line_width: f64,
22 pub tip_length: f64,
24 pub label_size: f64,
26}
27
28impl Default for GeomBracket {
29 fn default() -> Self {
30 GeomBracket {
31 color: (0, 0, 0),
32 line_width: 1.0,
33 tip_length: 8.0,
34 label_size: 12.0,
35 }
36 }
37}
38
39impl Geom for GeomBracket {
40 fn draw(
41 &self,
42 data: &DataFrame,
43 coord: &dyn Coord,
44 scales: &ScaleSet,
45 _theme: &Theme,
46 backend: &mut dyn DrawBackend,
47 ) -> Result<(), RenderError> {
48 let xmin_col = data
49 .column("xmin")
50 .ok_or(RenderError::MissingAesthetic("xmin".into()))?;
51 let xmax_col = data
52 .column("xmax")
53 .ok_or(RenderError::MissingAesthetic("xmax".into()))?;
54 let y_col = data
55 .column("y")
56 .ok_or(RenderError::MissingAesthetic("y".into()))?;
57 let label_col = data.column("label");
58
59 let plot_area = backend.plot_area();
60 let x_scale = scales.get(&Aesthetic::X);
61 let y_scale = scales.get(&Aesthetic::Y);
62
63 for i in 0..data.nrows() {
64 let nxmin = x_scale.map(|s| s.map(&xmin_col[i])).unwrap_or(0.0);
65 let nxmax = x_scale.map(|s| s.map(&xmax_col[i])).unwrap_or(0.0);
66 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
67
68 let (px_min, py) = coord.transform((nxmin, ny), &plot_area);
69 let (px_max, _) = coord.transform((nxmax, ny), &plot_area);
70
71 let tip = self.tip_length;
74 backend.draw_line(
75 &[
76 (px_min, py + tip),
77 (px_min, py),
78 (px_max, py),
79 (px_max, py + tip),
80 ],
81 &LineStyle {
82 color: self.color,
83 alpha: 1.0,
84 width: self.line_width,
85 linetype: Linetype::Solid,
86 },
87 )?;
88
89 if let Some(lc) = label_col {
91 let text = lc[i].to_group_key();
92 if !text.is_empty() {
93 let cx = (px_min + px_max) / 2.0;
94 backend.draw_text(
95 &text,
96 (cx, py - self.label_size * 0.3 - 2.0),
97 &TextStyle {
98 color: self.color,
99 size: self.label_size,
100 anchor: TextAnchor::Middle,
101 angle: 0.0,
102 family: None,
103 face: FontFace::Plain,
104 },
105 )?;
106 }
107 }
108 }
109
110 Ok(())
111 }
112
113 fn required_aes(&self) -> Vec<Aesthetic> {
114 vec![Aesthetic::Xmin, Aesthetic::Xmax, Aesthetic::Y]
115 }
116
117 fn default_stat(&self) -> Box<dyn Stat> {
118 Box::new(StatIdentity)
119 }
120 fn default_position(&self) -> Box<dyn Position> {
121 Box::new(PositionIdentity)
122 }
123 fn default_params(&self) -> GeomParams {
124 GeomParams::default()
125 }
126 fn name(&self) -> &str {
127 "bracket"
128 }
129
130 fn set_series_color(&mut self, color: (u8, u8, u8)) {
131 self.color = color;
132 }
133}