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 pub fill: Option<((u8, u8, u8), f64)>,
84}
85
86#[derive(Clone, Debug, Default)]
89pub(crate) struct KeyProps {
90 pub color: Option<(u8, u8, u8)>,
91 pub fill: Option<(u8, u8, u8)>,
92 pub shape: Option<PointShape>,
93 pub linetype: Option<Linetype>,
94 pub size: Option<f64>,
95 pub alpha: Option<f64>,
96 pub linewidth: Option<f64>,
97}
98
99pub(crate) fn draw_key(
101 backend: &mut dyn DrawBackend,
102 glyph: KeyGlyph,
103 props: &KeyProps,
104 ink: (u8, u8, u8),
105 (x, y, s): (f64, f64, f64),
106) -> Result<(), RenderError> {
107 let color = props.color.unwrap_or(ink);
108 let fill = props.fill.or(props.color).unwrap_or((89, 89, 89));
111 let poly_fill = props.fill.unwrap_or((89, 89, 89));
112 let alpha = props.alpha.unwrap_or(1.0);
113 let lw = props.linewidth.unwrap_or(1.0).min(s);
114 let lt = props.linetype.clone().unwrap_or(Linetype::Solid);
115 let line = |w: f64| LineStyle {
116 color,
117 width: w,
118 alpha,
119 linetype: lt.clone(),
120 ..Default::default()
121 };
122 let (cx, cy) = (x + s / 2.0, y + s / 2.0);
123 let at = |fx: f64, fy: f64| (x + fx * s, y + fy * s);
124 let rect = |fill: Option<(u8, u8, u8)>, stroke: Option<(u8, u8, u8)>| RectStyle {
125 fill,
126 stroke,
127 stroke_width: lw.max(0.5),
128 alpha,
129 clip: false,
130 corner_radius: 0.0,
131 };
132 let point = |backend: &mut dyn DrawBackend, r: f64| {
133 let shape = props.shape.unwrap_or(PointShape::Circle);
134 backend.draw_shape(
135 (cx, cy),
136 r,
137 &PointStyle {
138 color,
139 alpha,
140 filled: true,
141 shape,
142 },
143 )
144 };
145 match glyph {
146 KeyGlyph::Point => point(backend, props.size.unwrap_or(s / 5.0).min(s / 2.0))?,
147 KeyGlyph::Dotplot => point(backend, s / 3.0)?,
148 KeyGlyph::Path => backend.draw_line(&[at(0.1, 0.5), at(0.9, 0.5)], &line(lw))?,
149 KeyGlyph::Vline | KeyGlyph::Linerange | KeyGlyph::Vpath => {
150 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.9)], &line(lw))?
151 }
152 KeyGlyph::Abline => backend.draw_line(&[at(0.0, 1.0), at(1.0, 0.0)], &line(lw))?,
153 KeyGlyph::Timeseries => backend.draw_line(
154 &[at(0.0, 0.9), at(0.4, 0.1), at(0.6, 0.6), at(1.0, 0.1)],
155 &line(lw),
156 )?,
157 KeyGlyph::Rect => backend.draw_rect(at(0.0, 0.0), at(1.0, 1.0), &rect(Some(fill), None))?,
158 KeyGlyph::Polygon => backend.draw_rect(
159 at(0.0, 0.0),
160 at(1.0, 1.0),
161 &rect(Some(poly_fill), props.color),
162 )?,
163 KeyGlyph::Smooth => {
164 let band = props.fill.or(props.color).unwrap_or((153, 153, 153));
165 backend.draw_rect(
166 at(0.0, 0.0),
167 at(1.0, 1.0),
168 &RectStyle {
169 alpha: 0.4,
170 ..rect(Some(band), None)
171 },
172 )?;
173 backend.draw_line(&[at(0.1, 0.5), at(0.9, 0.5)], &line(lw.max(1.0)))?;
174 }
175 KeyGlyph::Boxplot => {
176 let fill = props.fill.unwrap_or((255, 255, 255));
177 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.25)], &line(0.5))?;
178 backend.draw_line(&[at(0.5, 0.75), at(0.5, 0.9)], &line(0.5))?;
179 backend.draw_rect(
180 at(0.125, 0.25),
181 at(0.875, 0.75),
182 &rect(Some(fill), Some(color)),
183 )?;
184 backend.draw_line(&[at(0.125, 0.5), at(0.875, 0.5)], &line(1.0))?;
185 }
186 KeyGlyph::Crossbar => {
187 let fill = props.fill.unwrap_or((255, 255, 255));
188 backend.draw_rect(
189 at(0.125, 0.25),
190 at(0.875, 0.75),
191 &rect(Some(fill), Some(color)),
192 )?;
193 backend.draw_line(&[at(0.125, 0.5), at(0.875, 0.5)], &line(1.0))?;
194 }
195 KeyGlyph::Pointrange => {
196 backend.draw_line(&[at(0.5, 0.1), at(0.5, 0.9)], &line(lw))?;
197 point(backend, props.size.unwrap_or(s / 6.0).min(s / 3.0))?;
198 }
199 KeyGlyph::Text | KeyGlyph::Label => {
200 if glyph == KeyGlyph::Label {
201 backend.draw_rect(
202 at(0.1, 0.15),
203 at(0.9, 0.85),
204 &RectStyle {
205 fill: Some((255, 255, 255)),
206 stroke: Some(color),
207 stroke_width: 0.5,
208 alpha,
209 clip: false,
210 corner_radius: 1.5,
211 },
212 )?;
213 }
214 backend.draw_text(
215 "a",
216 (cx, cy + s * 0.18),
217 &TextStyle {
218 color,
219 size: s * 0.6,
220 anchor: TextAnchor::Middle,
221 ..Default::default()
222 },
223 )?;
224 }
225 KeyGlyph::Blank => {}
226 }
227 Ok(())
228}