Skip to content

Commit

Permalink
Draw text margin
Browse files Browse the repository at this point in the history
  • Loading branch information
staticintlucas committed Aug 4, 2023
1 parent 9e20763 commit 976c08f
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 118 deletions.
186 changes: 94 additions & 92 deletions src/drawing/imp/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,116 +3,118 @@ use std::f64::consts::{FRAC_PI_2, PI};
use kurbo::{Arc, BezPath, Circle, Point, Rect, Shape, Size};

use crate::key::{Homing, Shape as KeyShape, Type as KeyType};
use crate::utils::{Color, RoundRect};
use crate::utils::RoundRect;
use crate::{DrawingOptions, Key};

use super::ARC_TOL;
use super::{Outline, Path, ARC_TOL};

#[derive(Debug, Clone)]
pub(crate) struct KeyPath {
pub path: BezPath,
pub fill: Color,
pub outline: Color,
}

impl KeyPath {
pub fn top(key: &Key, options: &DrawingOptions) -> Self {
let top_rect = options.profile.top_rect;
pub(crate) fn top(key: &Key, options: &DrawingOptions) -> Path {
let top_rect = options.profile.top_rect;

let path = match key.shape {
KeyShape::Normal(size) => top_rect
.with_size(top_rect.size() + 1e3 * (size - Size::new(1., 1.)))
.to_path(ARC_TOL),
KeyShape::SteppedCaps => top_rect
.with_size(top_rect.size() + 1e3 * (Size::new(0.25, 0.)))
.to_path(ARC_TOL),
KeyShape::IsoHorizontal | KeyShape::IsoVertical => iso_top_path(top_rect),
};
let path = match key.shape {
KeyShape::Normal(size) => top_rect
.with_size(top_rect.size() + 1e3 * (size - Size::new(1., 1.)))
.to_path(ARC_TOL),
KeyShape::SteppedCaps => top_rect
.with_size(top_rect.size() + 1e3 * (Size::new(0.25, 0.)))
.to_path(ARC_TOL),
KeyShape::IsoHorizontal | KeyShape::IsoVertical => iso_top_path(top_rect),
};

Self {
path,
fill: key.color,
outline: key.color.highlight(0.15),
}
Path {
path,
fill: Some(key.color),
outline: Some(Outline {
color: key.color.highlight(0.15),
width: options.outline_width,
}),
}
}

pub fn bottom(key: &Key, options: &DrawingOptions) -> Self {
let bottom_rect = options.profile.bottom_rect;
pub(crate) fn bottom(key: &Key, options: &DrawingOptions) -> Path {
let bottom_rect = options.profile.bottom_rect;

let path = match key.shape {
KeyShape::Normal(size) => bottom_rect
.with_size(bottom_rect.size() + 1e3 * (size - Size::new(1., 1.)))
.to_path(ARC_TOL),
KeyShape::SteppedCaps => bottom_rect
.with_size(bottom_rect.size() + 1e3 * (Size::new(0.75, 0.)))
.to_path(ARC_TOL),
KeyShape::IsoHorizontal | KeyShape::IsoVertical => iso_bottom_path(bottom_rect),
};
let path = match key.shape {
KeyShape::Normal(size) => bottom_rect
.with_size(bottom_rect.size() + 1e3 * (size - Size::new(1., 1.)))
.to_path(ARC_TOL),
KeyShape::SteppedCaps => bottom_rect
.with_size(bottom_rect.size() + 1e3 * (Size::new(0.75, 0.)))
.to_path(ARC_TOL),
KeyShape::IsoHorizontal | KeyShape::IsoVertical => iso_bottom_path(bottom_rect),
};

Self {
path,
fill: key.color,
outline: key.color.highlight(0.15),
}
Path {
path,
fill: Some(key.color),
outline: Some(Outline {
color: key.color.highlight(0.15),
width: options.outline_width,
}),
}
}

pub fn homing(key: &Key, options: &DrawingOptions) -> Option<Self> {
let profile = &options.profile;
pub(crate) fn homing(key: &Key, options: &DrawingOptions) -> Option<Path> {
let profile = &options.profile;

let KeyType::Homing(homing) = key.typ else { return None };
let homing = homing.unwrap_or(profile.homing.default);
let KeyType::Homing(homing) = key.typ else { return None };
let homing = homing.unwrap_or(profile.homing.default);

let center = profile
.top_rect
.rect()
.with_size(profile.top_rect.size() + 1e3 * (key.shape.size() - Size::new(1., 1.)))
.center();
let center = profile
.top_rect
.rect()
.with_size(profile.top_rect.size() + 1e3 * (key.shape.size() - Size::new(1., 1.)))
.center();

let bez_path = match homing {
Homing::Scoop => None,
Homing::Bar => Some(
Rect::from_center_size(
center + (0., profile.homing.bar.y_offset),
profile.homing.bar.size,
)
.into_path(ARC_TOL),
),
Homing::Bump => Some(
Circle::new(
center + (0., profile.homing.bump.y_offset),
profile.homing.bump.diameter / 2.,
)
.into_path(ARC_TOL),
),
};
let bez_path = match homing {
Homing::Scoop => None,
Homing::Bar => Some(
Rect::from_center_size(
center + (0., profile.homing.bar.y_offset),
profile.homing.bar.size,
)
.into_path(ARC_TOL),
),
Homing::Bump => Some(
Circle::new(
center + (0., profile.homing.bump.y_offset),
profile.homing.bump.diameter / 2.,
)
.into_path(ARC_TOL),
),
};

bez_path.map(|path| Self {
path,
fill: key.color,
outline: key.color.highlight(0.15),
})
}
bez_path.map(|path| Path {
path,
fill: Some(key.color),
outline: Some(Outline {
color: key.color.highlight(0.15),
width: options.outline_width,
}),
})
}

pub fn step(key: &Key, options: &DrawingOptions) -> Option<Self> {
matches!(key.shape, KeyShape::SteppedCaps).then(|| {
let profile = &options.profile;
pub(crate) fn step(key: &Key, options: &DrawingOptions) -> Option<Path> {
matches!(key.shape, KeyShape::SteppedCaps).then(|| {
let profile = &options.profile;

// Take average dimensions of top and bottom
let rect = RoundRect::from_origin_size(
((profile.top_rect.origin().to_vec2() + profile.bottom_rect.origin().to_vec2())
/ 2.)
.to_point(),
(profile.top_rect.size() + profile.bottom_rect.size()) / 2.,
(profile.top_rect.radii() + profile.bottom_rect.radii()) / 2.,
);
// Take average dimensions of top and bottom
let rect = RoundRect::from_origin_size(
((profile.top_rect.origin().to_vec2() + profile.bottom_rect.origin().to_vec2()) / 2.)
.to_point(),
(profile.top_rect.size() + profile.bottom_rect.size()) / 2.,
(profile.top_rect.radii() + profile.bottom_rect.radii()) / 2.,
);

Self {
path: step_path(rect),
fill: key.color,
outline: key.color.highlight(0.15),
}
})
}
Path {
path: step_path(rect),
fill: Some(key.color),
outline: Some(Outline {
color: key.color.highlight(0.15),
width: options.outline_width,
}),
}
})
}

fn iso_bottom_path(rect: RoundRect) -> BezPath {
Expand Down
71 changes: 61 additions & 10 deletions src/drawing/imp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,85 @@
mod key;

use kurbo::Point;
use itertools::Itertools;
use kurbo::{BezPath, Point, Rect, Shape, Size, Vec2};

use crate::key::Type as KeyType;
use crate::key::{Shape as KeyShape, Type as KeyType};
use crate::utils::Color;
use crate::{DrawingOptions, Key};

pub(crate) use self::key::KeyPath;

// TODO move this somewhere?
const ARC_TOL: f64 = 1.; // Tolerance for converting Arc->Bézier with Kurbo

#[derive(Debug, Clone, Copy)]
pub(crate) struct Outline {
pub color: Color,
pub width: f64,
}

#[derive(Debug, Clone)]
pub(crate) struct Path {
pub path: BezPath,
pub outline: Option<Outline>,
pub fill: Option<Color>,
}

#[derive(Debug, Clone)]
pub(crate) struct KeyDrawing {
pub origin: Point,
pub paths: Vec<KeyPath>,
pub paths: Vec<Path>,
}

impl KeyDrawing {
pub fn new(key: &Key, options: &DrawingOptions) -> Self {
let show_key = options.show_keys && !matches!(key.typ, KeyType::None);

let bottom = show_key.then(|| KeyPath::bottom(key, options));
let top = show_key.then(|| KeyPath::top(key, options));
let step = show_key.then(|| KeyPath::step(key, options)).flatten();
let homing = show_key.then(|| KeyPath::homing(key, options)).flatten();
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 margin = options.show_margin.then(|| {
let (offset, size) = match key.shape {
KeyShape::Normal(size) => (Vec2::ZERO, 1e3 * (size - Size::new(1., 1.))),
KeyShape::SteppedCaps => (Vec2::ZERO, Size::new(250., 0.)),
KeyShape::IsoHorizontal => (Vec2::ZERO, Size::new(500., 0.)),
KeyShape::IsoVertical => (Vec2::new(250., 0.), Size::new(250., 1000.)),
};

let path = key
.legends
.iter()
.flatten()
.filter_map(|l| l.as_ref().map(|l| l.size))
.unique()
.map(|s| options.profile.text_margin.get(s))
.map(move |r| {
Rect::from_origin_size(r.origin() + offset, r.size() + size).into_path(ARC_TOL)
})
.fold(BezPath::new(), |mut p, r| {
p.extend(r);
p
});

Path {
path,
outline: Some(Outline {
color: Color::new(0xff, 0, 0),
width: 5.,
}),
fill: None,
}
});

// Do a bunch of chaining here rather than using [...].iter().filter_map(|it| it). This
// gives iterator a known size so it will allocate the required size when collecting to a
// Vec<_>
let paths = bottom.into_iter().chain(top).chain(step).chain(homing);
let paths = bottom
.into_iter()
.chain(top)
.chain(step)
.chain(homing)
.chain(margin);

Self {
origin: key.position,
Expand Down
4 changes: 1 addition & 3 deletions src/drawing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ use kurbo::{Point, Rect, Size};

use crate::{Font, Key, Profile};

pub(crate) use imp::{KeyDrawing, KeyPath};
pub(crate) use imp::{KeyDrawing, Path};

#[derive(Debug, Clone)]
pub struct Drawing {
bounds: Rect,
keys: Vec<imp::KeyDrawing>,
scale: f64,
outline: f64,
}

impl Drawing {
Expand All @@ -34,7 +33,6 @@ impl Drawing {
bounds,
keys,
scale: options.dpi * 0.75, // 1u = 0.75in => dpu = 0.75*dpi
outline: options.outline_width,
}
}

Expand Down
28 changes: 15 additions & 13 deletions src/drawing/svg.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use itertools::Itertools;
use kurbo::{Affine, PathEl, Point};
use svg::node::element::{Group, Path};
use svg::node::element::{Group, Path as SvgPath};
use svg::Document;

use crate::drawing::Drawing;

use super::{KeyDrawing, KeyPath};
use super::{KeyDrawing, Path};

macro_rules! fmt_num {
($fmt:literal, $($args:expr),*) => {
Expand Down Expand Up @@ -34,25 +34,22 @@ pub(crate) fn draw(drawing: &Drawing) -> String {
let document = drawing
.keys
.iter()
.map(|k| draw_key(k, drawing.outline))
.map(draw_key)
.fold(document, Document::add);

document.to_string()
}

fn draw_key(key: &KeyDrawing, outline: f64) -> Group {
fn draw_key(key: &KeyDrawing) -> Group {
// scale from keyboard units to drawing units (milliunits)
let pos = Affine::scale(1e3) * key.origin;

let group = Group::new().set("transform", fmt_num!("translate({}, {})", pos.x, pos.y));

key.paths
.iter()
.map(|p| draw_path(p, outline))
.fold(group, Group::add)
key.paths.iter().map(draw_path).fold(group, Group::add)
}

fn draw_path(key: &KeyPath, outline: f64) -> Path {
fn draw_path(key: &Path) -> SvgPath {
let data = key
.path
.iter()
Expand Down Expand Up @@ -91,11 +88,16 @@ fn draw_path(key: &KeyPath, outline: f64) -> Path {
})
.join("");

let path = Path::new().set("d", data).set("fill", key.fill.to_string());
let fill = if let Some(color) = key.fill {
color.to_string()
} else {
"none".into()
};
let path = SvgPath::new().set("d", data).set("fill", fill);

if outline > 1e-3 {
path.set("stroke", key.outline.to_string())
.set("stroke-width", fmt_num!("{}", outline))
if let Some(outline) = key.outline {
path.set("stroke", outline.color.to_string())
.set("stroke-width", fmt_num!("{}", outline.width))
} else {
path.set("stroke", "none")
}
Expand Down

0 comments on commit 976c08f

Please sign in to comment.