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で書かれました。
参考
AAQのDocker環境備忘録
yskoht/aaqをDocker化して Docker Hub に公開したので備忘録です。
aaqの開発環境を構築する
$ git clone git@github.com:yskoht/aaq.git $ docker run -it -v $(pwd):/root ruby:2.7.1-alpine /bin/sh % apk add gcc libc-dev make git pkgconfig imagemagick6 imagemagick6-dev imagemagick6-libs % bundle % bundle exec aaq # コマンド実行 % bundle exec rake spec # テスト実行
DockerHubに公開する
$ docker login $ docker image build -t yskoht/aaq:0.1.3 . $ docker push yskoht/aaq:0.1.3 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE yskoht/aaq 0.1.3 1a119dc7a71f About a minute ago 157MB $ docker tag 1a119dc7a71f yskoht/aaq:latest $ docker push yskoht/aaq:latest
以下のコマンドでaaqを実行できるようになりました。
$ docker run --rm -v $(pwd):/root yskoht/aaq aaq Octocat/Octocat.png --color
コピーしてコメントアウトするVS Codeプラグインを作った
Duplicate, Then Comment-out
DuplicateThenCommentOut - Visual Studio Marketplace
コピーしてコメントアウト?
プログラミングをしていると既存のコードを保持した上で、少し変更して動作を確認したいことがよくあります。そういう場合、自分は対象箇所をコピーしてコメントアウトするという操作を行います。
従来は、
- 範囲選択
- コピー&ペースト
- 範囲選択
- コメントアウト
と1回ずつ範囲選択して、コピペとコメントアウトをする必要がありました。 このプラグインを使うと、
- 範囲選択
- プラグイン実行
の2ステップで同じことを実現できます。
上のgifは範囲選択をしたあと、コマンドパレットを呼び出してから(Cmd
+ Shift
+ P
)、Duplicate then Comment-out
を選択してプラグインを実行している様子です。
ショートカットの設定
ショートカットを設定しておけば、いちいちコマンドパレットを呼び出す必要がなくなります。keybindings.jsonに以下のように追記し、key
を好きなようにカスタマイズすれば、好きなショートカットでプラグインを実行できるようになります。
{ "key": "ctrl+shift+/", "command": "extension.duplicateThenCommentOut" },
ソースコード
Enjoy!
Binary Indexed Tree
AtCoder Beginner Contest 153
BIT知らなかった。
BIT
参考
F - Silver Fox vs Monster
class BIT def initialize(n) @tree = Array.new(n + 1, 0) end def add(i, diff) while i < @tree.size @tree[i] += diff i += (i & -i) end end def sum(i) t = 0 while i > 0 do t += @tree[i] i -= (i & -i) end return t end end n, d, a = gets.chomp.split.map(&:to_i) ms = [] n.times do ms.push gets.chomp.split.map(&:to_i) end ms = ms.sort_by { |x| x[0] } ms.push [1<<60, 0] dst = {} n.times do |i| x, _ = *ms[i] r = x + 2*d k = ms.bsearch_index { |m| m[0] > r } dst[i] = k end bit = BIT.new(n+1) res = 0 n.times do |i| x, h = *ms[i] t = bit.sum(i + 1) h -= t next if h <= 0 cnt = (h + a - 1) / a res += cnt damage = cnt * a bit.add(i + 1, damage) bit.add(dst[i] + 1, -damage) end puts res
参考
My Technology Roadmap for DIY Keyboard (2018 ~ 2019)
新しいキーボードを作る際に技術的なチャレンジを毎回決めているのですが、 キーボードを作り始めてから2年経ったのでできるようになったことをまとめておきます。
9key (2018-01-13)
- キーマトリクス回路の実装
- QMK firmwareのビルド&フラッシュ
- ICSP (in-circuit serial programming)を用いたプログラムの書き込み
Thumbhelix (2018-05-01)
- KiCadを用いたPCBの作成
- シンボルライブラリの作成
- フットプリントライブラリの作成
- Eeschemaで回路図を作り、Pcbnewで配置配線
- ALL PCBでPCB発注
- アクリルでケースの作成
- アクリルケースの図面作成
- Ponokoで発注
- coromozaでレーザー加工
- I2Cライブラリの使用
- Tokyo Mechanical Keyboard Meetup Vol.4に展示
pakbd Prototype (2018-08-31)
- 分割キーボードの作成
- Tokyo Mechanical Keyboard Meetup Vol.5に展示
pakbd Rev1 (2018-11-03)
- PCBに切れ目を入れて5行版と4行版に対応
- 4行レイアウトの実装
- Cherry MX用スイッチソケットを用いたスイッチのホットスワップ
- きりいたドットコムでステンレスプレートの発注
- TRSケーブルの自作
- QMK firmwareの整備
pakbd Rev2 (2019-02-24)
- 6行レイアウトの実装
- スタビライザーの使用
クオリティ上げるのは、なかなかつらい感じだ…次は上手くいきそう pic.twitter.com/Ynd9IB7jx2
— yskoht (@_yskoht) February 24, 2019
pakbd Rev3 (2019-03-30)
- 薄型化
- アクリル積層ケースの作成
- ProMicro上面実装
— yskoht (@_yskoht) March 22, 2019
— yskoht (@_yskoht) March 30, 2019
Business Card Keyboard (2019-09-29)
- USB-C化
- SK6812MINI(LED)対応PCBの作成
- Kailh Choc用スイッチソケットを用いたスイッチのホットスワップ
- ALL PCBでステンシルの発注
- 自宅ホットプレートリフロー
- 脱ProMicro (ATmega32U4の基板実装)
- PCBのシルクにイラストの挿入
棚卸し
8月のキーボードでやりたいことを列挙していたので棚卸しする。57%達成。
- 基板のリファクタリング(ProMicro、スイッチ位置の修正。シルク入れ等) →✅
- QMKファームウェアの整備 →✅
- 薄型化 →✅
- LED対応 →✅
- ホットスワップソケット対応 →✅
- ミドルプレートの作成 →✅
- ステンレスプレートの作成 →✅
- tilt機構 →❌
- 興味がなくなったのでキャンセル
- 左右一体で使う場合のロック機構→❌
- あまり持ち運ばないのでキャンセル
- トラックポイント→❌
- そろそろDIY Keyboardにおいての回答がほしい。継続
- USB-C化 →✅
- 静電容量無接点スイッチ対応→❌
- 勉強が足りない。継続
- ARM化、脱QMK→❌
- 勉強が足りない。継続
- Bluethooth化 →❓ yskoht.hatenablog.com
今後のやりたいこと
来年はファームウェア周りをやっていきたい
- ファームウェアの自作
- ARM化
- ポインティングデバイス
- 静電容量無接点スイッチ対応
番外編 (作ったツールなど)
Keymapviz (2018-03-01)
QMK Firmwareのkeymap.cからアスキーアートを出力するスクリプト。PyPlに登録したんですが、いまいまJSONを出力するのが壊れていますね。すみません。正月休み中に直します🙇(あと記事が古くなっているのも直したい。直接でPNGが出力できるようにもしたい)→直しました(2019-01-05)
Keyboard Layouter Plugin (2019-04-01)
KiCadでスイッチを自動配置するプラグイン。
HardTyping (2019-12-25)
タイピングサイト。がんばっていきたいです。よろしくお願いします🙇
自作キーボード向けタイピングサイトを作った
この記事はキーボード #2 Advent Calendar 2019の25日目の記事です。昨日は😢🔰神🔰😢さんのトラックボールマウスを分解してArduino Microで動かした話でした。かなりヌルヌル動いていてすごい。
自作キーボード向けタイピングサイト
自作キーボードを作ったいいけれど、キーマップに馴染むまでが大変で、いい感じに練習できるツールがあったらいいなと思って作りました。
特徴① レイアウトの変更が可能
レイアウトをKeyboard Layout Editor形式のJSONで自由に変更できます。JSONの詳細はレイアウトの設定を御覧ください。
特徴② レイヤーに対応
数字や記号はRAIZE
レイヤーやLOWER
レイヤーに割り当てられることが多いですが、既存のタイピングアプリではなかなか練習しづらいです。HardTypingでは押下するキーとレイヤーを変更するためのもモディファイアキーも一緒にハイライトしてくれます。
今後の展望
現在ベータ版です。やりたいことは色々あるんですがまだ全然できていません。
- タイピングする文章をユーザーが投稿できるようにしたい
- キーマップをユーザーがシェアできるようにしたい
- keymap.cからレイアウト設定用のJSONを出力したり、JSONからkeymap.cを出力したりしたい
- ランキング機能をつけたい
- コンテンツを増やしたい
- かな入力に対応したい
- キーボード上のホットスポットを可視化したい
- 統計情報を出力したい
新しい配列を気軽に試したり、練習できたり、配列の試行錯誤に便利なツールになれば良いなと思います。ゆっくりやっていこうと思うので今後ともよろしくお願いします🙇♂️
この記事はpakbd rev3を使って書かれました。
名刺サイズキーボードを作った
Business Card Keyboard
meishi展1に行ってからずっと作りたいなと思っていた名刺サイズ(91mm×55mm)のキーボードを作りました。
- yskoht/business-card-keyboard-device: Business card sized keyboard
- yskoht/business-card-keyboard: business-card-keyboard firmware
Feature
ATmega32U4の基板実装
今回やりたかったことのメイントピックは脱ProMicroでした。回路図は公開されているので問題なかったですが、部品は何を使えばよいのかよくわからない感じです。今回は「適当に集める→動いた」で良かったですが、HomeReflowKit – スイッチサイエンスを改めてよく見たら部品表が載っていたので、これを集めればよかったのかもしれません(というか、このHomeReflowKitが欲しい)。個別に部品を買うと50個、100個単位でしか買えなかったりするので、部品が大量に余ってしまいました。
USB-C化
やりたかったことの2つ目はUSB-C化でした。使った部品は以下です。
このパーツの素晴らしいところはピッチ変換基板があり、しかもそのEagleファイルがGithubに公開されている2ところです。
EagleファイルをKiCadに変換するだけでフットプリントの用意ができました。そして、「これは手ハンダは無理だ」となり、自宅リフローを決意しました。
ALL PCBでステンシルの発注
PCBとステンシルをALL PCBで発注しました。ステンシルが$20.00、PCBが$5.00、送料は$27.00で合計$52.00でした。「ステンシル高ぇ、送料高ぇ」と思っていたら届いたものが思いの外大きく、そりゃこれくらいしますね、という感じでした。
てかステンシルでかすぎ pic.twitter.com/dbR0ZerGr1
— yskoht (@_yskoht) September 11, 2019
自宅ホットプレートリフロー
SK6812MINI (LED) 対応PCBの作成
せっかくなのでLEDも付けました。フットプリントはHelixを参考にさせていただきました。シンボルはKiCadに入っていたものを使ったため、シンボルとフットプリントの端子番号が整合せず、間違って配線してしまったので、PCBを再発注しました。横着せずにシンボルも自分で作ったほうが安全ですね。最初に発注した基板はSK6812MINIがぴったりハマっていい感じだったのですが、再発注した基板は若干緩い感じで製造誤差を実感しました。
Kailh Choc用スイッチソケットを用いたスイッチのホットスワップ
今回はKailhのChocスイッチを使いたかったのでCPG135001S30を使いました。フットプリント作るのめんどくさいなと思っていたところ、foostan/kbd: Publish data for Keyboardで公開されていることを知りました。速攻でスター付けて利用させていただきました。ありがとうございます。
PCBのシルクにイラストの挿入
線とビアは重ならないようにしたほうが良さそうです。
失敗したこと
スレーブ側でhas_usbが1になってしまった
今回、参考にした回路図3にはVBUSのラインにダイオードが付いていませんでした。
なので、マスター側のVCCをTRRSケーブルを通じてスレーブ側のVCCにつなげると、スレーブ側のUVCCもHIGHになり、スレーブ側でもhas_usbが1になってしまいました。
Pro micro スイッチサイエンス版の回路図にはしっかりダイオードが入っているので、こちらを参考にするのが良さそうだなと思います。