diff --git a/PHYレイヤー.md b/PHYレイヤー.md index 35166e5..393d724 100644 --- a/PHYレイヤー.md +++ b/PHYレイヤー.md @@ -18,28 +18,44 @@ PHYレイヤーは、キースイッチやキーキャップ、その他周辺 ユニットサイズ(1Uが何mmか、x軸・y軸それぞれ)を規定。 ビジュアル表現として、内側へのオフセット位置及びオフセット距離(mm or U単位)・R(U単位)を規定。 -### キーの座標 +### 座標と回転 -キーキャップの左上角をローカル原点とする。 -座標単位はU。1Uのサイズは別途指定する。 -回転中心はローカル原点からの相対座標で表す。グローバル -座標・回転角度は0.25U単位・15度単位でスナップするが正規化しない。 +各ノードは、親ノードから見た相対的な配置を持つ。絶対座標は保持しない。 +クラスター単位の移動・回転を親ノード1つの書き換えで済ませるため、相対座標を正本とする。 + +配置は「親の座標系で θ 回転してから (x, y) 平行移動」と定義する。 + +```txt +p_parent = Translate(x, y) · Rotate(θ) · p_child +``` + +この定義により、(x, y) は自分のローカル原点が親座標系のどこに来るかを表し、θ は自分の向きを表す。 +回転軸は常に自分自身のローカル原点であり、パラメータとしては持たない。 +キーもクラスターも同じ変換を持つため、キーごとに向きが異なる配置のためにクラスターを作る必要はない。 + +ローカル原点はキーキャップの左上角とする。座標単位はU。1Uのサイズはプロファイルで指定する。 + +座標・回転角度は、ローカル座標系で0.25U単位・15度単位でスナップするが正規化しない。 + +KLE JSONやPCB向けの出力は、階層を畳んで絶対座標に変換した導出物として生成する。 ### キーのサイズ -0.25U単位でスナップするが正規化しない。 +0.25U単位でスナップするが正規化しない。 +サイズはローカルの値であり、親の変換を受けない。 ### グルーピング 複数のキーを「クラスター」としてまとめて運用することができる。 クラスターのみにフォーカスして編集が可能。 -クラスターごとの回転中心を設定できる。 +クラスターの原点が、配下のノードの回転中心になる。 **内部処理での利用**: - クラスターごとの一括編集・移動・回転 - 階層構造による管理(クラスターの入れ子構造) - 編集時のフォーカス制御 +- 回転中心を保存したい場合はクラスターにする(ノード自身は回転中心を持たないため) **外部連携での扱い**: @@ -51,28 +67,28 @@ PHYレイヤーは、キースイッチやキーキャップ、その他周辺 ## 保存形式 -キーごとに絶対座標を持つ。 -全てのキー、またはキー及びクラスターをまとめたクラスターを配列として持つ +各ノードは親から見た変換を持つ。キーとクラスターを混在させた木構造として保持する。 ```ts -type Position={ - x:number, - y:number, - w:number, - h:number, - rx:number, - ry:number, - ra:number +type Transform = { + x: number, // 親座標系での位置 + y: number, + rotation: number // 親座標系での回転角度(度) } -type Key={ - position:Position, - profile:Profile +type Size = { + w: number, // ローカル値。親の変換を受けない + h: number } -type Cluster={ - position:Position, - clusters:(Key[]|Cluster[]) +type Key = { + transform: Transform, + size: Size, + profile: Profile } -const keys:(Key|Cluster)[]=[] +type Cluster = { + transform: Transform, + children: (Key | Cluster)[] +} +const root: (Key | Cluster)[] = [] ``` ## 連携用出力形式 @@ -186,3 +202,17 @@ interface ClusterData { 3. **段階的移行**:既存のKLEユーザーが段階的に拡張機能を利用可能 4. **ツール間連携**:KLE標準形式での出力により、他のキーボード設計ツールとの相互運用性を確保 5. **データの軽量化**:KLE出力時に不要な情報を含めず、ファイルサイズを最小化 + +## 検討済み・非採用 + +- キーごとの絶対座標保持 (journal/2026-09-18-phy-coordinate-model.md) +- 相対座標と絶対座標の併記 (journal/2026-09-18-phy-coordinate-model.md) +- ノードごとの回転中心パラメータ (journal/2026-09-18-phy-coordinate-model.md) + +## 未確認事項 + +### P-1 KLE JSONへ書き出した配置が行のまとまりを保てるか + +- 位置づけ: 本文が前提にしているものではない。連携用出力形式の設計に関わる未解決の問い +- 現状の認識: 階層を畳んでKLEに読み込ませると行のまとまりが崩れる。他レイヤーの情報を使ってでも行ごとにまとめたいが、方法は未検討 +- 格上げ条件: 回転を含むPHYデータをKLE JSONに変換してKLE本家に読み込ませ、rotation groupと行配列がどう再構成されるかを確認する diff --git a/journal/2026-09-18-phy-coordinate-model.md b/journal/2026-09-18-phy-coordinate-model.md new file mode 100644 index 0000000..6ae612c --- /dev/null +++ b/journal/2026-09-18-phy-coordinate-model.md @@ -0,0 +1,77 @@ +# PHY座標モデル: 相対座標 + 回転中心パラメータなし + +## 結論 + +- 各ノードは親から見た変換 (x, y, rotation) を持つ。絶対座標は保持しない +- 回転中心のパラメータは持たない。回転軸は常に自分自身のローカル原点 +- Key も Cluster も同じ変換を持つ。per-key回転のためにクラスターを作る必要はない +- スナップ(0.25U / 15度)はローカル座標系での話 +- w, h は Transform から分離する(親の変換を受けないため) + +## 案の比較 + +### 座標系 + +| 案 | 利点 | 欠点 | +|---|---|---| +| 絶対座標 | KLEとの往復が素直。保存値がそのまま最終位置 | クラスターのpositionと二重管理になる。階層が実質的に選択範囲の記録にしかならない | +| 相対座標(採用) | クラスターの移動・回転が親1ノードの書き換えで済む | 畳む処理が要る。KLE再インポート時に逆変換で端数が出る | +| 相対+絶対を併記 | 読み出しに変換が不要 | 二重管理の問題がそのまま残る | + +### 回転中心 + +| 案 | 利点 | 欠点 | +|---|---|---| +| ノードごとに回転中心を持つ(KLE方式) | 任意のピボットを保存できる | グループ回転で全キーが同じrx/ryを共有する必要がある。移動のたびに全キー更新 | +| 自分の原点に固定(採用) | パラメータが消え、式が Translate·Rotate だけになる | 共通ピボットを残したい場合はクラスターを作る必要がある | + +## 根拠 + +### 絶対座標を採らない理由 + +キーが絶対座標を持つ状態でクラスターにも position があると、同じ情報が2箇所に存在する。 +クラスターを回すときにクラスターの角度を書き換えるのか配下の全キーを再計算するのかが定まらず、 +片方だけ更新されたファイルが生まれる。元の保存形式でクラスターの position の意味が +書かれていなかったのは、この行き場のなさが未解決のままだったためと思われる。 + +また絶対座標を正本にすると、入れ子構造は「選択範囲の記録」以上の意味を持たない。 +それならフラット配列 + clusterId の方が構造として正直で、階層クラスターの要求と噛み合わない。 + +畳んで絶対座標にする処理は KLE出力・PCB出力のためにどのみち必要。 +絶対座標を正本にしてもクラスター操作のために逆方向が要るので、結局両方向を書くことになる。 + +### per-key回転とクラスター数 + +平面上の剛体配置の自由度は、位置2 + 向き1 = 3。 +ノードが (x, y, rotation) を持つならちょうど3自由度なので、どんな配置でも +全キーを同じ親の直下に並べて表現できる。キーごとに角度が違ってもクラスターは増えない。 + +当初 Alice を per-key回転の例として挙げたが、Alice は左右のアルファ部がそれぞれ一体で傾いた +2クラスターであり、per-key回転の例ではない(ユーザーによる訂正)。 +代わりに GRIN系 の指摘があったが、実際のジオメトリを確認していないため保留。 +上の自由度の議論は形状に依存しないので、結論は変わらない。 + +### ピボットは編集操作の状態であってデータではない + +ピボットは「θを変えたとき何が動かないか」を決めているだけで、 +結果として得られる配置は (x, y, rotation) で表現できる。 +扇状配置のような共通ピボットは、エディタ側の操作として結果の数値を焼き込めばよい。 +後から開き具合を編集し直したい場合だけ、その扇をクラスターにする(クラスター原点 = ピボット)。 +ピボットを永続データとして持つと、KLE の rotation_x / rotation_y と同じものになる。 + +### row/col への影響 + +放射状に並べるとrowの概念が崩れるのではないかという懸念があったが、 +PHYレイヤーはrow/colを扱わないため問題にならない。 +matrix情報は任意の付加情報で、正本はIDX/ELC側にある。幾何がmatrixに制約されることはない。 +KLEでrowが崩壊するのは物理配置から論理的な行を推定しているためで、 +その推定をやめることがレイヤー分離の目的そのもの。 + +## 捨てた案 + +- 相対座標を正本にしつつ絶対座標をキャッシュとして併記する。二重管理の問題が再発する + +## 未決のまま残したもの + +- KLE出力時に行ごとにまとめたいという要求。方法未検討のため未確認事項として残した +- Key.profile を実体で持つか参照IDで持つか。今回の決定の範囲外なので元のまま Profile 実体を残した