KLE互換ドキュメント2本を廃止し、裏取りした事実をPHYに書き戻す

kle-compatibility.md / kle-compatibility-analysis.md は埋め込み前提の
設計で、KLE往復の決定と矛盾していた。KLE本家ソースで確認できた事実だけを
PHYレイヤー.md に書き戻し、2本は削除した。

- ConfigObject の回転角度キーを ra から r に修正し、fa sm sb st を追加
- KLE出力で失われる情報、KLEの p がPHYのプロファイルと別物であることを明記
- KLEは保存時にキーを並べ替えるため、配列順は照合に使わない
- 過去のKLE JSONはPHYデータなしでも読めなければならない、という制約を追加
- 未確認事項に P-3 クラスター自動推定、P-4 KLE固有の形状・属性の受け止め先を追加

Journal: journal/2026-09-18-kle-compat-docs-retire.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 21:18:49 +09:00
co-authored by Claude Opus 5
parent 1a9a2a06c5
commit bb1108763a
5 changed files with 78 additions and 708 deletions
-2
View File
@@ -13,8 +13,6 @@ PHY(物理配置) / ELC(電気配線) / IDX(キー識別) / MAP(キーマップ)
| `keyboard-design-layers.md` | レイヤー構造とツール分割方針(最上位) |
| `PHYレイヤー.md` | PHY層の仕様 |
| `phase1-json-schema.md` | 各層のJSON Schema |
| `kle-compatibility.md` | KLEとの双方向変換仕様 |
| `kle-compatibility-analysis.md` | KLE互換性の調査・案比較 |
| `keyboard-making-workflow.md` | 既存の自作キーボード制作フローとの対応 |
| `typescript-implementation-plan.md` | 実装計画・技術スタック |
+32 -4
View File
@@ -98,6 +98,7 @@ KLEで編集された時点で、そのデータはBeyond KLEのデータでは
- KLE JSONにはBeyond KLE固有の情報(キーID、クラスター構造など)を含めない。出力は素のKLE JSONで、KLE JSONを読む他ツールにそのまま渡せる
- KLE JSONの取り込みは、手元のPHYデータとの照合で行う。IDや埋め込み情報には頼らない
- 過去のKLE JSONは、PHYデータがなくても読み込めなければならない。変換を伴ってよい
### KLE JSON出力
@@ -113,9 +114,9 @@ type ConfigObject = {
y2?: number,
w2?: number,
h2?: number,
rx?: number,
r?: number, // 回転角度
rx?: number, // 回転中心
ry?: number,
ra?: number,
l?: boolean,
n?: boolean,
d?: boolean,
@@ -125,14 +126,22 @@ type ConfigObject = {
a?: number,
f?: number,
f2?: number,
fa?: number[],
p?: string,
s?: string
sm?: string,
sb?: string,
st?: string
}
type KeyLabel = string // 表示用ラベルのみ。識別情報は含めない
const kleData = (ConfigObject | KeyLabel)[][]
```
- 回転角度は正規化せずそのまま出力する。KLEのデータは任意の角度を受け付け、15度刻みはKLEのUI上の操作単位にすぎない
- rx, ry はPHYに持たないので、出力時に選ぶ
- ユニットサイズ(mm)、ビジュアルオフセット、クラスター構造はKLE JSONに出力されない
- KLEの `p` はキーキャップのプロファイル名(SA、DCSなど)であり、PHYのプロファイルとは対応させない
### KLE JSONの取り込み
既存のPHYデータを畳んだ配置に対して、KLE JSONの各キーを形状(位置・サイズ・回転)とラベルで照合する。
@@ -140,6 +149,9 @@ const kleData = (ConfigObject | KeyLabel)[][]
- 照合できたキーは元のノードとして扱う。KLE側で配置が変わっていれば、元の親から見た相対座標に戻して反映する
- 照合できないキーは root 直下に新規キーとして置く。クラスターへの割り当てはユーザーが行う
- 構造の復元は保証しない。KLE側での編集が大きいほど、クラスター構造は失われる
- 配列の順番は照合に使わない。KLEは保存時にキーを「角度 → rx → ry → y → x」の順に並べ替えるため、順番は形状から決まる以上の情報を持たない
KLEの rx, ry, r はキーの絶対配置 (x, y, rotation) に変換して取り込む。元の rx, ry は保持しない。
## 検討済み・非採用
@@ -154,10 +166,26 @@ const kleData = (ConfigObject | KeyLabel)[][]
- 位置づけ: 本文が前提にしているものではない。連携用出力形式の設計に関わる未解決の問い
- 現状の認識: 階層を畳んでKLEに読み込ませると行のまとまりが崩れる。他レイヤーの情報を使ってでも行ごとにまとめたいが、方法は未検討
- 手がかり: KLEは r, rx, ry が同じで y が等しいキーを1行にまとめる。同じ絶対角度を持つキーに共通の rx, ry(例えばクラスターの原点)を選べば、行にまとめられる見込み
- 格上げ条件: 回転を含むPHYデータをKLE JSONに変換してKLE本家に読み込ませ、rotation groupと行配列がどう再構成されるかを確認する
### P-2 KLE JSON取り込み時の照合基準
- 位置づけ: 本文「KLE JSONの取り込み」の具体化に必要な未解決の問い
- 現状の認識: 形状の許容誤差、ラベルの扱い、複数候補がある場合の決め方が未定。KLEが保存時にキーの並び順を変えるかは未確認で、配列順を照合に使えるかも分からない
- 現状の認識: 形状の許容誤差、ラベルの扱い、複数候補がある場合の決め方が未定
- 格上げ条件: 照合規則を決めたうえで、KLE本家でキーの移動・追加・削除をしたJSONを取り込み、意図どおりに対応づくことを確認する
### P-3 KLE JSON取り込み時のクラスター自動推定
- 位置づけ: 未解決の問い。PHYデータなしで過去のKLE JSONを取り込むとき、座標の近さや角度からクラスターを推定するか
- 現状の認識: 採用しない可能性が高い。ただし代わりに欲しい機能があり、それを思い出すきっかけとして残している
- 格上げ条件: 代わりの機能を定義し、この推定を採るか捨てるかをユーザーが決める
### P-4 KLE固有の形状と属性の受け止め先
- 位置づけ: 本文「過去のKLE JSONは読み込めなければならない」を満たすために必要な未解決の問い
- 現状の認識: 現在の Key 型(transform, size, profile)では次を受け止められない
- 長方形でないキー(x2, y2, w2, h2。ISO Enterなど)。受け止められないとISO配列が読めない。ローカルの値なので Size の拡張で相対座標モデルに収まる見込み
- デカールとゴースト(d, g)。PHYで持つか、取り込み時に捨てるか
- 見た目の属性(ラベル、c, t, p, f系, l, n)。落としてよいか、別の層で保持するか。ラベルは照合にも使う
- 格上げ条件: 各項目について、PHYで持つ・他の層で持つ・捨てるのいずれかをユーザーが決め、Key 型に反映する
@@ -0,0 +1,46 @@
# KLE互換ドキュメント2本の廃止と書き戻し
## 結論
- kle-compatibility.md / kle-compatibility-analysis.md を作業ツリーから削除した
- 原本: `git show 1a9a2a0:kle-compatibility.md` / `git show 1a9a2a0:kle-compatibility-analysis.md`
- 経緯: compatibility は初期PHYドキュメントをもとに検討しながら更新したもの。analysis はそれをさらに分析したもの(ユーザー談)
- PHYレイヤー.md に書き戻したもの(KLE本家ソースで裏取り済み)
- ConfigObject の修正: `ra` → `r`、`fa` `sm` `sb` `st` を追加
- 回転角度は正規化せず出力できる(15度はUIの操作単位)
- ユニットサイズ・ビジュアルオフセット・クラスター構造はKLEに出力されない
- KLEの `p` はキーキャップのプロファイル名で、PHYのプロファイルとは別物
- KLEは保存時にキーを並べ替えるので、配列順は照合に使えない
- P-1 に行まとめの手がかりを追記
- 新たに本文に加えた制約: 過去のKLE JSONはPHYデータなしでも読めなければならない(ユーザー要件。変換は可)
- 未確認事項として立てたもの: P-3 クラスター自動推定(未決。代わりに欲しい機能を思い出すきっかけ)、P-4 KLE固有の形状・属性の受け止め先
## 各記述の行き先
| 元の記述 | 行き先 |
|---|---|
| 階層を畳む flattenElements | 既にPHYにある(階層を畳んで絶対座標に変換) |
| ID保持方法の比較、`_beyondkle` ハイブリッド、ID復元 | 捨てた。2026-09-18-kle-roundtrip で不採用 |
| 往復で構造が一致することを保証するテスト | 捨てた。「構造の復元は保証しない」と矛盾 |
| ConversionSettings, バージョン管理メタデータ | 捨てた。埋め込み前提 |
| autoDetectClusters | PHY 未確認事項 P-3 |
| CLI コマンド例 (export/import) | 捨てた。CLIの形は実装計画側で決める |
| KLE Key クラスのプロパティ一覧、互換性マトリックス | 事実部分のみ ConfigObject とKLE出力の制約として書き戻し |
| 「プロファイル設定 ↔ p が1:1対応」 | 誤り。KLEの p はキャップ形状名 |
| KLE UI改造の項目・難易度・80/15/5%・フェーズ分け | 捨てた。KLE改造か自作かを検討するときは原本を git show で読む |
| ゴーストキーによる階層クラスター | 捨てた。2026-09-18-kle-roundtrip で不採用 |
| 15度回転制約の分析 | 結論のみ書き戻し。analysis 中の kb.js 引用コードは実物と一致(kb.js:817) |
## 検証ログ
ijprest/keyboard-layout-editor master (2026-09-18 取得)
- serial.js:92-100 sortKeys。角度((a+360)%360) → rotation_x → rotation_y → y → x の順に並べ替える
- serial.js:187 serialize の中で sortKeys を呼ぶ。保存のたびに並べ替わる
- serial.js:193-198 行が切り替わる条件は y の変化か (r, rx, ry) の変化
- serial.js:219-269 出力されるプロパティは r rx ry y x c t g p sm sb st a f f2 fa w h w2 h2 x2 y2 n l d で決め打ち。未知のプロパティは落ちる
- 2026-09-18-kle-roundtrip で「未確認」とした「シリアライザは既知プロパティしか書かない」はこれで確定
- serial.js:105 defaultKeyProps で profile は "cap appearance" に分類されている
- serial.js:176-178 メタデータは keyboard.meta の全キーをループで書き出す。未知のメタデータキーも保存時に残る(今回の決定では使わない)
- kb.js:78 rotateStep = 15。kb.js:817 プロパティ入力は ±180 にクランプするだけ。kb.js:1119 キー操作による回転は Math.round で整数度に丸める
- kb.css:94 `.ghosted { opacity: 0.5; }`。ゴーストキーは半透明で表示される。analysis の「見た目に影響しない」は誤り
-453
View File
@@ -1,453 +0,0 @@
# 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<string | undefined>;
textSize: Array<number | undefined>;
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
<!-- 新しいサイドパネル -->
<div id="cluster-manager">
<tree-view>
<cluster-node id="root">
<cluster-node id="left_block">
<cluster-node id="left_alpha" />
<cluster-node id="left_modifiers" />
</cluster-node>
<cluster-node id="right_block">
<cluster-node id="right_alpha" />
<cluster-node id="right_modifiers" />
</cluster-node>
</cluster-node>
</tree-view>
</div>
```
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の既存データモデルを活用
- 見た目に影響しない形でメタデータ保存
- 階層構造の完全な再現が可能
- 段階的な実装が可能
階層型クラスターが「絶対実装したい」要求に対して、技術的に実現可能な解決策を提供できます。
-249
View File
@@ -1,249 +0,0 @@
# KLE JSON 互換性設計
## 双方向変換戦略
### Beyond KLE → KLE JSON
#### 1. フラット化プロセス
```typescript
interface ConversionOptions {
preserveIds: boolean; // IDを保持するか
preserveClusterInfo: boolean; // クラスター情報を保持するか
idEmbedMethod: 'metadata' | 'label' | 'comment';
}
function convertToKLE(beyondKle: PHYLayer, options: ConversionOptions): KLELayout {
const flatKeys = flattenElements(beyondKle.elements);
return {
name: beyondKle.metadata.name,
keys: flatKeys.map(key => convertKey(key, options))
};
}
function flattenElements(elements: Element[], parentTransform?: Transform): FlatKey[] {
const result: FlatKey[] = [];
for (const element of elements) {
if (element.type === 'key') {
const absolutePos = applyTransform(element.position, parentTransform);
result.push({
...element,
absolutePosition: absolutePos,
clusterPath: getClusterPath(element)
});
} else if (element.type === 'cluster') {
const clusterTransform = combineTransforms(parentTransform, element.position);
result.push(...flattenElements(element.elements, clusterTransform));
}
}
return result;
}
```
#### 2. ID保持方法の比較
| 方法 | メリット | デメリット | KLE互換性 |
|------|----------|------------|-----------|
| `_beyondkle` メタデータ | 構造化された情報保持 | KLEパーサーが無視する可能性 | △ |
| ラベル9番目位置 | 確実に保存される | 表示されてしまう | ○ |
| コメントフィールド | 非表示 | 標準的でない | △ |
#### 3. 推奨アプローチ: ハイブリッド方式
```json
{
"name": "Split Layout (Generated by Beyond KLE)",
"_beyondkle": {
"version": "1.0.0",
"sourceFile": "layout.beyond.json",
"conversionSettings": {
"flattenClusters": true,
"preserveRotations": true
}
},
"keys": [
{
"x": 0, "y": 0, "w": 1, "h": 1,
"t": "#000000\n\n\n\n\n\n\n\n\nk00",
"_beyondkle": {
"id": "k00",
"clusterPath": ["left_half", "left_main"],
"relativePosition": { "x": 0, "y": 0 },
"absolutePosition": { "x": 0, "y": 0 }
}
}
]
}
```
### KLE JSON → Beyond KLE
#### 1. ID復元戦略
```typescript
function convertFromKLE(kleLayout: KLELayout): PHYLayer {
// 1. メタデータからBeyond KLE情報を取得
const beyondKleInfo = kleLayout._beyondkle;
if (beyondKleInfo?.sourceFile) {
// 元のBeyond KLEファイルが指定されている場合
return reconstructFromSource(kleLayout, beyondKleInfo);
}
// 2. キーからIDとクラスター情報を抽出
const keys = kleLayout.keys.map(kleKey => {
const id = extractId(kleKey);
const clusterInfo = extractClusterInfo(kleKey);
return {
type: 'key' as const,
id: id || generateId(kleKey),
profile: 'default',
position: { x: kleKey.x, y: kleKey.y },
size: { width: kleKey.w || 1, height: kleKey.h || 1 },
metadata: extractMetadata(kleKey)
};
});
// 3. クラスター構造の再構築
return reconstructClusters(keys);
}
function extractId(kleKey: any): string | null {
// 1. _beyondkle メタデータから
if (kleKey._beyondkle?.id) return kleKey._beyondkle.id;
// 2. ラベル9番目から
if (kleKey.labels?.[8]) return kleKey.labels[8];
// 3. t文字列の解析
const tMatch = kleKey.t?.match(/\n([a-zA-Z0-9_]+)$/);
if (tMatch) return tMatch[1];
return null;
}
```
#### 2. クラスター再構築アルゴリズム
```typescript
function reconstructClusters(flatKeys: Key[]): Element[] {
// クラスター情報が保持されている場合
const clusteredKeys = groupByCluster(flatKeys);
if (Object.keys(clusteredKeys).length > 1) {
return Object.entries(clusteredKeys).map(([clusterName, keys]) => ({
type: 'cluster' as const,
id: clusterName,
name: clusterName,
position: calculateClusterOrigin(keys),
elements: keys.map(key => ({
...key,
position: makeRelativeToCluster(key.position, clusterOrigin)
}))
}));
}
// クラスター情報がない場合は自動推定
return autoDetectClusters(flatKeys);
}
function autoDetectClusters(keys: Key[]): Element[] {
// 座標の近さとレイアウトパターンからクラスターを推定
// 1. 連続する矩形領域の検出
// 2. 回転角度の類似性
// 3. 一定の間隔での分離
const clusters = detectSpatialClusters(keys);
if (clusters.length === 1) {
// 単一クラスターの場合はフラットに
return keys;
}
return clusters;
}
```
## 実装における考慮事項
### 1. バージョン管理
```json
{
"_beyondkle": {
"version": "1.0.0",
"generator": "beyond-kle-converter",
"timestamp": "2025-11-09T12:00:00Z",
"preservedData": {
"clusters": true,
"rotations": true,
"profiles": false
}
}
}
```
### 2. 設定可能な変換オプション
```typescript
interface ConversionSettings {
// ID保持方法
idPreservation: 'metadata' | 'label' | 'both' | 'none';
// クラスター処理
clusterHandling: 'preserve' | 'flatten' | 'auto';
// 回転の処理
rotationHandling: 'preserve' | 'flatten';
// プロファイル情報
includeProfiles: boolean;
// 互換性レベル
compatibilityLevel: 'strict' | 'extended' | 'beyondkle';
}
```
### 3. 往復変換の整合性保証
```typescript
// テストケース
const originalBeyondKLE = loadBeyondKLE('test.beyond.json');
const kleJson = convertToKLE(originalBeyondKLE, settings);
const reconstructed = convertFromKLE(kleJson);
// 構造的等価性の検証
assert(structurallyEquivalent(originalBeyondKLE, reconstructed));
```
## ワークフロー例
### 1. Beyond KLE → 外部ツール → Beyond KLE
```bash
# Beyond KLE → KLE JSON (ID付き)
beyond-kle export --format=kle --preserve-ids layout.beyond.json
# 外部ツール(KLE等)で編集
# ...
# KLE JSON → Beyond KLE (構造復元)
beyond-kle import --format=kle --auto-cluster edited.kle.json
```
### 2. 段階的移行
```bash
# 既存のKLE JSON → Beyond KLE
beyond-kle import legacy.kle.json
# Beyond KLE で編集・管理
# 必要に応じてKLE JSONで共有
beyond-kle export --format=kle-compatible project.beyond.json
```
この設計により、Beyond KLEの高度な機能を使いながらも、既存のKLEエコシステムとの互換性を保てると思いますが、いかがでしょうか?