# 機能設計書: Quaternion（四元数/回転の表現）

## 1. 機能概要

### 1.1 機能の目的
Quaternionは、3D空間における回転を表現するためのクラスである。オイラー角と比較してジンバルロックが発生せず、回転の補間（SLERP）が滑らかに行えるため、3Dグラフィックスやアニメーションにおいて回転表現の標準として使用される。

### 1.2 主な機能
- 四元数の基本演算（乗算、共役、正規化）
- 様々な回転表現からの変換（オイラー角、軸角度、回転行列）
- 球面線形補間（SLERP）
- 2つのベクトル間の回転の計算
- 段階的な回転（rotateTowards）
- ランダム回転の生成

### 1.3 関連する画面/コンポーネント
- Object3Dの回転
- カメラの回転
- アニメーションの回転補間
- スケルタルアニメーション

## 2. 機能仕様

### 2.1 データ構造

```javascript
class Quaternion {
    _x: number;  // X成分（虚部）
    _y: number;  // Y成分（虚部）
    _z: number;  // Z成分（虚部）
    _w: number;  // W成分（実部）
    isQuaternion: boolean;  // 型判定フラグ
}
```

### 2.2 プロパティ詳細

| プロパティ名 | 型 | 説明 | デフォルト値 |
|-------------|-----|------|-------------|
| x | number | X成分（虚部） | 0 |
| y | number | Y成分（虚部） | 0 |
| z | number | Z成分（虚部） | 0 |
| w | number | W成分（実部） | 1 |
| isQuaternion | boolean | 型判定フラグ（読み取り専用） | true |

### 2.3 メソッド詳細

#### 2.3.1 基本操作
| メソッド | 説明 |
|---------|------|
| set(x, y, z, w) | 成分を設定 |
| clone() | 複製を作成 |
| copy(q) | 他のクォータニオンをコピー |
| identity() | 単位クォータニオン（無回転）に設定 |

#### 2.3.2 変換（From）
| メソッド | 説明 |
|---------|------|
| setFromEuler(euler, update) | オイラー角から設定 |
| setFromAxisAngle(axis, angle) | 軸と角度から設定 |
| setFromRotationMatrix(m) | 回転行列から設定 |
| setFromUnitVectors(vFrom, vTo) | 2つのベクトル間の回転を設定 |

#### 2.3.3 クォータニオン演算
| メソッド | 説明 |
|---------|------|
| multiply(q) | 後置乗算 (this * q) |
| premultiply(q) | 前置乗算 (q * this) |
| multiplyQuaternions(a, b) | a * b の結果を格納 |
| invert() | 逆クォータニオン（共役と同じ、正規化前提） |
| conjugate() | 共役クォータニオン |
| normalize() | 正規化（単位クォータニオンに） |
| dot(q) | 内積 |

#### 2.3.4 補間・回転
| メソッド | 説明 |
|---------|------|
| slerp(qb, t) | 球面線形補間 |
| slerpQuaternions(qa, qb, t) | 2つのクォータニオン間のSLERP |
| angleTo(q) | 2つのクォータニオン間の角度 |
| rotateTowards(q, step) | 指定角度分だけ回転 |
| random() | ランダムな回転を生成 |

#### 2.3.5 長さ
| メソッド | 説明 |
|---------|------|
| length() | 長さ（4次元ユークリッドノルム） |
| lengthSq() | 長さの2乗 |

#### 2.3.6 静的メソッド
| メソッド | 説明 |
|---------|------|
| Quaternion.slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) | フラット配列でのSLERP |
| Quaternion.multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) | フラット配列での乗算 |

### 2.4 ビジネスルール
- three.jsでは正規化されたクォータニオンを期待
- 単位クォータニオン（w=1, x=y=z=0）は無回転を表す
- 演算は元のクォータニオンを変更する（ミュータブル）
- setterでは_onChangeCallback()が呼ばれる

### 2.5 数学的背景
クォータニオン q = w + xi + yj + zk
- 回転角度θ、回転軸(ax, ay, az)の場合:
  - w = cos(θ/2)
  - x = ax * sin(θ/2)
  - y = ay * sin(θ/2)
  - z = az * sin(θ/2)

