# 機能設計書: Euler（オイラー角/回転の表現）

## 1. 機能概要

### 1.1 機能の目的
Eulerは、オイラー角を使用して3D空間における回転を表現するためのクラスである。X、Y、Z軸周りの回転角度と回転順序（order）を指定することで、直感的に回転を記述できる。人間にとって理解しやすい回転表現として広く使用される。

### 1.2 主な機能
- オイラー角の設定と取得
- 回転順序（order）の管理
- 回転行列からの変換
- クォータニオンからの変換
- ベクトルからの設定
- 回転順序の変更（reorder）

### 1.3 関連する画面/コンポーネント
- Object3Dの回転設定
- カメラの回転
- エディターでの回転入力
- アニメーションの回転キーフレーム

## 2. 機能仕様

### 2.1 データ構造

```javascript
class Euler {
    _x: number;      // X軸回りの回転（ラジアン）
    _y: number;      // Y軸回りの回転（ラジアン）
    _z: number;      // Z軸回りの回転（ラジアン）
    _order: string;  // 回転順序
    isEuler: boolean; // 型判定フラグ
}
```

### 2.2 プロパティ詳細

| プロパティ名 | 型 | 説明 | デフォルト値 |
|-------------|-----|------|-------------|
| x | number | X軸回りの回転角度（ラジアン） | 0 |
| y | number | Y軸回りの回転角度（ラジアン） | 0 |
| z | number | Z軸回りの回転角度（ラジアン） | 0 |
| order | string | 回転順序 | 'XYZ' |
| isEuler | boolean | 型判定フラグ（読み取り専用） | true |

### 2.3 回転順序（order）
サポートされる回転順序:
- 'XYZ' - X→Y→Zの順で回転（デフォルト）
- 'YXZ' - Y→X→Zの順で回転
- 'ZXY' - Z→X→Yの順で回転
- 'ZYX' - Z→Y→Xの順で回転
- 'YZX' - Y→Z→Xの順で回転
- 'XZY' - X→Z→Yの順で回転

### 2.4 メソッド詳細

#### 2.4.1 基本操作
| メソッド | 説明 |
|---------|------|
| set(x, y, z, order) | 成分と回転順序を設定 |
| clone() | 複製を作成 |
| copy(euler) | 他のオイラー角をコピー |

#### 2.4.2 変換（From）
| メソッド | 説明 |
|---------|------|
| setFromRotationMatrix(m, order, update) | 回転行列から設定 |
| setFromQuaternion(q, order, update) | クォータニオンから設定 |
| setFromVector3(v, order) | ベクトルから設定 |

#### 2.4.3 回転順序操作
| メソッド | 説明 |
|---------|------|
| reorder(newOrder) | 回転順序を変更（回転は維持） |

#### 2.4.4 その他
| メソッド | 説明 |
|---------|------|
| equals(euler) | 等値比較 |
| fromArray(array) | 配列から設定 |
| toArray(array, offset) | 配列に出力 |

### 2.5 ビジネスルール
- 角度はラジアンで指定
- 演算は元のオイラー角を変更する（ミュータブル）
- setterでは_onChangeCallback()が呼ばれる
- デフォルト回転順序は'XYZ'（Euler.DEFAULT_ORDER）

### 2.6 ジンバルロック
オイラー角には特定の回転角度でジンバルロックが発生する問題がある。
- 例: 'XYZ'順でY軸回りに90度回転するとXとZ軸が一致
- 解決策: クォータニオンを使用するか、回転順序を変更

## 3. 処理フロー

### 3.1 setFromRotationMatrix()フロー
```
[setFromRotationMatrix(m, order, update)]
        |
        v
[行列要素取得]
m11, m12, ... = te[...]
        |
        v
[orderによる分岐]
        |
   +----+----+----+----+----+----+
   |    |    |    |    |    |    |
 XYZ  YXZ  ZXY  ZYX  YZX  XZY
   |    |    |    |    |    |    |
   v    v    v    v    v    v    v
[角度計算（各順序に応じた公式）]
        |
        v
[order設定]
this._order = order
        |
        v
[update == true?]
   Yes |         No
       v          |
[_onChangeCallback()]
       |          |
       v          v
[自身を返却]<-----'
```

### 3.2 setFromQuaternion()フロー
```
[setFromQuaternion(q, order, update)]
        |
        v
[クォータニオンから回転行列生成]
_matrix.makeRotationFromQuaternion(q)
        |
        v
[回転行列からオイラー角設定]
this.setFromRotationMatrix(_matrix, order, update)
        |
        v
[自身を返却]
```

### 3.3 reorder()フロー
```
[reorder(newOrder)]
        |
        v
[現在のオイラー角からクォータニオン生成]
_quaternion.setFromEuler(this)
        |
        v
[クォータニオンから新しい順序でオイラー角設定]
this.setFromQuaternion(_quaternion, newOrder)
        |
        v
[自身を返却]
```

## 4. コードリーディングガイド

### 4.1 推奨読解順序
1. **Euler.js** - クラス定義とプロパティ（1-51行目）
2. コンストラクタとgetters/setters（35-127行目）
3. setFromRotationMatrix()（189-318行目）
4. setFromQuaternion()（328-334行目）
5. reorder()（359-365行目）

### 4.2 重要な処理の詳細

#### コンストラクタ（35-51行目）
```javascript
constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) {
    this.isEuler = true;

    this._x = x;
    this._y = y;
    this._z = z;
    this._order = order;
}
```

