From b732336d16063e2949331457a78a5be1c6f7d565 Mon Sep 17 00:00:00 2001 From: Lucas Jansen <7199136+staticintlucas@users.noreply.github.com> Date: Fri, 22 Dec 2023 01:36:31 +0000 Subject: [PATCH] Misc refactor of keyset-key --- keyset-drawing/src/imp/key.rs | 29 +++++-- keyset-drawing/src/imp/mod.rs | 13 +-- keyset-drawing/src/lib.rs | 2 +- keyset-drawing/src/pdf.rs | 2 +- keyset-key/src/kle/error.rs | 12 ++- keyset-key/src/kle/mod.rs | 144 +++++++++++++++++----------------- keyset-key/src/legend.rs | 16 +++- keyset-key/src/lib.rs | 127 +++++++++++++++++++----------- keyset-profile/src/lib.rs | 6 ++ 9 files changed, 212 insertions(+), 139 deletions(-) diff --git a/keyset-drawing/src/imp/key.rs b/keyset-drawing/src/imp/key.rs index 7a8af06..81f8ea1 100644 --- a/keyset-drawing/src/imp/key.rs +++ b/keyset-drawing/src/imp/key.rs @@ -9,8 +9,12 @@ use super::{Outline, Path, ARC_TOL}; pub fn top(key: &key::Key, options: &Options) -> Path { let path = match key.shape { - key::Shape::Normal(size) => options.profile.top_with_size(size).to_path(ARC_TOL), - key::Shape::SteppedCaps => options.profile.top_with_size((1.25, 1.)).to_path(ARC_TOL), + key::Shape::None(..) => BezPath::new(), + key::Shape::Normal(size) | key::Shape::Space(size) => { + options.profile.top_with_size(size).to_path(ARC_TOL) + } + key::Shape::Homing(..) => options.profile.top_with_size((1.0, 1.0)).to_path(ARC_TOL), + key::Shape::SteppedCaps => options.profile.top_with_size((1.25, 1.0)).to_path(ARC_TOL), key::Shape::IsoHorizontal | key::Shape::IsoVertical => iso_top_path(&options.profile), }; @@ -26,7 +30,14 @@ pub fn top(key: &key::Key, options: &Options) -> Path { pub fn bottom(key: &key::Key, options: &Options) -> Path { let path = match key.shape { - key::Shape::Normal(size) => options.profile.bottom_with_size(size).to_path(ARC_TOL), + key::Shape::None(..) => BezPath::new(), + key::Shape::Normal(size) | key::Shape::Space(size) => { + options.profile.bottom_with_size(size).to_path(ARC_TOL) + } + key::Shape::Homing(..) => options + .profile + .bottom_with_size((1.0, 1.0)) + .to_path(ARC_TOL), key::Shape::SteppedCaps => options .profile .bottom_with_size((1.75, 1.)) @@ -47,12 +58,14 @@ pub fn bottom(key: &key::Key, options: &Options) -> Path { pub fn homing(key: &key::Key, options: &Options) -> Option<Path> { let profile = &options.profile; - let key::Type::Homing(homing) = key.typ else { + let key::Shape::Homing(homing) = key.shape else { return None; }; let homing = homing.unwrap_or(profile.homing.default); - let center = profile.top_with_size(key.shape.margin().size()).center(); + let center = profile + .top_with_size(key.shape.inner_rect().size()) + .center(); let bez_path = match homing { key::Homing::Scoop => None, @@ -447,7 +460,7 @@ mod tests { // Scoop let scoop = { let mut key = key::Key::example(); - key.typ = key::Type::Homing(Some(key::Homing::Scoop)); + key.shape = key::Shape::Homing(Some(key::Homing::Scoop)); key }; @@ -457,7 +470,7 @@ mod tests { // Bar let bar = { let mut key = key::Key::example(); - key.typ = key::Type::Homing(Some(key::Homing::Bar)); + key.shape = key::Shape::Homing(Some(key::Homing::Bar)); key }; @@ -479,7 +492,7 @@ mod tests { // Bump let bump = { let mut key = key::Key::example(); - key.typ = key::Type::Homing(Some(key::Homing::Bump)); + key.shape = key::Shape::Homing(Some(key::Homing::Bump)); key }; diff --git a/keyset-drawing/src/imp/mod.rs b/keyset-drawing/src/imp/mod.rs index aeb76d9..cbcccbd 100644 --- a/keyset-drawing/src/imp/mod.rs +++ b/keyset-drawing/src/imp/mod.rs @@ -4,6 +4,7 @@ mod legend; use std::collections::HashSet; use ::key::Key; +use ::key::Shape as KeyShape; use color::Color; use geom::{BezPath, Point, Shape, Vec2}; @@ -33,22 +34,14 @@ pub struct KeyDrawing { impl KeyDrawing { pub fn new(key: &Key, options: &Options) -> Self { - let show_key = options.show_keys && !matches!(key.typ, ::key::Type::None); + let show_key = options.show_keys && !matches!(key.shape, KeyShape::None(..)); let bottom = show_key.then(|| key::bottom(key, options)); let top = show_key.then(|| key::top(key, options)); let step = show_key.then(|| key::step(key, options)).flatten(); let homing = show_key.then(|| key::homing(key, options)).flatten(); - let top_rect = match key.shape { - ::key::Shape::Normal(size) => options.profile.top_with_size(size).rect(), - ::key::Shape::SteppedCaps => options.profile.top_with_size((1.25, 1.)).rect(), - ::key::Shape::IsoHorizontal => options.profile.top_with_size((1.5, 1.)).rect(), - ::key::Shape::IsoVertical => { - let rect = options.profile.top_with_size((1.25, 2.)).rect(); - rect.with_origin(rect.origin() + (250., 0.)) - } - }; + let top_rect = options.profile.top_with_rect(key.shape.inner_rect()).rect(); let margin = options.show_margin.then(|| { // TODO get unique margins, not size_idx's. Currently impossible because Insets: !Hash diff --git a/keyset-drawing/src/lib.rs b/keyset-drawing/src/lib.rs index 24d8a75..7840d35 100644 --- a/keyset-drawing/src/lib.rs +++ b/keyset-drawing/src/lib.rs @@ -41,7 +41,7 @@ impl Drawing { pub fn new(keys: &[Key], options: &Options) -> Self { let bounds = keys .iter() - .map(|k| k.shape.bounds().with_origin(k.position)) + .map(|k| k.shape.outer_rect().with_origin(k.position)) .fold( Rect::from_origin_size(Point::ORIGIN, Size::new(1., 1.)), |rect, key| rect.union(key), diff --git a/keyset-drawing/src/pdf.rs b/keyset-drawing/src/pdf.rs index e55467b..0575c2b 100644 --- a/keyset-drawing/src/pdf.rs +++ b/keyset-drawing/src/pdf.rs @@ -171,7 +171,7 @@ mod tests { use crate::Options; #[test] - fn test_to_svg() { + fn test_to_pdf() { let options = Options { show_margin: true, // to give us an unfilled path ..Default::default() diff --git a/keyset-key/src/kle/error.rs b/keyset-key/src/kle/error.rs index 15da136..ca7face 100644 --- a/keyset-key/src/kle/error.rs +++ b/keyset-key/src/kle/error.rs @@ -1,14 +1,23 @@ +/// An error parsing a KLE layout #[derive(Debug)] pub enum Error { + /// An error in parsing the KLE JSON file + JsonParseError(serde_json::Error), + /// A key size not supported by `keyset` UnsupportedKeySize { + /// The key's `w` value w: f64, + /// The key's `h` value h: f64, + /// The key's `x2` value x2: f64, + /// The key's `y2` value y2: f64, + /// The key's `w2` value w2: f64, + /// The key's `h2` value h2: f64, }, - JsonParseError(serde_json::Error), } impl std::fmt::Display for Error { @@ -47,6 +56,7 @@ impl From<serde_json::Error> for Error { } } +/// A [`std::result::Result`] with [`Error`] as it's error type pub type Result<T> = std::result::Result<T, Error>; #[cfg(test)] diff --git a/keyset-key/src/kle/mod.rs b/keyset-key/src/kle/mod.rs index 53e7c91..55308ce 100644 --- a/keyset-key/src/kle/mod.rs +++ b/keyset-key/src/kle/mod.rs @@ -1,12 +1,18 @@ +//! Load KLE layouts from JSON files + mod error; use geom::{Point, Size}; use kle_serial as kle; -use crate::{Homing, Key, Legend, Shape, Type}; +use crate::{Homing, Key, Legend, Shape}; pub use error::{Error, Result}; fn shape_from_kle(key: &kle::Key) -> Result<Shape> { + const STEP_CAPS: [f64; 6] = [1.25, 1.0, 0.0, 0.0, 1.75, 1.0]; + const ISO_VERT: [f64; 6] = [1.25, 2.0, -0.25, 0.0, 1.5, 1.0]; + const ISO_HORIZ: [f64; 6] = [1.5, 1.0, 0.25, 0.0, 1.25, 2.0]; + fn is_close<const N: usize>(a: &[f64; N], b: &[f64; N]) -> bool { a.iter().zip(b).all(|(a, b)| (b - a).abs() < 1e-2) } @@ -21,14 +27,31 @@ fn shape_from_kle(key: &kle::Key) -> Result<Shape> { .. } = key; - if is_close(&[w, h, x2, y2, w2, h2], &[1.25, 1., 0., 0., 1.75, 1.]) { + let is_normal = is_close(&[x2, y2, w2, h2], &[0.0, 0.0, w, h]); + let is_1u = is_normal && is_close(&[w, h], &[1.0, 1.0]); + + let dims = [w, h, x2, y2, w2, h2]; + + if is_1u && (key.profile.contains("scoop") || key.profile.contains("dish")) { + Ok(Shape::Homing(Some(Homing::Scoop))) + } else if is_1u && key.profile.contains("bar") { + Ok(Shape::Homing(Some(Homing::Bar))) + } else if is_1u && (key.profile.contains("bump") || key.profile.contains("dot")) { + Ok(Shape::Homing(Some(Homing::Bump))) + } else if is_normal && key.profile.contains("space") { + Ok(Shape::Space(Size::new(w, h))) + } else if is_1u && key.homing { + Ok(Shape::Homing(None)) + } else if key.decal { + Ok(Shape::None(Size::new(w, h))) + } else if is_normal { + Ok(Shape::Normal(Size::new(w, h))) + } else if is_close(&dims, &STEP_CAPS) { Ok(Shape::SteppedCaps) - } else if is_close(&[w, h, x2, y2, w2, h2], &[1.25, 2., -0.25, 0., 1.5, 1.]) { + } else if is_close(&dims, &ISO_VERT) { Ok(Shape::IsoVertical) - } else if is_close(&[w, h, x2, y2, w2, h2], &[1.5, 1., 0.25, 0., 1.25, 2.]) { + } else if is_close(&dims, &ISO_HORIZ) { Ok(Shape::IsoHorizontal) - } else if is_close(&[x2, y2, w2, h2], &[0., 0., w, h]) { - Ok(Shape::Normal(Size::new(w, h))) } else { // TODO support all key shapes/sizes Err(Error::UnsupportedKeySize { @@ -42,29 +65,6 @@ fn shape_from_kle(key: &kle::Key) -> Result<Shape> { } } -fn type_from_kle(key: &kle::Key) -> Type { - const SCOOP_KW: [&str; 2] = ["scoop", "dish"]; - const BAR_KW: [&str; 2] = ["bar", "line"]; - const BUMP_KW: [&str; 4] = ["bump", "dot", "nub", "nipple"]; - - // TODO support ghosted keys? - if SCOOP_KW.iter().any(|kw| key.profile.contains(kw)) { - Type::Homing(Some(Homing::Scoop)) - } else if BAR_KW.iter().any(|kw| key.profile.contains(kw)) { - Type::Homing(Some(Homing::Bar)) - } else if BUMP_KW.iter().any(|kw| key.profile.contains(kw)) { - Type::Homing(Some(Homing::Bump)) - } else if key.profile.contains("space") { - Type::Space - } else if key.homing { - Type::Homing(None) - } else if key.decal { - Type::None - } else { - Type::Normal - } -} - impl From<kle::Legend> for Legend { fn from(legend: kle::Legend) -> Self { let kle::Legend { text, size, color } = legend; @@ -82,7 +82,6 @@ impl TryFrom<kle::Key> for Key { fn try_from(mut key: kle::Key) -> Result<Self> { let position = Point::new(key.x + key.x2.min(0.), key.y + key.y2.min(0.)); let shape = shape_from_kle(&key)?; - let typ = type_from_kle(&key); let color = key.color.rgb().into(); let legends = { let mut arr = <[Option<kle::Legend>; 9]>::default(); @@ -93,13 +92,17 @@ impl TryFrom<kle::Key> for Key { Ok(Self { position, shape, - typ, color, legends, }) } } +/// Loads a KLE layout from a JSON string into a [`Vec<Key>`] +/// +/// # Errors +/// +/// If an pub fn from_json(json: &str) -> Result<Vec<Key>> { let key_iter: kle::KeyIterator = serde_json::from_str(json)?; key_iter.map(Key::try_from).collect() @@ -114,6 +117,37 @@ mod tests { #[test] fn key_shape_from_kle() { + let default_key = shape_from_kle(&kle::Key::default()).unwrap(); + let decal = shape_from_kle(&kle::Key { + decal: true, + ..Default::default() + }) + .unwrap(); + let space = shape_from_kle(&kle::Key { + profile: "space".into(), + ..Default::default() + }) + .unwrap(); + let homing_default = shape_from_kle(&kle::Key { + homing: true, + ..Default::default() + }) + .unwrap(); + let homing_scoop = shape_from_kle(&kle::Key { + profile: "scoop".into(), + ..Default::default() + }) + .unwrap(); + let homing_bar = shape_from_kle(&kle::Key { + profile: "bar".into(), + ..Default::default() + }) + .unwrap(); + let homing_bump = shape_from_kle(&kle::Key { + profile: "bump".into(), + ..Default::default() + }) + .unwrap(); let regular_key = shape_from_kle(&kle::Key { width: 2.25, height: 1., @@ -155,7 +189,14 @@ mod tests { }) .unwrap(); - assert_matches!(regular_key, Shape::Normal(size) if size == Size::new(2.25, 1.)); + assert_matches!(default_key, Shape::Normal(size) if size == Size::new(1.0, 1.0)); + assert_matches!(regular_key, Shape::Normal(size) if size == Size::new(2.25, 1.0)); + assert_matches!(decal, Shape::None(size) if size == Size::new(1.0, 1.0)); + assert_matches!(space, Shape::Space(size) if size == Size::new(1.0, 1.0)); + assert_matches!(homing_default, Shape::Homing(None)); + assert_matches!(homing_scoop, Shape::Homing(Some(Homing::Scoop))); + assert_matches!(homing_bar, Shape::Homing(Some(Homing::Bar))); + assert_matches!(homing_bump, Shape::Homing(Some(Homing::Bump))); assert_matches!(iso_horiz, Shape::IsoHorizontal); assert_matches!(iso_vert, Shape::IsoVertical); assert_matches!(step_caps, Shape::SteppedCaps); @@ -184,45 +225,6 @@ mod tests { ); } - #[test] - fn key_type_from_kle() { - let regular_key = type_from_kle(&kle::Key { - ..Default::default() - }); - let decal = type_from_kle(&kle::Key { - decal: true, - ..Default::default() - }); - let space = type_from_kle(&kle::Key { - profile: "space".into(), - ..Default::default() - }); - let homing_default = type_from_kle(&kle::Key { - homing: true, - ..Default::default() - }); - let homing_scoop = type_from_kle(&kle::Key { - profile: "scoop".into(), - ..Default::default() - }); - let homing_bar = type_from_kle(&kle::Key { - profile: "bar".into(), - ..Default::default() - }); - let homing_bump = type_from_kle(&kle::Key { - profile: "bump".into(), - ..Default::default() - }); - - assert_matches!(regular_key, Type::Normal); - assert_matches!(decal, Type::None); - assert_matches!(space, Type::Space); - assert_matches!(homing_default, Type::Homing(None)); - assert_matches!(homing_scoop, Type::Homing(Some(Homing::Scoop))); - assert_matches!(homing_bar, Type::Homing(Some(Homing::Bar))); - assert_matches!(homing_bump, Type::Homing(Some(Homing::Bump))); - } - #[test] fn kle_from_json() { let result1: Vec<_> = from_json(&unindent( diff --git a/keyset-key/src/legend.rs b/keyset-key/src/legend.rs index a942fce..b920c26 100644 --- a/keyset-key/src/legend.rs +++ b/keyset-key/src/legend.rs @@ -2,14 +2,19 @@ use std::ops::{Index, IndexMut}; use color::Color; +/// A single legend #[derive(Debug, Clone, PartialEq)] pub struct Legend { + /// The legend text pub text: String, + /// The legend size pub size_idx: usize, + /// The legend colour pub color: Color, } impl Legend { + /// Create a new [`Legend`] pub fn new(text: impl Into<String>, size_idx: usize, color: Color) -> Self { Self { text: text.into(), @@ -19,11 +24,12 @@ impl Legend { } } +/// A set of legends for a key #[derive(Debug, Clone, Default)] pub struct Legends([Option<Legend>; 9]); impl Legends { - // Example non-blank key used in some of our tests + /// An example non-blank set of legends #[must_use] pub fn example() -> Self { Self([ @@ -39,18 +45,21 @@ impl Legends { ]) } + /// Creates an iterator in a left-to-right, top-to-bottom order pub fn iter(&self) -> std::slice::Iter<Option<Legend>> { self.0.iter() } } impl From<[Option<Legend>; 9]> for Legends { + /// Converts from an array in left-to-right, top-to-bottom order fn from(value: [Option<Legend>; 9]) -> Self { Self(value) } } impl From<[[Option<Legend>; 3]; 3]> for Legends { + /// Converts from an array of arrays in row-major order fn from(mut value: [[Option<Legend>; 3]; 3]) -> Self { let mut arr = <[Option<Legend>; 9]>::default(); arr[0..3].swap_with_slice(&mut value[0]); @@ -64,6 +73,7 @@ impl IntoIterator for Legends { type Item = Option<Legend>; type IntoIter = <[Option<Legend>; 9] as IntoIterator>::IntoIter; + /// Creates an iterator in a left-to-right, top-to-bottom order fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } @@ -72,12 +82,14 @@ impl IntoIterator for Legends { impl Index<usize> for Legends { type Output = Option<Legend>; + /// Indexes the legends arranged in left-to-right, top-to-bottom order fn index(&self, index: usize) -> &Self::Output { self.0.index(index) } } impl IndexMut<usize> for Legends { + /// Mutably indexes the legends arranged in left-to-right, top-to-bottom order fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.0.index_mut(index) } @@ -86,12 +98,14 @@ impl IndexMut<usize> for Legends { impl Index<(usize, usize)> for Legends { type Output = Option<Legend>; + /// Indexes the legends using a `(column, row)` tuple fn index(&self, (column, row): (usize, usize)) -> &Self::Output { self.0.index(row * 3 + column) } } impl IndexMut<(usize, usize)> for Legends { + /// Mutably indexes the legends using a `(column, row)` tuple fn index_mut(&mut self, (column, row): (usize, usize)) -> &mut Self::Output { self.0.index_mut(row * 3 + column) } diff --git a/keyset-key/src/lib.rs b/keyset-key/src/lib.rs index eb09bb6..eada9a7 100644 --- a/keyset-key/src/lib.rs +++ b/keyset-key/src/lib.rs @@ -1,3 +1,8 @@ +//! This crate contains the key and legend types used for describing layouts used internally by +//! [keyset]. It also contains utility functions for loading KLE layouts +//! +//! [keyset]: https://crates.io/crates/keyset + #![warn( missing_docs, clippy::all, @@ -10,7 +15,6 @@ clippy::cargo, clippy::nursery )] -#![allow(missing_docs, clippy::missing_errors_doc)] // TODO mod legend; @@ -23,75 +27,97 @@ use geom::{Point, Rect, Size}; use color::Color; +/// The type of homing used on a homing key #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Homing { + /// A scooped homing key, also known as a dished homing key Scoop, + /// A key with a homing bar, sometimes called a line Bar, + /// A key with a homing bump, also known as a nub, dot, or nipple Bump, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Type { - None, // a.k.a. decal in KLE lingo - Normal, // Just a regular ol' key - Homing(Option<Homing>), - Space, -} - -#[derive(Debug, Clone, Copy, PartialEq)] +/// The shape of a key +#[derive(Debug, Clone, Copy)] pub enum Shape { + /// Not a *key* per se, but only a legend. This is usually used for labels and is the same as a + /// decal in KLE + None(Size), + /// A regular key of the given size Normal(Size), + /// A spacebar of the given size + Space(Size), + /// A homing key with the given homing type. If the homing type is [`None`] the profile's + /// default homing type is assumed to be used + Homing(Option<Homing>), + /// A stepped caps lock key, i.e. a 1.25u key with additional 0.5u step on the right SteppedCaps, + /// A vertically-aligned ISO enter, i.e. an ISO enter where legends are aligned within the + /// vertical 1.25u × 2.0u section of the key IsoVertical, + /// A horizontally-aligned ISO enter, i.e. an ISO enter where legends are aligned within the + /// horizontal 1.5u top section of the key IsoHorizontal, } -impl From<Size> for Shape { - fn from(value: Size) -> Self { - Self::Normal(value) - } -} - impl Shape { + /// The outer bounding rectangle of the key shape, i.e. the bounding box of the key shape. The + /// inner and outer bounds are the same for regular-shaped keys, but are different for stepped + /// keys, L-shaped keys, etc. #[must_use] - pub fn bounds(self) -> Rect { + pub fn outer_rect(self) -> Rect { match self { - Self::Normal(size) => Rect::from_origin_size((0.0, 0.0), size), - Self::IsoHorizontal | Self::IsoVertical => { - Rect::from_origin_size((0.0, 0.0), (1.5, 2.0)) + Self::None(size) | Self::Normal(size) | Self::Space(size) => { + Rect::from_origin_size(Point::ORIGIN, size) + } + Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)), + Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.75, 1.0)), + Self::IsoVertical | Self::IsoHorizontal => { + Rect::from_origin_size(Point::ORIGIN, (1.5, 2.0)) } - Self::SteppedCaps => Rect::from_origin_size((0.0, 0.0), (1.75, 1.0)), } } + /// The inner bounding rectangle of the key shape, i.e. the bounds for the part of the key + /// containing the legend. The inner and outer bounds are the same for regular-shaped keys, but + /// are different for stepped keys, L-shaped keys, etc. #[must_use] - pub fn margin(self) -> Rect { + pub fn inner_rect(self) -> Rect { match self { - Self::Normal(size) => Rect::from_origin_size((0.0, 0.0), size), - Self::SteppedCaps => Rect::from_origin_size((0.0, 0.0), (1.25, 1.0)), + Self::None(size) | Self::Normal(size) | Self::Space(size) => { + Rect::from_origin_size(Point::ORIGIN, size) + } + Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)), + Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.25, 1.0)), Self::IsoVertical => Rect::from_origin_size((0.25, 0.0), (1.25, 2.0)), - Self::IsoHorizontal => Rect::from_origin_size((0.0, 0.0), (1.5, 1.0)), + Self::IsoHorizontal => Rect::from_origin_size(Point::ORIGIN, (1.5, 1.0)), } } } +/// A key #[derive(Debug, Clone)] #[non_exhaustive] pub struct Key { + /// The position of the key pub position: Point, + /// The key's shape pub shape: Shape, - pub typ: Type, + /// The key's colour pub color: Color, + /// The key's legends pub legends: Legends, } impl Key { + /// A new blank key #[must_use] pub fn new() -> Self { Self::default() } - // Example non-blank key used in some of our tests + /// An example non-blank key #[must_use] pub fn example() -> Self { Self { @@ -106,7 +132,6 @@ impl Default for Key { Self { position: Point::ORIGIN, shape: Shape::Normal(Size::new(1., 1.)), - typ: Type::Normal, color: Color::new(0.8, 0.8, 0.8), legends: Legends::default(), } @@ -120,31 +145,43 @@ pub mod tests { use super::*; #[test] - fn shape_bounds() { + fn shape_outer_size() { assert_eq!( - Shape::Normal(Size::new(2.25, 1.)).bounds(), + Shape::Normal(Size::new(2.25, 1.)).outer_rect(), Rect::new(0.0, 0.0, 2.25, 1.) ); - assert_eq!(Shape::IsoVertical.bounds(), Rect::new(0.0, 0.0, 1.5, 2.0)); - assert_eq!(Shape::IsoHorizontal.bounds(), Rect::new(0.0, 0.0, 1.5, 2.0)); - assert_eq!(Shape::SteppedCaps.bounds(), Rect::new(0.0, 0.0, 1.75, 1.0)); + assert_eq!( + Shape::IsoVertical.outer_rect(), + Rect::new(0.0, 0.0, 1.5, 2.0) + ); + assert_eq!( + Shape::IsoHorizontal.outer_rect(), + Rect::new(0.0, 0.0, 1.5, 2.0) + ); + assert_eq!( + Shape::SteppedCaps.outer_rect(), + Rect::new(0.0, 0.0, 1.75, 1.0) + ); } #[test] - fn shape_margin() { + fn shape_inner_size() { assert_eq!( - Shape::Normal(Size::new(2.25, 1.)).margin(), + Shape::Normal(Size::new(2.25, 1.)).inner_rect(), Rect::new(0.0, 0.0, 2.25, 1.) ); - assert_eq!(Shape::IsoVertical.margin(), Rect::new(0.25, 0.0, 1.5, 2.0)); - assert_eq!(Shape::IsoHorizontal.margin(), Rect::new(0.0, 0.0, 1.5, 1.0)); - assert_eq!(Shape::SteppedCaps.margin(), Rect::new(0.0, 0.0, 1.25, 1.0)); - } - - #[test] - fn shape_from() { - let shape = Shape::from(Size::new(1.75, 1.)); - assert_matches!(shape, Shape::Normal(x) if x == Size::new(1.75, 1.)); + assert_eq!( + Shape::IsoVertical.inner_rect(), + Rect::new(0.25, 0.0, 1.5, 2.0) + ); + assert_eq!( + Shape::IsoHorizontal.inner_rect(), + Rect::new(0.0, 0.0, 1.5, 1.0) + ); + assert_eq!( + Shape::SteppedCaps.inner_rect(), + Rect::new(0.0, 0.0, 1.25, 1.0) + ); } #[test] @@ -153,7 +190,6 @@ pub mod tests { assert_eq!(key.position, Point::new(0., 0.)); assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.)); - assert_matches!(key.typ, Type::Normal); assert_eq!(key.color, Color::new(0.8, 0.8, 0.8)); for legend in key.legends { assert!(legend.is_none()); @@ -167,7 +203,6 @@ pub mod tests { assert_eq!(key.position, Point::new(0., 0.)); assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.)); - assert_matches!(key.typ, Type::Normal); assert_eq!(key.color, Color::new(0.8, 0.8, 0.8)); for (legend, is_some) in key.legends.into_iter().zip(legend_is_some) { assert_eq!(legend.is_some(), is_some); diff --git a/keyset-profile/src/lib.rs b/keyset-profile/src/lib.rs index 5e84f5c..fda212c 100644 --- a/keyset-profile/src/lib.rs +++ b/keyset-profile/src/lib.rs @@ -293,6 +293,12 @@ impl Profile { top_rect.with_size(top_rect.size() + 1e3 * (size.into() - Size::new(1., 1.))) } + pub fn top_with_rect(&self, rect: impl Into<Rect>) -> RoundRect { + let rect = rect.into(); + let result = self.top_with_size(rect.size()); + result.with_origin(result.origin() + 1e3 * rect.origin().to_vec2()) + } + pub fn bottom_with_size(&self, size: impl Into<Size>) -> RoundRect { let bottom_rect = self.bottom.round_rect(); bottom_rect.with_size(bottom_rect.size() + 1e3 * (size.into() - Size::new(1., 1.)))