1#![warn(
7 missing_docs,
8 clippy::all,
9 clippy::correctness,
10 clippy::suspicious,
11 clippy::style,
12 clippy::complexity,
13 clippy::perf,
14 clippy::pedantic,
15 clippy::cargo,
16 clippy::nursery
17)]
18
19mod legend;
20
21#[cfg(feature = "kle")]
22pub mod kle;
23
24pub use legend::{Legend, Legends};
25
26use geom::{Point, Rect, Size};
27
28use color::Color;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum Homing {
33 Scoop,
35 Bar,
37 Bump,
39}
40
41#[derive(Debug, Clone, Copy)]
43pub enum Shape {
44 None(Size),
47 Normal(Size),
49 Space(Size),
51 Homing(Option<Homing>),
54 SteppedCaps,
56 IsoVertical,
59 IsoHorizontal,
62}
63
64impl Shape {
65 #[must_use]
69 pub fn outer_rect(self) -> Rect {
70 match self {
71 Self::None(size) | Self::Normal(size) | Self::Space(size) => {
72 Rect::from_origin_size(Point::ORIGIN, size)
73 }
74 Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)),
75 Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.75, 1.0)),
76 Self::IsoVertical | Self::IsoHorizontal => {
77 Rect::from_origin_size(Point::ORIGIN, (1.5, 2.0))
78 }
79 }
80 }
81
82 #[must_use]
86 pub fn inner_rect(self) -> Rect {
87 match self {
88 Self::None(size) | Self::Normal(size) | Self::Space(size) => {
89 Rect::from_origin_size(Point::ORIGIN, size)
90 }
91 Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)),
92 Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.25, 1.0)),
93 Self::IsoVertical => Rect::from_origin_size((0.25, 0.0), (1.25, 2.0)),
94 Self::IsoHorizontal => Rect::from_origin_size(Point::ORIGIN, (1.5, 1.0)),
95 }
96 }
97}
98
99#[derive(Debug, Clone)]
101#[non_exhaustive]
102pub struct Key {
103 pub position: Point,
105 pub shape: Shape,
107 pub color: Color,
109 pub legends: Legends,
111}
112
113impl Key {
114 #[must_use]
116 pub fn new() -> Self {
117 Self::default()
118 }
119
120 #[must_use]
122 pub fn example() -> Self {
123 Self {
124 legends: Legends::example(),
125 ..Self::default()
126 }
127 }
128}
129
130impl Default for Key {
131 fn default() -> Self {
132 Self {
133 position: Point::ORIGIN,
134 shape: Shape::Normal(Size::new(1., 1.)),
135 color: Color::new(0.8, 0.8, 0.8),
136 legends: Legends::default(),
137 }
138 }
139}
140
141#[cfg(test)]
142pub mod tests {
143 use assert_matches::assert_matches;
144
145 use super::*;
146
147 #[test]
148 fn shape_outer_size() {
149 assert_eq!(
150 Shape::Normal(Size::new(2.25, 1.)).outer_rect(),
151 Rect::new(0.0, 0.0, 2.25, 1.)
152 );
153 assert_eq!(
154 Shape::IsoVertical.outer_rect(),
155 Rect::new(0.0, 0.0, 1.5, 2.0)
156 );
157 assert_eq!(
158 Shape::IsoHorizontal.outer_rect(),
159 Rect::new(0.0, 0.0, 1.5, 2.0)
160 );
161 assert_eq!(
162 Shape::SteppedCaps.outer_rect(),
163 Rect::new(0.0, 0.0, 1.75, 1.0)
164 );
165 }
166
167 #[test]
168 fn shape_inner_size() {
169 assert_eq!(
170 Shape::Normal(Size::new(2.25, 1.)).inner_rect(),
171 Rect::new(0.0, 0.0, 2.25, 1.)
172 );
173 assert_eq!(
174 Shape::IsoVertical.inner_rect(),
175 Rect::new(0.25, 0.0, 1.5, 2.0)
176 );
177 assert_eq!(
178 Shape::IsoHorizontal.inner_rect(),
179 Rect::new(0.0, 0.0, 1.5, 1.0)
180 );
181 assert_eq!(
182 Shape::SteppedCaps.inner_rect(),
183 Rect::new(0.0, 0.0, 1.25, 1.0)
184 );
185 }
186
187 #[test]
188 fn key_new() {
189 let key = Key::new();
190
191 assert_eq!(key.position, Point::new(0., 0.));
192 assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.));
193 assert_eq!(key.color, Color::new(0.8, 0.8, 0.8));
194 for legend in key.legends {
195 assert!(legend.is_none());
196 }
197 }
198
199 #[test]
200 fn key_example() {
201 let key = Key::example();
202 let legend_is_some = [true, false, true, false, false, false, true, false, true];
203
204 assert_eq!(key.position, Point::new(0., 0.));
205 assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.));
206 assert_eq!(key.color, Color::new(0.8, 0.8, 0.8));
207 for (legend, is_some) in key.legends.into_iter().zip(legend_is_some) {
208 assert_eq!(legend.is_some(), is_some);
209 }
210 }
211}