## 3. 処理フロー

### 3.1 setFromAxisAngle()フロー
```
[setFromAxisAngle(axis, angle)]
        |
        v
[半角度計算]
halfAngle = angle / 2
s = sin(halfAngle)
        |
        v
[成分設定]
this._x = axis.x * s
this._y = axis.y * s
this._z = axis.z * s
this._w = cos(halfAngle)
        |
        v
[コールバック呼び出し]
this._onChangeCallback()
        |
        v
[自身を返却]
```

### 3.2 slerp()フロー
```
[slerp(qb, t)]
        |
        v
[内積計算]
dot = this.dot(qb)
        |
        v
[dot < 0?]
   Yes |         No
       v          |
[符号反転]        |
qb成分を反転      |
dot = -dot        |
       |          |
       v          v
[dot < 0.9995?]
   Yes |         No
       v          |
[SLERP計算]       |
theta = acos(dot) |
sin = sin(theta)  |
s = sin((1-t)*θ)/sin
t = sin(t*θ)/sin  |
       |          v
       |   [LERP + 正規化]
       |   線形補間後に正規化
       |          |
       v          v
[結果を格納]<-----'
        |
        v
[自身を返却]
```

### 3.3 setFromRotationMatrix()フロー
```
[setFromRotationMatrix(m)]
        |
        v
[行列要素取得]
m11, m12, ... m33 = te[...]
        |
        v
[トレース計算]
trace = m11 + m22 + m33
        |
        v
[trace > 0?]
   Yes |         No
       v          |
[標準計算]        |
s = sqrt(trace+1) |
w = 0.25/s        |
       |          v
       |   [最大対角要素で分岐]
       |   m11 > m22 && m11 > m33?
       |   → X軸ベースで計算
       |   m22 > m33?
       |   → Y軸ベースで計算
       |   else
       |   → Z軸ベースで計算
       |          |
       v          v
[コールバック]<---'
        |
        v
[自身を返却]
```

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

### 4.1 推奨読解順序
1. **Quaternion.js** - クラス定義とプロパティ（1-45行目）
2. コンストラクタとgetters/setters（29-239行目）
3. 変換メソッド（301-461行目）
4. 乗算（694-710行目）
5. SLERP（719-770行目）

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

#### コンストラクタ（29-45行目）
```javascript
constructor( x = 0, y = 0, z = 0, w = 1 ) {
    this.isQuaternion = true;

    this._x = x;
    this._y = y;
    this._z = z;
    this._w = w;
}
```
**注**: プロパティは`_`プレフィックス付きで、setterで変更コールバックを呼ぶ

#### setFromAxisAngle()（382-397行目）
```javascript
setFromAxisAngle( axis, angle ) {
    const halfAngle = angle / 2, s = Math.sin( halfAngle );

    this._x = axis.x * s;
    this._y = axis.y * s;
    this._z = axis.z * s;
    this._w = Math.cos( halfAngle );

    this._onChangeCallback();

    return this;
}
```
軸角度表現からクォータニオンへの変換

#### setFromRotationMatrix()（405-461行目）
```javascript
setFromRotationMatrix( m ) {
    const te = m.elements,
        m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
        m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
        m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],

        trace = m11 + m22 + m33;

    if ( trace > 0 ) {
        const s = 0.5 / Math.sqrt( trace + 1.0 );
        this._w = 0.25 / s;
        this._x = ( m32 - m23 ) * s;
        this._y = ( m13 - m31 ) * s;
        this._z = ( m21 - m12 ) * s;
    } else if ( m11 > m22 && m11 > m33 ) {
        // ... X軸ベースの計算
    } else if ( m22 > m33 ) {
        // ... Y軸ベースの計算
    } else {
        // ... Z軸ベースの計算
    }

    this._onChangeCallback();
    return this;
}
```
回転行列からクォータニオンへの変換（数値安定性を考慮）

