diff --git a/keyboards/kanata/laptop/lens.json b/keyboards/kanata/laptop/lens.json new file mode 100644 index 0000000..0b342d6 --- /dev/null +++ b/keyboards/kanata/laptop/lens.json @@ -0,0 +1,38 @@ +{ + "layout": "alpha_staggered_double_switch", + "keys": [ + ["Q", "!", "1", "f1"], + ["A", "<", "6", "f6"], + ["X", ">", "", "f11"], + ["W", "@", "2", "f2"], + ["R", "{", "7", "f7"], + ["C", "}", "", "f12"], + ["F", "#", "3", "f3"], + ["S", "[", "8", "f8"], + ["D", "]"], + ["P", "$", "4", "f4"], + ["T", "(", "9", "f9"], + ["V", ")"], + ["B", "%", "5", "f5"], + ["G", "?", "0", "f10"], + ["Z", ""], + ["TR", "", ""], + ["âŖ", "", ""], + ["TL", "", ""], + ["J", "^", "🏠", "😱"], + ["M", "-", "◄", "↩ī¸"], + ["K", "—", "", "🎮"], + ["L", "&", "âŦ", "copy"], + ["N", "_", "â–ŧ", "📋"], + ["H", "|", "", "✂ī¸"], + ["U", "*", "âĢ", "⏊"], + ["E", "/", "▲", "⏯ī¸"], + [",", "\\", "", "âĒ"], + ["Y", "~", "end", "🔊"], + ["I", "=", "â–ē", "🔉"], + [".", "+", "", "🔇"], + [":", "`", "🗑ī¸", "🔆"], + ["O", ";", "", "🔅"], + ["'", "\"", ""] + ] +} diff --git a/keyboards/kanata/laptop/lens.svg b/keyboards/kanata/laptop/lens.svg new file mode 100644 index 0000000..580ed36 --- /dev/null +++ b/keyboards/kanata/laptop/lens.svg @@ -0,0 +1,505 @@ + + + + + + Q + + + ! + + + 1 + + + f1 + + + + + + + A + + + < + + + 6 + + + f6 + + + + + + + X + + + > + + + + + f11 + + + + + + + W + + + @ + + + 2 + + + f2 + + + + + + + R + + + { + + + 7 + + + f7 + + + + + + + C + + + } + + + + + f12 + + + + + + + F + + + # + + + 3 + + + f3 + + + + + + + S + + + [ + + + 8 + + + f8 + + + + + + + D + + + ] + + + + + + + + + + + P + + + $ + + + 4 + + + f4 + + + + + + + T + + + ( + + + 9 + + + f9 + + + + + + + V + + + ) + + + + + + + + + + + B + + + % + + + 5 + + + f5 + + + + + + + G + + + ? + + + 0 + + + f10 + + + + + + + Z + + + + + + + + + + + + + ■ + + + + + + + + + + + + + âŖ + + + + + + + + + + + + + ■ + + + + + + + + + + + + + J + + + ^ + + + 🏠 + + + 😱 + + + + + + + M + + + - + + + ◄ + + + ↩ī¸ + + + + + + + K + + + — + + + + + 🎮 + + + + + + + L + + + & + + + âŦ + + + copy + + + + + + + N + + + _ + + + â–ŧ + + + 📋 + + + + + + + H + + + | + + + + + ✂ī¸ + + + + + + + U + + + * + + + âĢ + + + ⏊ + + + + + + + E + + + / + + + ▲ + + + ⏯ī¸ + + + + + + + , + + + \ + + + + + âĒ + + + + + + + Y + + + ~ + + + end + + + 🔊 + + + + + + + I + + + = + + + â–ē + + + 🔉 + + + + + + + . + + + + + + + + + 🔇 + + + + + + + : + + + ` + + + 🗑ī¸ + + + 🔆 + + + + + + + O + + + ; + + + + + 🔅 + + + + + + + ' + + + " + + + + + + + \ No newline at end of file diff --git a/keyboards/qmk/ferris-sweep/lens.json b/keyboards/qmk/ferris-sweep/lens.json index ce5f87f..9231e9f 100644 --- a/keyboards/qmk/ferris-sweep/lens.json +++ b/keyboards/qmk/ferris-sweep/lens.json @@ -1,49 +1,39 @@ { - "colorscheme": { - "keyFill": "#ffffff", - "keyStroke": "#000000", - "mainLayerColor": "black", - "tlLayerColor": "blue", - "trLayerColor": "red", - "blLayerColor": "purple" - }, + "layout": "split_3x5_2", "keys": [ ["Q", "!", "1", "f1"], ["A", "<", "6", "f6"], ["X", ">", "", "f11"], ["W", "@", "2", "f2"], - ["R", "(", "7", "f7"], - ["C", "]", "", "f12"], + ["R", "{", "7", "f7"], + ["C", "}", "", "f12"], ["F", "#", "3", "f3"], ["S", "[", "8", "f8"], ["D", "]"], ["P", "$", "4", "f4"], - ["T", "{", "9", "f9"], - ["V", "}"], + ["T", "(", "9", "f9"], + ["V", ")"], ["B", "%", "5", "f5"], - ["G", "-", "0", "f10"], - ["Z", "—"], + ["G", "?", "0", "f10"], + ["Z", ""], ["TR", "", ""], ["âŖ", "", ""], ["⇧", "", ""], ["TL", "", ""], - ["J", "^", "🏠"], - ["M", "?", "◄", "😱"], - ["K", "", "", "🎮"], - ["L", "&", "âŦ", "🔊"], - ["N", "_", "â–ŧ", "🔉"], - ["H", "|", "", "🔇"], - ["U", "*", "âĢ", "🔆"], - ["E", "/", "▲", "🔅"], - [",", "\\", "", "↩ī¸"], - ["Y", "~", "end", "âĒ"], - ["I", "=", "â–ē", "⏯ī¸"], - [".", "+", "", "⏊"], - [":", "`", "🗑ī¸", "copy"], - ["O", ";", "", "📋"], - ["'", "\"", "", "✂ī¸"] - ], - "imagePadding": 20, - "keySize": 50, - "layout": "split_3x5_2" + ["J", "^", "🏠", "😱"], + ["M", "-", "◄", "↩ī¸"], + ["K", "—", "", "🎮"], + ["L", "&", "âŦ", "copy"], + ["N", "_", "â–ŧ", "📋"], + ["H", "|", "", "✂ī¸"], + ["U", "*", "âĢ", "⏊"], + ["E", "/", "▲", "⏯ī¸"], + [",", "\\", "", "âĒ"], + ["Y", "~", "end", "🔊"], + ["I", "=", "â–ē", "🔉"], + [".", "+", "", "🔇"], + [":", "`", "🗑ī¸", "🔆"], + ["O", ";", "", "🔅"], + ["'", "\"", ""] + ] } diff --git a/keyboards/qmk/ferris-sweep/lens.svg b/keyboards/qmk/ferris-sweep/lens.svg index f66ef8a..aff835b 100644 --- a/keyboards/qmk/ferris-sweep/lens.svg +++ b/keyboards/qmk/ferris-sweep/lens.svg @@ -1,518 +1,518 @@ - - - + + + - + Q - + ! - + 1 - + f1 - - + + - + A - + < - + 6 - + f6 - - + + - + X - + > - + - + f11 - - + + - + W - + @ - + 2 - + f2 - - + + - + R - - ( + + { - + 7 - + f7 - - + + - + C - - ] + + } - + - + f12 - - + + - + F - + # - + 3 - + f3 - - + + - + S - + [ - + 8 - + f8 - - + + - + D - + ] - + - + - - + + - + P - + $ - + 4 - + f4 - - + + - + T - - { + + ( - + 9 - + f9 - - + + - + V - - } + + ) - + - + - - + + - + B - + % - + 5 - + f5 - - + + - + G - - - + + ? - + 0 - + f10 - - + + - + Z - - — + - + - + - - + + - + ■ - + - + - + - - + + - + âŖ - + - + - + - - + + - + ⇧ - + - + - + - - + + - + ■ - + - + - + - - + + - + J - + ^ - + 🏠 - - - - - - - - M - - - ? - - - ◄ - - + 😱 - - + + - - K + + M - + + - - + + ◄ - - 🎮 - - - - - - - L - - - & - - - âŦ - - - 🔊 - - - - - - - N - - - _ - - - â–ŧ - - - 🔉 - - - - - - - H - - - | - - - - - 🔇 - - - - - - - U - - - * - - - âĢ - - - 🔆 - - - - - - - E - - - / - - - ▲ - - - 🔅 - - - - - - - , - - - \ - - - - + ↩ī¸ - - + + - - Y + + K - - ~ + + — - - end + - - âĒ + + 🎮 - - + + - - I + + L - - = + + & - - â–ē + + âŦ - - ⏯ī¸ - - - - - - - . - - - + - - - - - ⏊ - - - - - - - : - - - ` - - - 🗑ī¸ - - + copy - - + + - - O + + N - - ; + + _ - + + â–ŧ - + 📋 - - + + - - ' + + H - - " + + | - + - + ✂ī¸ + + + + + U + + + * + + + âĢ + + + ⏊ + + + + + + + E + + + / + + + ▲ + + + ⏯ī¸ + + + + + + + , + + + \ + + + + + âĒ + + + + + + + Y + + + ~ + + + end + + + 🔊 + + + + + + + I + + + = + + + â–ē + + + 🔉 + + + + + + + . + + + + + + + + + 🔇 + + + + + + + : + + + ` + + + 🗑ī¸ + + + 🔆 + + + + + + + O + + + ; + + + + + 🔅 + + + + + + + ' + + + " + + + + + + \ No newline at end of file diff --git a/layout-lens/src/config.ts b/layout-lens/src/config.ts index b42c5eb..09b12eb 100644 --- a/layout-lens/src/config.ts +++ b/layout-lens/src/config.ts @@ -7,8 +7,28 @@ import { PredefinedLayout, PredefinedLayoutName, SpecialSymbols, + LayoutMeasurements, + LayoutColorscheme, } from "./types"; import split_3x5_2 from "./layouts/split_3x5_2"; +import alpha_staggered_double_switch from "./layouts/alpha_staggered_double_switch"; + +const defaultMeasurements: LayoutMeasurements = { + imagePadding: 20, + keySize: 60, + keyPadding: 2, + keyCornerRadius: 5, + keyStrokeWidth: 1.5, +}; + +const defaultColorscheme: LayoutColorscheme = { + keyFill: "#ffffff", + keyStroke: "#000000", + mainLayerColor: "black", + tlLayerColor: "blue", + trLayerColor: "red", + blLayerColor: "purple", +}; export function parseConfig(input: string): Config { const parsed = JSON.parse(input); @@ -30,9 +50,8 @@ export function parseConfig(input: string): Config { return special as SpecialSymbols; }), ), - colorscheme: parsed.colorscheme, - imagePadding: parsed.imagePadding, - keySize: parsed.keySize, + colorscheme: { ...defaultColorscheme, ...parsed.colorscheme }, + measurements: { ...defaultMeasurements, ...parsed.measurements }, layout, }; } @@ -48,15 +67,16 @@ function key( const layouts: Record = { [PredefinedLayoutName.split_3x5_2]: split_3x5_2, + [PredefinedLayoutName.alpha_staggered_double_switch]: + alpha_staggered_double_switch, }; export function makeLayout(config: Config): Layout { - const predefined = layouts[config.layout](config.keySize); + const predefined = layouts[config.layout]; return { keys: config.keys.map((k) => key(...(k as Arguments))), colorscheme: config.colorscheme, - imagePadding: config.imagePadding, - keySize: config.keySize, + measurements: config.measurements, visual: predefined.visual, size: predefined.size, }; diff --git a/layout-lens/src/layout.ts b/layout-lens/src/layout.ts index c2f1b8e..75df055 100644 --- a/layout-lens/src/layout.ts +++ b/layout-lens/src/layout.ts @@ -1,15 +1,27 @@ import { VisualKey, VisualLayout } from "./types"; import * as V from "./vec2"; -function visualKey(at: V.Vec2, keySize: number, angle: number = 0): VisualKey { - return { position: at, size: [keySize, keySize], angle }; +export function visualKey( + position: V.Vec2, + size: V.Vec2 = [1, 1], + angle: number = 0, +): VisualKey { + return { position, size, angle }; } -function col(at: V.Vec2, keySize: number): VisualLayout { +function offsetMany(keys: VisualLayout, offsets: V.Vec2[]): VisualLayout { + return keys.map((key, index) => { + const offset = offsets[index] || [0, 0]; + key.position = V.add(key.position, offset); + return key; + }); +} + +function col(at: V.Vec2): VisualLayout { return [ - visualKey(at, keySize), - visualKey(V.add(at, [0, keySize]), keySize), - visualKey(V.add(at, [0, 2 * keySize]), keySize), + visualKey(at), + visualKey(V.add(at, [0, 1])), + visualKey(V.add(at, [0, 2])), ]; } @@ -20,22 +32,18 @@ function radians(deg: number): number { export function thumbs( at: V.Vec2, reverse: boolean, - keySize: number, thumbRotation = reverse ? -15 : 15, ): VisualLayout { - // Distance between thumb key centers - const factor = keySize; - const offset: V.Vec2 = [ - Math.cos(radians(thumbRotation)) * factor, - Math.sin(radians(thumbRotation)) * factor, + Math.cos(radians(thumbRotation)), + Math.sin(radians(thumbRotation)), ]; const result = [ - visualKey(at, keySize, thumbRotation), + visualKey(at, undefined, thumbRotation), visualKey( V.add(at, reverse ? V.neg(offset) : offset), - keySize, + undefined, thumbRotation, ), ]; @@ -48,9 +56,17 @@ export function thumbs( export function cols( at: V.Vec2, cols: V.Vec2[], - keySize: number, + offsets: V.Vec2[] = [], ): VisualLayout { return cols.flatMap((self, index) => - col(V.add(at, V.add(self, [index * keySize, 0])), keySize), + offsetMany(col(V.add(at, V.add(self, [index, 0]))), offsets), ); } + +export function scaleVisual(visual: VisualKey, amount: number): VisualKey { + return { + angle: visual.angle, + position: V.scale(visual.position, amount), + size: V.scale(visual.size, amount), + }; +} diff --git a/layout-lens/src/layouts/alpha_staggered_double_switch.ts b/layout-lens/src/layouts/alpha_staggered_double_switch.ts new file mode 100644 index 0000000..37e8d8d --- /dev/null +++ b/layout-lens/src/layouts/alpha_staggered_double_switch.ts @@ -0,0 +1,32 @@ +import * as L from "../layout"; +import type { PredefinedLayout } from "../types"; +import type { Vec2 } from "../vec2"; + +// 3x5 block +const block: Vec2[] = [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], +]; + +// per-column offsets +const offsets: Vec2[] = [ + [0, 0], + [0.25, 0], + [0.75, 0], +]; + +const layout: PredefinedLayout = { + visual: [ + L.cols([0, 0], block, offsets), + L.visualKey([1.75, 3.25]), + L.visualKey([2.75, 3.25], [5, 1]), + L.visualKey([7.75, 3.25]), + L.cols([5, 0], block, offsets), + ].flat(), + size: [10.8, 4], +}; + +export default layout; diff --git a/layout-lens/src/layouts/split_3x5_2.ts b/layout-lens/src/layouts/split_3x5_2.ts index 5f88871..dc864d3 100644 --- a/layout-lens/src/layouts/split_3x5_2.ts +++ b/layout-lens/src/layouts/split_3x5_2.ts @@ -2,25 +2,23 @@ import * as L from "../layout"; import type { PredefinedLayout } from "../types"; import type { Vec2 } from "../vec2"; -const layout: PredefinedLayout = (keySize) => { - // 3x5 block - const block: Vec2[] = [ - [0, keySize], - [0, keySize / 2], - [0, 0], - [0, keySize / 2], - [0, keySize], - ]; +// 3x5 block +const block: Vec2[] = [ + [0, 1], + [0, 0.5], + [0, 0], + [0, 0.5], + [0, 1], +]; - return { - visual: [ - L.cols([0, 0], block, keySize), - L.thumbs([keySize * 3.5, keySize * 4.5], false, keySize), - L.thumbs([keySize * 7.5, keySize * 4.5], true, keySize), - L.cols([7 * keySize, 0], block, keySize), - ].flat(), - size: [keySize * 12, keySize * 6], - }; +const layout: PredefinedLayout = { + visual: [ + L.cols([0, 0], block), + L.thumbs([3.5, 4.5], false), + L.thumbs([7.5, 4.5], true), + L.cols([7, 0], block), + ].flat(), + size: [12, 6], }; export default layout; diff --git a/layout-lens/src/render.ts b/layout-lens/src/render.ts index 6b5f730..6c78761 100644 --- a/layout-lens/src/render.ts +++ b/layout-lens/src/render.ts @@ -1,9 +1,12 @@ import { tag, px } from "./svg"; +import * as L from "./layout"; +import * as V from "./vec2"; import { KeyboardKey, KeySymbol, Layout, LayoutColorscheme, + LayoutMeasurements, SpecialSymbols, VisualKey, } from "./types"; @@ -18,13 +21,18 @@ function renderKey( visual: VisualKey, key: KeyboardKey, colorscheme: LayoutColorscheme, - keySize: number, + measurements: LayoutMeasurements, ) { + const withPadding = { + position: V.add(visual.position, measurements.keyPadding), + size: V.add(visual.size, -2 * measurements.keyPadding), + }; + const centerX = visual.position[0] + visual.size[0] / 2; const centerY = visual.position[1] + visual.size[1] / 2; const textAttribs = { "text-anchor": "middle", - "dominant-baseline": "middle", + "dominant-baseline": "central", "font-family": "Helvetica", }; @@ -40,24 +48,25 @@ function renderKey( transform: visual.angle && visual.angle !== 0 ? `rotate(${visual.angle}, ${centerX}, ${centerY})` - : undefined, + : "", }, [ tag("rect", { - width: px(visual.size[0]), - height: px(visual.size[1]), - x: visual.position[0], - y: visual.position[1], + width: px(withPadding.size[0]), + height: px(withPadding.size[1]), + x: withPadding.position[0], + y: withPadding.position[1], + rx: measurements.keyCornerRadius, fill: colorscheme.keyFill, stroke: colorscheme.keyStroke, - "stroke-width": px(2), + "stroke-width": px(measurements.keyStrokeWidth), }), tag( "text", { x: centerX, y: centerY, - textLength: px(keySize / 2), + textLength: px(withPadding.size[1] / 2), fill: textColor(key.main, colorscheme.mainLayerColor), ...textAttribs, }, @@ -66,19 +75,20 @@ function renderKey( tag( "text", { - x: visual.position[0] + visual.size[0] / 6, - y: visual.position[1] + visual.size[1] / 6, + x: withPadding.position[0] + withPadding.size[0] / 6, + y: withPadding.position[1] + withPadding.size[1] / 6, fill: textColor(key.tlLayer, colorscheme.tlLayerColor), "font-size": "66%", ...textAttribs, + "text-anchor": "start", }, textContents(key.tlLayer), ), tag( "text", { - x: visual.position[0] + (9 * visual.size[0]) / 10, - y: visual.position[1] + visual.size[1] / 6, + x: withPadding.position[0] + (9 * withPadding.size[0]) / 10, + y: withPadding.position[1] + withPadding.size[1] / 6, fill: textColor(key.trLayer, colorscheme.trLayerColor), "font-size": "66%", ...textAttribs, @@ -89,8 +99,8 @@ function renderKey( tag( "text", { - x: visual.position[0] + visual.size[0] / 10, - y: visual.position[1] + (5 * visual.size[1]) / 6, + x: withPadding.position[0] + withPadding.size[0] / 10, + y: withPadding.position[1] + (5 * withPadding.size[1]) / 6, fill: textColor(key.blLayer, colorscheme.blLayerColor), "font-size": "66%", ...textAttribs, @@ -107,17 +117,24 @@ export function renderLayout(layout: Layout) { "svg", { viewBox: [ - -layout.imagePadding, - -layout.imagePadding, - 2 * layout.imagePadding + layout.size[0], - 2 * layout.imagePadding + layout.size[1], + -layout.measurements.imagePadding, + -layout.measurements.imagePadding, + 2 * layout.measurements.imagePadding + + layout.size[0] * layout.measurements.keySize, + 2 * layout.measurements.imagePadding + + layout.size[1] * layout.measurements.keySize, ].join(" "), xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", }, layout.visual .map((key, index) => - renderKey(key, layout.keys[index], layout.colorscheme, layout.keySize), + renderKey( + L.scaleVisual(key, layout.measurements.keySize), + layout.keys[index], + layout.colorscheme, + layout.measurements, + ), ) .join("\n"), ); diff --git a/layout-lens/src/types.ts b/layout-lens/src/types.ts index ab2e524..691123e 100644 --- a/layout-lens/src/types.ts +++ b/layout-lens/src/types.ts @@ -38,28 +38,33 @@ export interface LayoutColorscheme { blLayerColor: string; } -export interface Layout { - visual: VisualLayout; - keys: KeyboardKey[]; - colorscheme: LayoutColorscheme; - imagePadding: number; - size: Vec2; - keySize: number; -} - export enum PredefinedLayoutName { split_3x5_2, + alpha_staggered_double_switch, +} + +export interface LayoutMeasurements { + imagePadding: number; + keySize: number; + keyPadding: number; + keyCornerRadius: number; + keyStrokeWidth: number; } export interface Config { keys: KeySymbol[][]; - colorscheme: LayoutColorscheme; - imagePadding: number; - keySize: number; layout: PredefinedLayoutName; + colorscheme: LayoutColorscheme; + measurements: LayoutMeasurements; } -export type PredefinedLayout = (keySize: number) => { +export type PredefinedLayout = { visual: VisualLayout; size: Vec2; }; + +export interface Layout extends PredefinedLayout { + keys: KeyboardKey[]; + colorscheme: LayoutColorscheme; + measurements: LayoutMeasurements; +} diff --git a/layout-lens/src/vec2.ts b/layout-lens/src/vec2.ts index fc99054..d065ed2 100644 --- a/layout-lens/src/vec2.ts +++ b/layout-lens/src/vec2.ts @@ -1,9 +1,14 @@ export type Vec2 = [number, number]; -export function add(x: Vec2, y: Vec2): Vec2 { - return [x[0] + y[0], x[1] + y[1]]; +export function add(x: Vec2, y: number | Vec2): Vec2 { + const z = typeof y === "number" ? [y, y] : y; + return [x[0] + z[0], x[1] + z[1]]; } export function neg(v: Vec2): Vec2 { return [-v[0], -v[1]]; } + +export function scale(v: Vec2, amount: number): Vec2 { + return [v[0] * amount, v[1] * amount]; +}