# PHYレイヤーとKLE(Keyboard Layout Editor)の互換性分析 ## 概要 検討中のPHYレイヤー構造とKeyboard Layout Editor(KLE)の互換性について詳細に分析した結果をまとめます。KLEをベースとしたフロントエンドの実現可能性を評価します。 ## KLEデータモデルの特徴 ### KLEで対応可能な主要プロパティ KLEの`Key`オブジェクトは以下の構造を持ちます: ```typescript export class Key { // 位置・サイズ関連 x: number; // 絶対位置 (U単位) y: number; // 絶対位置 (U単位) width: number; // サイズ (U単位) height: number; // サイズ (U単位) // 回転関連 rotation_x: number; // 回転中心X座標 rotation_y: number; // 回転中心Y座標 rotation_angle: number; // 回転角度 // 複雑な形状対応 x2: number; // 第2矩形の相対位置 y2: number; // 第2矩形の相対位置 width2: number; // 第2矩形のサイズ height2: number; // 第2矩形のサイズ // 表示・装飾関連 color: string; // キーキャップ色 labels: string[]; // 最大12個のラベル textColor: Array; textSize: Array; profile: string; // キープロファイル // フラグ類 decal: boolean; // デカールモード ghost: boolean; // ゴーストモード stepped: boolean; // ステップキー nub: boolean; // ホーミング突起 // スイッチ情報 sm: string; // スイッチマウント sb: string; // スイッチブランド st: string; // スイッチタイプ } ``` ## 互換性マトリックス ### ✅ 完全対応可能 | PHYレイヤー機能 | KLEプロパティ | 備考 | |---|---|---| | キー座標(x,y) | `x`, `y` | 1:1対応、U単位 | | キーサイズ(w,h) | `width`, `height` | 1:1対応、U単位 | | 回転角度 | `rotation_angle` | 1:1対応 | | 回転中心 | `rotation_x`, `rotation_y` | 1:1対応 | | 0.25U単位スナップ | - | KLEも小数点座標対応 | | 複雑形状キー | `x2`, `y2`, `width2`, `height2` | ISO Enter等に対応 | | プロファイル設定 | `profile` | SA、DCS、OEM等対応 | ### ⚠️ 部分対応・要改造 | PHYレイヤー機能 | KLE対応状況 | 改造内容 | |---|---|---| | クラスター管理 | 非対応 | カスタムメタデータとしてlabelsに格納 | | 複数プロファイル | 一部対応 | メタデータで管理、UI拡張必要 | | ユニットサイズ設定 | 非対応 | グローバル設定として追加実装 | | ビジュアルオフセット | 非対応 | レンダリング層で独自実装 | ### ❌ 対応困難・KLE制約 | PHYレイヤー機能 | KLE制約 | 回避策 | |---|---|---| | 15度以外の回転角度 | UI制約(内部は対応可能) | UI改造またはAPI直接操作 | | 階層的クラスター | データモデル非対応 | ゴーストキーによるメタデータ保存 | | クラスター別回転中心 | 直接非対応 | 各キーの回転中心で代替 | ## 具体的な実装戦略 ### 1. KLEベースデータ変換 PHYレイヤーからKLE形式への変換: ```typescript // PHYレイヤー → KLE変換例 function convertToKLE(phyData: PHYLayer): KLEData { return phyData.elements.map(element => { if (element.type === 'key') { return { x: element.position.x, y: element.position.y, width: element.size.width, height: element.size.height, rotation_x: element.position.rotationOrigin?.x || 0, rotation_y: element.position.rotationOrigin?.y || 0, rotation_angle: element.position.rotation || 0, // クラスター情報をlabelsの特定位置に格納 labels: [element.clusterId || '', '', '', '', '', '', '', '', '', '', '', ''], // プロファイル情報 profile: element.profile || 'OEM' }; } // クラスターの場合は展開処理 }); } ``` ### 2. KLE UI改造点 #### 必要な改造項目 1. **クラスター管理UI** - クラスター作成・編集パネル - クラスター単位の選択・移動 - クラスター階層表示 2. **プロファイル管理** - 複数プロファイル切り替えUI - プロファイル別ユニットサイズ設定 3. **メタデータ拡張** - PHYレイヤー固有情報の保存 - 互換性情報の管理 #### UI改造の難易度 - **低難易度**: 既存プロパティパネルの拡張 - **中難易度**: クラスター管理機能の追加 - **高難易度**: レンダリングエンジンの改造 ### 3. 制限事項と回避策 #### 現在のKLE制約 1. **データモデル制約** - フラットな配列構造のみ対応 - 階層的クラスター管理は困難 2. **レンダリング制約** - HTMLベースのレンダリング - 複雑な視覚効果には限界 #### 回避策 1. **段階的対応** - Phase 1: 基本的な位置・サイズ・回転のみ対応 - Phase 2: クラスター管理機能追加 - Phase 3: 高度なビジュアル機能 2. **ハイブリッドアプローチ** - KLEをベースエディタとして使用 - 独自機能は別パネルで管理 - 最終出力時にPHYレイヤー形式で統合 ## 推奨実装アプローチ ### フェーズ1: KLE最小改造アプローチ(推奨) 1. **KLEの既存機能をそのまま活用** - 基本的な配置・サイズ・回転 - プロファイル設定 - 色・ラベル設定 2. **最小限の拡張** - カスタムメタデータ保存機能 - PHYレイヤー形式エクスポート 3. **段階的機能追加** - クラスター管理(Phase 2) - 高度なビジュアル機能(Phase 3) ### 実装優先順位 1. **高優先度** - 即座に対応可能 - ✅ 基本位置・サイズ・回転 - ✅ プロファイル設定 - ✅ KLE→PHYレイヤー変換 2. **中優先度** - 改造が必要だが実現可能 - ⚠️ 簡易クラスター管理 - ⚠️ 複数プロファイル対応 - ⚠️ カスタムユニットサイズ 3. **低優先度** - 大規模改造が必要 - ❌ 階層的クラスター - ❌ 高度なビジュアルオフセット - ❌ リアルタイム3Dプレビュー ## 結論 **KLEをベースとしたPHYレイヤーエディタの実現は十分可能**です。 ### 対応可能な機能(約80%) - 基本的なキー配置・サイズ・回転 - 複雑形状キー(ISO Enter等) - プロファイル管理 - 基本的なクラスター管理 ### 改造が必要な機能(約15%) - 高度なクラスター管理 - 複数プロファイル切り替え - カスタムユニットサイズ ### 対応困難な機能(約5%) - 完全階層的クラスター構造 - 高度なビジュアルエフェクト PHYレイヤーの主要機能はKLEで十分実現可能であり、段階的な改造により完全対応も期待できます。 ## 階層型クラスターの実装戦略 ### 問題提起 ユーザーからの要求: - **階層型クラスター管理は絶対に実装したい** - 出力時はフラット化してもOK(階層情報を何らかの形で記録できれば) - ラベルに突っ込むと見づらくなる懸念 - **ゴーストキーによる情報保存**のアイデア ### ゴーストキーアプローチの詳細設計 #### 基本概念 クラスター情報を`ghost: true`のキーとして保存し、実際のキーレンダリングには影響を与えずにメタデータとして扱う。 ```typescript // クラスター情報保存用ゴーストキー type ClusterGhostKey = { ghost: true, decal: false, x: number, // クラスター範囲の左上角 y: number, // クラスター範囲の左上角 width: number, // クラスター範囲の幅 height: number, // クラスター範囲の高さ rotation_x: number, // クラスター回転中心 rotation_y: number, // クラスター回転中心 rotation_angle: number, // クラスター回転角度 color: "#transparent", // 透明 labels: [ "CLUSTER_META", // [0] 識別子 clusterId, // [1] クラスターID parentClusterId, // [2] 親クラスターID(階層用) clusterName, // [3] クラスター名 JSON.stringify(metadata), // [4] その他メタデータ "", "", "", "", "", "", "" // [5-11] 予約 ] } ``` #### 階層構造の表現 ```typescript // 例:分割キーボードの左右ブロック + 各ブロック内のキークラスター const clusterHierarchy = { "root": { children: ["left_block", "right_block"] }, "left_block": { parent: "root", children: ["left_alpha", "left_modifiers"] }, "right_block": { parent: "root", children: ["right_alpha", "right_modifiers"] }, "left_alpha": { parent: "left_block", keyIds: ["Q", "W", "E", "R", "T", "A", "S", "D", "F", "G", ...] } // ... } ``` #### KLE UI拡張による階層管理 1. **クラスター管理パネル** ```html
``` 2. **クラスター操作機能** - ドラッグ&ドロップによるキーのクラスター移動 - 階層ツリーでのクラスター選択 - クラスター単位での一括変換(移動・回転・リサイズ) - クラスター単位でのコピー&ペースト ### データフロー設計 #### 1. 編集時(UI → 内部データ) ```javascript // クラスター作成時 function createCluster(name, parentId, selectedKeys) { const clusterId = generateUniqueId(); // ゴーストキーを作成 const ghostKey = createClusterGhostKey({ id: clusterId, name: name, parentId: parentId, boundingBox: calculateBoundingBox(selectedKeys) }); // 選択されたキーにクラスターIDを付与 selectedKeys.forEach(key => { key.clusterId = clusterId; // カスタムプロパティとして追加 }); // KLEのキー配列に追加 scope.keys().push(ghostKey); } ``` #### 2. 保存時(内部データ → KLE JSON) ```javascript function serializeWithClusters(keyboard) { const kleData = serial.serialize(keyboard); // 通常のキーとゴーストキーが混在した状態で保存 // ゴーストキーがクラスター情報を保持 return kleData; } ``` #### 3. 読み込み時(KLE JSON → 内部データ) ```javascript function deserializeWithClusters(kleData) { const keyboard = serial.deserialize(kleData); // ゴーストキーからクラスター情報を復元 const clusterMap = new Map(); const normalKeys = []; keyboard.keys.forEach(key => { if (key.ghost && key.labels[0] === 'CLUSTER_META') { const clusterId = key.labels[1]; const parentId = key.labels[2]; const metadata = JSON.parse(key.labels[4]); clusterMap.set(clusterId, { ghostKey: key, parentId: parentId, metadata: metadata, children: new Set() }); } else { normalKeys.push(key); } }); // 階層構造を再構築 buildClusterHierarchy(clusterMap); return { keyboard: { ...keyboard, keys: normalKeys }, clusters: clusterMap }; } ``` ### 15度回転制約について #### 現在の制約の詳細 KLEのソースコード分析結果: ```javascript $scope.rotateStep = 15; // UI上のデフォルト回転ステップ // 実際の制約チェック(kb.js内) rotation_angle: function() { return Math.max(-180, Math.min(180, value)); // ±180度の範囲制約のみ } ``` **つまり:** - **UIでは15度ステップで回転操作**(ユーザビリティのため) - **内部データには制約なし**(任意の角度を保存可能) - **JSON出力も制約なし**(小数点を含む任意の角度値) #### PHYレイヤーでの対応 ```javascript // PHY → KLE変換時に任意角度をそのまま渡す function convertRotation(phyRotation) { return phyRotation; // 制約なし、そのまま変換 } // KLE UI拡張で任意角度入力を許可 function enableCustomRotation() { // 既存の15度ステップに加えて、直接入力機能を追加 scope.customRotateStep = 1; // 1度ステップ scope.allowCustomAngle = true; // 任意角度入力許可 } ``` ## 実装優先度の再評価 ### 修正版優先度 1. **最高優先度** - 即座に対応可能 - ✅ 基本位置・サイズ・回転(任意角度対応) - ✅ プロファイル設定 - ✅ KLE→PHYレイヤー変換 2. **高優先度** - ゴーストキーアプローチで対応 - 🔶 階層型クラスター管理(ゴーストキー方式) - 🔶 クラスターメタデータ保存 - 🔶 クラスター単位操作UI 3. **中優先度** - UI改造が必要 - ⚠️ 複数プロファイル対応 - ⚠️ カスタムユニットサイズ - ⚠️ 15度以外の回転角度UI対応 4. **低優先度** - 大規模改造が必要 - ❌ 高度なビジュアルオフセット - ❌ リアルタイム3Dプレビュー ### 実装結論 **ゴーストキーアプローチにより、階層型クラスター管理は実現可能**です。この方法なら: - KLEの既存データモデルを活用 - 見た目に影響しない形でメタデータ保存 - 階層構造の完全な再現が可能 - 段階的な実装が可能 階層型クラスターが「絶対実装したい」要求に対して、技術的に実現可能な解決策を提供できます。