#### slerp()（719-770行目）
```javascript
slerp( qb, t ) {
    let x = qb._x, y = qb._y, z = qb._z, w = qb._w;
    let dot = this.dot( qb );

    if ( dot < 0 ) {
        x = -x; y = -y; z = -z; w = -w;
        dot = -dot;
    }

    let s = 1 - t;

    if ( dot < 0.9995 ) {
        // SLERP
        const theta = Math.acos( dot );
        const sin = Math.sin( theta );
        s = Math.sin( s * theta ) / sin;
        t = Math.sin( t * theta ) / sin;

        this._x = this._x * s + x * t;
        // ...
    } else {
        // ほぼ同じ回転の場合はLERP + 正規化
        this._x = this._x * s + x * t;
        // ...
        this.normalize();
    }

    return this;
}
```
球面線形補間（小さい角度では線形補間にフォールバック）

### 4.3 プログラム呼び出し階層図
```
Quaternion
├── 基本操作
│   ├── set() / clone() / copy()
│   ├── identity()
│   └── getters/setters (x, y, z, w)
├── 変換（From）
│   ├── setFromEuler()
│   ├── setFromAxisAngle()
│   ├── setFromRotationMatrix()
│   └── setFromUnitVectors()
├── クォータニオン演算
│   ├── multiply() → multiplyQuaternions()
│   ├── premultiply() → multiplyQuaternions()
│   ├── multiplyQuaternions()
│   ├── invert() → conjugate()
│   ├── conjugate()
│   ├── normalize()
│   └── dot()
├── 補間・回転
│   ├── slerp()
│   ├── slerpQuaternions() → copy() + slerp()
│   ├── angleTo() → dot() + acos()
│   ├── rotateTowards() → angleTo() + slerp()
│   └── random()
├── 長さ
│   ├── length() → lengthSq() + sqrt()
│   └── lengthSq()
├── 静的メソッド
│   ├── Quaternion.slerpFlat()
│   └── Quaternion.multiplyQuaternionsFlat()
└── その他
    ├── equals()
    ├── fromArray() / toArray()
    ├── fromBufferAttribute()
    └── toJSON()
```

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

## 5. 使用例

### 5.1 基本的な使用例
```javascript
// クォータニオンの作成
const q = new THREE.Quaternion();

// 軸角度から設定（Y軸回りに90度）
q.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);

// オイラー角から設定
q.setFromEuler(new THREE.Euler(0, Math.PI / 4, 0));
```

### 5.2 回転の合成
```javascript
const q1 = new THREE.Quaternion();
const q2 = new THREE.Quaternion();

q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
q2.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);

// 回転の合成（q1の後にq2を適用）
q1.multiply(q2);
```

### 5.3 球面線形補間（SLERP）
```javascript
const start = new THREE.Quaternion();
const end = new THREE.Quaternion();

start.setFromEuler(new THREE.Euler(0, 0, 0));
end.setFromEuler(new THREE.Euler(0, Math.PI, 0));

// 中間の回転を計算
const current = start.clone().slerp(end, 0.5);
```

### 5.4 2つのベクトル間の回転
```javascript
const q = new THREE.Quaternion();
const vFrom = new THREE.Vector3(0, 0, 1).normalize();
const vTo = new THREE.Vector3(1, 0, 0).normalize();

q.setFromUnitVectors(vFrom, vTo);
// qはvFromをvToに回転させるクォータニオン
```

### 5.5 段階的な回転
```javascript
const current = new THREE.Quaternion();
const target = new THREE.Quaternion();
target.setFromEuler(new THREE.Euler(0, Math.PI, 0));

// フレームごとに少しずつ回転
const step = 0.05;  // ラジアン
current.rotateTowards(target, step);
```

## 6. 備考

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

### 6.2 パフォーマンス考慮事項
- SLERP計算にはacos、sin、sqrtが必要で比較的高コスト
- 角度が小さい場合は線形補間にフォールバック（dot > 0.9995）
- 正規化は長さ計算が必要なので頻繁に行わない

### 6.3 注意事項
- 正規化されたクォータニオンのみが有効な回転を表す
- qと-qは同じ回転を表す（ダブルカバー）
- setFromUnitVectors()は正規化されたベクトルを前提とする
- _onChangeCallback()はObject3Dとの連携に使用される

### 6.4 数値安定性
setFromRotationMatrix()は、対角要素の大きさに応じて計算方法を分岐することで、数値的な安定性を確保している。
