Keyboard Layout Editor JSONの使い方
この記事はキーボード #2 Advent Calendar 2020の8日目の記事です。
昨日は@F_YUUCHIさんの2020年にやったこと/できなかったこと&これからの話でした。
TL;DR
- キーボードの配列を表現するのに Keyboard Layout Editorで使われているデータ形式が便利です
- この記事では、そのデータ形式について述べ、デシリアライズとレイアウトを行います
- デシリアライズは ijprest/kle-serial を使用すると簡単に行なえます
概要
Keyboard Layout Editor(以下、KLE)ではキーの位置や角度を自由に移動させてレイアウトを検討することができます。
View post on imgur.com
imgur.com
レイアウトをJSONとしてダウンロードすることができます。 そのJSONをアップロードすればそのレイアウトを再度編集することができて便利です。
ユースケース例
PCB上にキーを自動で配置する
KiCadでキーを自動で配置したいと思ったときに、入力ファイルとしてKLE JSONを使うことができます。
View post on imgur.com
imgur.com
タイピングゲームのキーボードをカスタマイズする
KLE JSONを変更・編集できれば、タイピングゲームのキーボードをユーザーの好きにカスタマイズできます。
View post on imgur.com
imgur.com
データ形式
データ形式の仕様は以下で説明されています。
この記事ではいくつかの例を通してデータ形式の主要な部分を理解したいと思います。
配列
{ ["a", "b"], ["c", "d"] }
1つ目の配列は y = 0
, 2つ目の配列は y = 1
, ... と配列ごとに行がインクリメントされます。各配列の1つ目の要素は x = 0
, 2つ目の要素は x = 1
, ... と列がインクリメントされます。
各キーの x
, y
は以下のようになっています。
a: { y: 0, x: 0 } b: { y: 0, x: 1 } c: { y: 1, x: 0 } d: { y: 1, x: 1 }
位置( x
, y
)
{ ["a", {x:1,y:0.5}, "b"], ["c", {y:-0.5},"d", {y:1},"e"], ["f"] }
x
, y
に指定した値を、現在の x
, y
に足すことができます。
各キーの x
, y
は以下のようになっています。
x
の値は要素ごとにインクリメントされ、配列ごとにリセットされます。
y
の値は配列ごとにインクリメントされ、状態は後続のキーに保持されます。
a: { y: 0, x: 0 } b: { y: 0.5, x: 2 } # 元の値 { y: 0, x: 1 } にオプションに指定した値が足されている c: { y: 1.5, x: 0 } # y はインクリメントされる。x は0にリセットされる d: { y: 1, x: 1 } # y -= 0.5 e: { y: 2, x: 2 } # y += 1 f: { y: 3, x: 0 }
幅/高さ ( w
, h
)
{ ["a", {w:1.5,h:1.5}, "b"], [{w:2,h:2}, "c", {h:2}, "d"], ["e"] }
キーの幅/高さを指定できます。x
には w
が加算されるので、隣のキーとは重なりませんが、
y
はそのままなので上下のキーと重なってしまいます。
重ならないようにするにはy
を指定する必要があります。
oddly-shaped keys ( x2
, y2
, w2
, h2
)
ISO Enterのようなキーを表現できます。
{ [{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},""] }
これは以下のように、赤と青の2つのキーの重なりで表現されています。
赤のキーは x:0.25, w:1.25, h:2
が指定されており、青のキーは w2:1.5, h2:1, x2:-0.25
が指定されています。青のキーに x2:-0.25
が指定されているのは x:0.25
で加えられたオフセットを戻すためです。
角度 ( r
, rx
, ry
)
※この項目はドキュメントに書かれてないので、自分で調べた範囲のことを書いています。
キーの傾きを表現します。
{ [{r:15},""] }
r
でキーの角度(度)を指定します。 rx
, ry
は回転の原点です。
transform-origin: rx, ry;
を指定し、transform: rotate(r);
することを想定されているように思います。
transform-origin - CSS: カスケーディングスタイルシート | MDN
以下のような仕様があるようです。
rx
,ry
はデフォルト 0r
は各配列の先頭にしか指定できないr
,rx
,ry
は後続のキーにも影響するrx
またはry
が指定されるとx
がrx
、y
がry
にリセットされる
デシリアライズ
KLE JSONをデシリアライズし、各キーのx
, y
, r
, rx
, ry
, w
, h
などを求めます。
オープンソースライブラリを使う
以下のライブラリを使うとデシリアライズが簡単にできます
const kle = require('@ijprest/kle-serial') const keyboard = kle.Serial.deserialize([ ["a", "b"], ]) console.log(JSON.stringify(keyboard))
出力
{ "meta": { "author": "", "backcolor": "#eeeeee", "background": null, "name": "", "notes": "", "radii": "", "switchBrand": "", "switchMount": "", "switchType": "" }, "keys": [ { "color": "#cccccc", "labels": [ "a" ], "textColor": [], "textSize": [], "default": { "textColor": "#000000", "textSize": 3 }, "x": 0, "y": 0, "width": 1, "height": 1, "x2": 0, "y2": 0, "width2": 1, "height2": 1, "rotation_x": 0, "rotation_y": 0, "rotation_angle": 0, "decal": false, "ghost": false, "stepped": false, "nub": false, "profile": "", "sm": "", "sb": "", "st": "" }, { "color": "#cccccc", "labels": [ "b" ], "textColor": [], "textSize": [], "default": { "textColor": "#000000", "textSize": 3 }, "x": 1, "y": 0, "width": 1, "height": 1, "x2": 0, "y2": 0, "width2": 1, "height2": 1, "rotation_x": 0, "rotation_y": 0, "rotation_angle": 0, "decal": false, "ghost": false, "stepped": false, "nub": false, "profile": "", "sm": "", "sb": "", "st": "" } ] }
現状だとrx
, ry
が指定されたときに、x
, y
に rx
, ry
が設定されないようで、以下のPRが出ています。
自分でデシリアライズしてみる
KiCadのプラグインを作るときに自作したものを貼っておきます。
出力
{'x': 0, 'y': 0, 'w': 1, 'h': 1, 'x2': 0, 'y2': 0, 'w2': 1, 'h2': 1, 'r': 0, 'rx': 0, 'ry': 0, 'label': ['a']} {'x': 1, 'y': 0, 'w': 1, 'h': 1, 'x2': 0, 'y2': 0, 'w2': 1, 'h2': 1, 'r': 0, 'rx': 0, 'ry': 0, 'label': ['b']} {'x': 0, 'y': 1, 'w': 1, 'h': 1, 'x2': 0, 'y2': 0, 'w2': 1, 'h2': 1, 'r': 0, 'rx': 0, 'ry': 0, 'label': ['c']} {'x': 1, 'y': 1, 'w': 1, 'h': 1, 'x2': 0, 'y2': 0, 'w2': 1, 'h2': 1, 'r': 0, 'rx': 0, 'ry': 0, 'label': ['d']}
レイアウト
デシリアライズにより各キーの位置関係と角度がわかったら、実際に描画したくなると思います。
雑にHTMLを出力してみます。ラッパーでtransform: rotate
してから left
, top
で位置を指定します。
const kle = require('@ijprest/kle-serial') // ErgoDox const keyboard = kle.Serial.deserialize([ [{x:3.5},"#\n3",{x:10.5},"*\n8"], [{y:-0.875,x:2.5},"@\n2",{x:1},"$\n4",{x:8.5},"&\n7",{x:1},"(\n9"], [{y:-0.875,x:5.5},"%\n5",{a:7},"",{x:4.5},"",{a:4},"^\n6"], [{y:-0.875,a:7,w:1.5},"",{a:4},"!\n1",{x:14.5},")\n0",{a:7,w:1.5},""], [{y:-0.375,x:3.5,a:4},"E",{x:10.5},"I"], [{y:-0.875,x:2.5},"W",{x:1},"R",{x:8.5},"U",{x:1},"O"], [{y:-0.875,x:5.5},"T",{a:7,h:1.5},"",{x:4.5,h:1.5},"",{a:4},"Y"], [{y:-0.875,a:7,w:1.5},"",{a:4},"Q",{x:14.5},"P",{a:7,w:1.5},""], [{y:-0.375,x:3.5,a:4},"D",{x:10.5},"K"], [{y:-0.875,x:2.5},"S",{x:1},"F",{x:8.5},"J",{x:1},"L"], [{y:-0.875,x:5.5},"G",{x:6.5},"H"], [{y:-0.875,a:7,w:1.5},"",{a:4},"A",{x:14.5},":\n;",{a:7,w:1.5},""], [{y:-0.625,x:6.5,h:1.5},"",{x:4.5,h:1.5},""], [{y:-0.75,x:3.5,a:4},"C",{x:10.5},"<\n,"], [{y:-0.875,x:2.5},"X",{x:1},"V",{x:8.5},"M",{x:1},">\n."], [{y:-0.875,x:5.5},"B",{x:6.5},"N"], [{y:-0.875,a:7,w:1.5},"",{a:4},"Z",{x:14.5},"?\n/",{a:7,w:1.5},""], [{y:-0.375,x:3.5},"",{x:10.5},""], [{y:-0.875,x:2.5},"",{x:1},"",{x:8.5},"",{x:1},""], [{y:-0.75,x:0.5},"","",{x:14.5},"",""], [{r:30,rx:6.5,ry:4.25,y:-1,x:1},"",""], [{h:2},"",{h:2},"",""], [{x:2},""], [{r:-30,rx:13,y:-1,x:-3},"",""], [{x:-3},"",{h:2},"",{h:2},""], [{x:-3},""] ]) console.log('<div style="position: relative;">') keyboard.keys.forEach(key => { const coef = 50 let wrapperStyle = '' wrapperStyle += 'position: absolute;' wrapperStyle += `transform: rotate(${key.rotation_angle}deg);` wrapperStyle += `transform-origin: ${key.rotation_x * coef}px ${key.rotation_y * coef}px;` let innerStyle = '' innerStyle += 'border-style: solid;' innerStyle += 'border-width: 1px;' innerStyle += 'position: absolute;' innerStyle += `left: ${key.x * coef}px;` innerStyle += `top: ${key.y * coef}px;` innerStyle += `width: ${key.width * coef - 4}px;` innerStyle += `height: ${key.height * coef - 4}px;` const label = key.labels[0] || '' console.log(`<div style="${wrapperStyle}">`) console.log(` <div style="${innerStyle}">${label}</div>`) console.log(`</div>`) }) console.log('</div>')
結果
最後に
キーボード関連のツールを作ろうと思ったときに、この記事がなにかの役に立てれば幸いです。
この記事はpakbd rev3で書かれました。