#### setFromRotationMatrix()（189-318行目）
```javascript
setFromRotationMatrix( m, order = this._order, update = true ) {
    const te = m.elements;
    const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
    const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
    const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];

    switch ( order ) {
        case 'XYZ':
            this._y = Math.asin( clamp( m13, - 1, 1 ) );

            if ( Math.abs( m13 ) < 0.9999999 ) {
                this._x = Math.atan2( - m23, m33 );
                this._z = Math.atan2( - m12, m11 );
            } else {
                this._x = Math.atan2( m32, m22 );
                this._z = 0;
            }
            break;

        case 'YXZ':
            // ... 他の回転順序
    }

    this._order = order;

    if ( update === true ) this._onChangeCallback();

    return this;
}
```
回転順序に応じて異なる公式でオイラー角を抽出

#### ジンバルロック対策（202-212行目）
```javascript
if ( Math.abs( m13 ) < 0.9999999 ) {
    // 通常ケース
    this._x = Math.atan2( - m23, m33 );
    this._z = Math.atan2( - m12, m11 );
} else {
    // ジンバルロック付近
    this._x = Math.atan2( m32, m22 );
    this._z = 0;
}
```
m13が±1に近い場合（90度回転）は特殊処理

#### setFromQuaternion()（328-334行目）
```javascript
setFromQuaternion( q, order, update ) {
    _matrix.makeRotationFromQuaternion( q );
    return this.setFromRotationMatrix( _matrix, order, update );
}
```
クォータニオン→回転行列→オイラー角の変換パス

#### reorder()（359-365行目）
```javascript
reorder( newOrder ) {
    _quaternion.setFromEuler( this );
    return this.setFromQuaternion( _quaternion, newOrder );
}
```
回転を維持しながら順序を変更（クォータニオン経由）

### 4.3 プログラム呼び出し階層図
```
Euler
├── 基本操作
│   ├── set()
│   ├── clone() / copy()
│   └── getters/setters (x, y, z, order)
├── 変換（From）
│   ├── setFromRotationMatrix()
│   │   └── [各回転順序の計算分岐]
│   ├── setFromQuaternion()
│   │   ├── _matrix.makeRotationFromQuaternion()
│   │   └── setFromRotationMatrix()
│   └── setFromVector3() → set()
├── 回転順序操作
│   └── reorder()
│       ├── _quaternion.setFromEuler()
│       └── setFromQuaternion()
└── その他
    ├── equals()
    ├── fromArray() / toArray()
    └── _onChange() / _onChangeCallback()
```

### 4.4 関連ファイル一覧
| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| src/math/Euler.js | メイン | Eulerクラスの実装 |
| src/math/Quaternion.js | 関連 | クォータニオンとの相互変換 |
| src/math/Matrix4.js | 関連 | 回転行列との相互変換 |
| src/math/Vector3.js | 関連 | ベクトルからの設定 |
| src/math/MathUtils.js | 関連 | clamp関数 |
| src/core/Object3D.js | 利用者 | オブジェクトの回転 |

## 5. 使用例

### 5.1 基本的な使用例
```javascript
// オイラー角の作成
const euler = new THREE.Euler(0, Math.PI / 2, 0, 'XYZ');

// 成分の設定
euler.set(Math.PI / 4, 0, 0);

// 個別設定
euler.x = Math.PI / 2;
euler.y = Math.PI / 4;
euler.z = 0;
```

### 5.2 Object3Dでの使用
```javascript
const mesh = new THREE.Mesh(geometry, material);

// rotationプロパティはEuler
mesh.rotation.x = Math.PI / 4;
mesh.rotation.y = Math.PI / 2;
mesh.rotation.set(0, Math.PI, 0);
```

### 5.3 回転順序の変更
```javascript
const euler = new THREE.Euler(Math.PI / 4, Math.PI / 2, 0, 'XYZ');

// 回転順序を変更（回転自体は維持）
euler.reorder('YXZ');
```

### 5.4 クォータニオンとの変換
```javascript
const euler = new THREE.Euler(0, Math.PI / 2, 0, 'XYZ');
const quaternion = new THREE.Quaternion();

// オイラー角→クォータニオン
quaternion.setFromEuler(euler);

// クォータニオン→オイラー角
euler.setFromQuaternion(quaternion, 'XYZ');
```

### 5.5 度数法での使用
```javascript
// 度をラジアンに変換
const degToRad = THREE.MathUtils.degToRad;

const euler = new THREE.Euler(
    degToRad(45),   // 45度
    degToRad(90),   // 90度
    degToRad(0)     // 0度
);
```

## 6. 備考

### 6.1 クォータニオンとの比較
| 項目 | Euler | Quaternion |
|------|-------|------------|
| 直感性 | 高い | 低い |
| ジンバルロック | あり | なし |
| 補間 | 不自然 | SLERP（滑らか） |
| メモリ | 4成分 | 4成分 |
| 回転合成 | 行列経由 | 乗算 |

### 6.2 回転順序の選択
- 'YXZ': FPSカメラによく使用（pitch-yaw-roll）
- 'ZYX': 航空機の姿勢によく使用（yaw-pitch-roll）
- 'XYZ': three.jsのデフォルト

### 6.3 注意事項
- 角度はラジアンで指定（度数法ではない）
- setterは_onChangeCallback()を呼ぶ（Object3Dとの連携）
- reorder()は回転情報が失われる可能性がある（警告あり）
- 90度付近でのジンバルロックに注意

### 6.4 ジンバルロックの回避策
1. クォータニオンを使用する（推奨）
2. 異なる回転順序を使用する
3. ジンバルロック付近の角度を避ける

### 6.5 パフォーマンス
- setFromRotationMatrix()は6つのswitch分岐がある
- クォータニオン経由の変換は行列生成が必要
- 頻繁な回転操作にはクォータニオンを推奨
