1use crate::aes::Aesthetic;
6use crate::render::backend::{
7 DrawBackend, LineStyle, Linetype, PointShape, PointStyle, RectStyle, TextAnchor, TextStyle,
8};
9use crate::render::RenderError;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum KeyGlyph {
14 Point,
16 Path,
18 Rect,
20 Polygon,
22 Boxplot,
24 Crossbar,
26 Pointrange,
28 Linerange,
30 Smooth,
32 Text,
34 Label,
36 Vline,
38 Vpath,
40 Abline,
42 Timeseries,
44 Dotplot,
46 Blank,
48}
49
50impl KeyGlyph {
51 pub fn parse(s: &str) -> Option<Self> {
53 Some(match s {
54 "point" => KeyGlyph::Point,
55 "path" => KeyGlyph::Path,
56 "rect" => KeyGlyph::Rect,
57 "polygon" => KeyGlyph::Polygon,
58 "boxplot" => KeyGlyph::Boxplot,
59 "crossbar" => KeyGlyph::Crossbar,
60 "pointrange" => KeyGlyph::Pointrange,
61 "linerange" => KeyGlyph::Linerange,
62 "smooth" => KeyGlyph::Smooth,
63 "text" => KeyGlyph::Text,
64 "label" => KeyGlyph::Label,
65 "vline" => KeyGlyph::Vline,
66 "vpath" => KeyGlyph::Vpath,
67 "abline" => KeyGlyph::Abline,
68 "timeseries" => KeyGlyph::Timeseries,
69 "dotplot" => KeyGlyph::Dotplot,
70 "blank" => KeyGlyph::Blank,
71 _ => return None,
72 })
73 }
74}
75
76#[derive(Clone, Debug, PartialEq)]
79pub struct LayerKey {
80 pub glyph: Option<KeyGlyph>,
81 pub aesthetics: Vec<Aesthetic>,
82}
83
84#[derive(Clone, Debug, Default)]
87pub(crate) struct KeyProps {
88 pub color: Option<(u8, u8, u8)>,
89 pub fill: Option<(u8, u8, u8)>,
90 pub shape: Option<PointShape>,
91 pub linetype: Option<Linetype>,
92 pub size: Option<f64>,
93 pub alpha: Option<f64>,
94 pub linewidth: Option<f64>,
95}
96
97pub(crate) fn draw_key(
99 backend: &mut dyn DrawBackend,
100 glyph: KeyGlyph,
101 props: &KeyProps,
102 ink: (u8, u8, u8),
103 (x, y, s): (f64, f64, f64),
104) -> Result<(), RenderError> {
105 let color = props.color.unwrap_or(ink);
106 let fill = props.fill.or(props.color).unwrap_or((89, 89, 89));
109 let poly_fill = props.fill.unwrap_or((89, 89, 89));
110 let alpha = props.alpha.unwrap_or(1.0);
111 let lw = props.linewidth.unwrap_or(1.0).min(s);
112 let lt = props.linetype.clone().unwrap_or(Linetype::Solid);
113 let line = |w: f64| LineStyle {
114 color,
115 width: w,
116 alpha,
117 linetype: lt.clone(),
118 ..Default::default()
119 };
120 let (cx, cy) = (x + s / 2.0, y + s / 2.0);
121 let at = |fx: f64, fy: f64| (x + fx * s, y + fy * s);
122 let rect = |fill: Option<(u8, u8, u8)>, stroke: Option<(u8, u8, u8)>| RectStyle {
123 fill,
124 stroke,
125 stroke_width: lw.max(0.5),
126 alpha,
127 clip: false,
128 corner_radius: 0.0,
129 };
130 let point = |backend: &mut dyn DrawBackend, r: f64| {
131 let shape = props.shape.unwrap_or(PointShape::Circle);
132 backend.draw_shape(
133 (cx, cy),
134 r,
135 &PointStyle {
136 color,
137 alpha,
138 filled: true,
139 shape,
140 },
141 )
142 };
143 match glyph {
144 KeyGlyph::Point => point(backend, props.size.unwrap_or(s / 5.0).min(s / 2.0))?,
145 KeyGlyph::Dotplot => point(backend, s / 3.0)?,
146 KeyGlyph::Path => backend.draw_line(&[at(0.1, 0.5), at(0.9, 0.5)], &line(lw))?,
147 KeyGlyph::Vline | KeyGlyph::Linerange | KeyGlyph::Vpath => {
148 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.9)], &line(lw))?
149 }
150 KeyGlyph::Abline => backend.draw_line(&[at(0.0, 1.0), at(1.0, 0.0)], &line(lw))?,
151 KeyGlyph::Timeseries => backend.draw_line(
152 &[at(0.0, 0.9), at(0.4, 0.1), at(0.6, 0.6), at(1.0, 0.1)],
153 &line(lw),
154 )?,
155 KeyGlyph::Rect => backend.draw_rect(at(0.0, 0.0), at(1.0, 1.0), &rect(Some(fill), None))?,
156 KeyGlyph::Polygon => backend.draw_rect(
157 at(0.0, 0.0),
158 at(1.0, 1.0),
159 &rect(Some(poly_fill), props.color),
160 )?,
161 KeyGlyph::Smooth => {
162 let band = props.fill.or(props.color).unwrap_or((153, 153, 153));
163 backend.draw_rect(
164 at(0.0, 0.0),
165 at(1.0, 1.0),
166 &RectStyle {
167 alpha: 0.4,
168 ..rect(Some(band), None)
169 },
170 )?;
171 backend.draw_line(&[at(0.1, 0.5), at(0.9, 0.5)], &line(lw.max(1.0)))?;
172 }
173 KeyGlyph::Boxplot => {
174 let fill = props.fill.unwrap_or((255, 255, 255));
175 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.25)], &line(0.5))?;
176 backend.draw_line(&[at(0.5, 0.75), at(0.5, 0.9)], &line(0.5))?;
177 backend.draw_rect(
178 at(0.125, 0.25),
179 at(0.875, 0.75),
180 &rect(Some(fill), Some(color)),
181 )?;
182 backend.draw_line(&[at(0.125, 0.5), at(0.875, 0.5)], &line(1.0))?;
183 }
184 KeyGlyph::Crossbar => {
185 let fill = props.fill.unwrap_or((255, 255, 255));
186 backend.draw_rect(
187 at(0.125, 0.25),
188 at(0.875, 0.75),
189 &rect(Some(fill), Some(color)),
190 )?;
191 backend.draw_line(&[at(0.125, 0.5), at(0.875, 0.5)], &line(1.0))?;
192 }
193 KeyGlyph::Pointrange => {
194 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.9)], &line(lw))?;
195 point(backend, props.size.unwrap_or(s / 6.0).min(s / 3.0))?;
196 }
197 KeyGlyph::Text | KeyGlyph::Label => {
198 if glyph == KeyGlyph::Label {
199 backend.draw_rect(
200 at(0.1, 0.15),
201 at(0.9, 0.85),
202 &RectStyle {
203 fill: Some((255, 255, 255)),
204 stroke: Some(color),
205 stroke_width: 0.5,
206 alpha,
207 clip: false,
208 corner_radius: 1.5,
209 },
210 )?;
211 }
212 backend.draw_text(
213 "a",
214 (cx, cy + s * 0.18),
215 &TextStyle {
216 color,
217 size: s * 0.6,
218 anchor: TextAnchor::Middle,
219 ..Default::default()
220 },
221 )?;
222 }
223 KeyGlyph::Blank => {}
224 }
225 Ok(())
226}