# 機能設計書 116-PropertyMixer

## 概要

本ドキュメントは、Three.jsにおける`PropertyMixer`クラスの機能設計を記述する。PropertyMixerは、バッファリングされたシーングラフプロパティを管理し、複数のアニメーションからの値を重み付け累積してオブジェクトに適用する内部クラスである。

### 本機能の処理概要

PropertyMixerは、アニメーションシステムの中核コンポーネントとして、複数のAnimationActionからの出力値を適切にブレンドし、最終的なプロパティ値を算出する。入力バッファ、累積バッファ、オリジナル値バッファ、加算累積バッファを管理し、ノーマルブレンドと加算ブレンドの両方をサポートする。

**業務上の目的・背景**：ゲームやアニメーションでは、複数のアニメーションを同時に再生してブレンドする必要がある（例：歩きながら手を振る、上半身と下半身の独立した動き等）。PropertyMixerは、各アニメーションの重みに応じた値の累積と、プロパティタイプに応じた適切な補間（線形補間、球面線形補間等）を提供する。

**機能の利用シーン**：
- 複数アニメーションのブレンド（歩行+発射アニメーション等）
- 加算アニメーション（ベースポーズ+表情変化等）
- クロスフェード時の中間状態計算
- 部分的なアニメーション適用（重み < 1）

**主要な処理内容**：
1. プロパティタイプに応じたミックス関数の選択（lerp, slerp, select）
2. 入力値の累積（accumulate, accumulateAdditive）
3. 累積結果のプロパティへの適用（apply）
4. オリジナル値の保存と復元

**関連システム・外部連携**：AnimationMixer、AnimationAction、PropertyBindingと連携し、キーフレーム補間結果をオブジェクトプロパティに反映する。

**権限による制御**：特になし（ライブラリ内部機能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 該当なし | - | - | PropertyMixerはライブラリ内部で使用されるクラスであり、直接的なUI関連はない |

## 機能種別

値累積 / ブレンド処理 / バッファ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| binding | PropertyBinding | Yes | プロパティバインディング | - |
| typeName | string | Yes | 値の型名（quaternion, bool, string等） | - |
| valueSize | number | Yes | 値のサイズ | - |

### 入力データソース

- Interpolantからの補間結果（bufferの先頭領域）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| binding | PropertyBinding | プロパティバインディング |
| valueSize | number | 値サイズ |
| buffer | Float64Array\|Array | バッファ（incoming, accu0, accu1, orig, add, [work]） |
| cumulativeWeight | number | ノーマルブレンド累積重み |
| cumulativeWeightAdditive | number | 加算ブレンド累積重み |

### 出力先

- PropertyBindingを介してオブジェクトプロパティに反映

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ
   └─ typeNameに応じたミックス関数の選択
   └─ バッファの割り当て（quaternionは6領域、それ以外は5領域）

2. saveOriginalState() - オリジナル値の保存
   └─ binding.getValue()でオリジナル値を取得
   └─ accu0, accu1をオリジナル値で初期化
   └─ add領域を単位元で初期化

3. accumulate() - ノーマルブレンド累積
   └─ 累積重み0なら入力値をそのままコピー
   └─ 累積重み>0ならミックス関数で補間

4. accumulateAdditive() - 加算ブレンド累積
   └─ 累積重み0なら単位元を設定
   └─ 加算ミックス関数で累積

5. apply() - プロパティへの適用
   └─ 累積重み<1ならオリジナル値をブレンド
   └─ 加算累積があれば加算
   └─ 値が変化した場合のみbinding.setValue()
```

### フローチャート

```mermaid
flowchart TD
    A[PropertyMixer生成] --> B{typeName}
    B -->|quaternion| C[slerp/slerpAdditive選択]
    B -->|bool/string| D[select選択]
    B -->|その他| E[lerp/lerpAdditive選択]
    C --> F[バッファ6領域確保]
    D --> G[バッファ5領域確保 Array]
    E --> H[バッファ5領域確保 Float64Array]

    I[フレーム更新] --> J[accumulate呼び出し]
    J --> K{cumulativeWeight == 0?}
    K -->|Yes| L[incoming値をaccuにコピー]
    K -->|No| M[ミックス関数で補間]
    L --> N[apply呼び出し]
    M --> N
    N --> O{weight < 1?}
    O -->|Yes| P[オリジナル値をブレンド]
    O -->|No| Q[そのまま]
    P --> R{加算累積あり?}
    Q --> R
    R -->|Yes| S[加算値を追加]
    R -->|No| T[binding.setValue]
    S --> T
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-116-01 | Quaternion補間 | quaternion型はslerpとmultiplyで処理 | typeName=='quaternion' |
| BR-116-02 | 離散型処理 | bool/string型はselectで処理（t>=0.5で切り替え） | typeName=='bool'または'string' |
| BR-116-03 | 変化検出 | 前フレームと値が同じなら適用をスキップ | apply実行時 |
| BR-116-04 | 加算単位元 | 数値は0、quaternionは(0,0,0,1)が単位元 | 加算初期化時 |

### 計算ロジック

**ノーマルブレンド累積**:
```javascript
if (cumulativeWeight === 0) {
    accu = incoming;
    cumulativeWeight = weight;
} else {
    cumulativeWeight += weight;
    mix = weight / cumulativeWeight;
    accu = mixFunction(accu, incoming, mix);
}
```

**加算ブレンド累積**:
```javascript
add += incoming * weight;
```

**最終適用**（weight < 1の場合）:
```javascript
accu = mixFunction(accu, original, 1 - weight);
accu += add; // 加算累積がある場合
```

## データベース操作仕様

該当なし（メモリ内データ構造）

## エラー処理

### エラーケース一覧

該当なし（内部クラスのため）

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- Float64Arrayによる高精度計算
- 変化検出による不要な書き込みの回避
- インプレース操作によるメモリ効率

## セキュリティ考慮事項

特になし

## 備考

- バッファレイアウト: [incoming | accu0 | accu1 | orig | add | (work)]
- accu0とaccu1はフレーム間で交互に使用して変化を検出
- workはquaternion型のみ使用（乗算の中間結果用）

---

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

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

PropertyMixerのバッファレイアウトと初期化を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PropertyMixer.js | `src/animation/PropertyMixer.js` | コンストラクタ（6-122行目） |

**読解のコツ**: バッファレイアウト
- `[0, valueSize)`: incoming - インターポラントからの入力値
- `[valueSize, valueSize*2)`: accu0 - 累積バッファ0
- `[valueSize*2, valueSize*3)`: accu1 - 累積バッファ1
- `[valueSize*3, valueSize*4)`: orig - オリジナル値
- `[valueSize*4, valueSize*5)`: add - 加算累積
- `[valueSize*5, valueSize*6)`: work - quaternion用作業領域

typeNameに応じて:
- `quaternion`: slerp/slerpAdditive、Float64Array(6領域)
- `bool/string`: select、Array(5領域)
- その他: lerp/lerpAdditive、Float64Array(5領域)

#### Step 2: 累積メソッドを理解する

accumulate/accumulateAdditiveの動作を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PropertyMixer.js | `src/animation/PropertyMixer.js` | accumulate()（130-165行目） |
| 2-2 | PropertyMixer.js | `src/animation/PropertyMixer.js` | accumulateAdditive()（172-191行目） |

**主要処理フロー**:
- **130-165行目**: accumulate() - 累積重み0ならコピー、>0なら補間
- **172-191行目**: accumulateAdditive() - 単位元初期化後、加算ミックス

#### Step 3: 適用メソッドを理解する

apply()の動作と変化検出を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PropertyMixer.js | `src/animation/PropertyMixer.js` | apply()（198-244行目） |
| 3-2 | PropertyMixer.js | `src/animation/PropertyMixer.js` | saveOriginalState()（250-274行目） |
| 3-3 | PropertyMixer.js | `src/animation/PropertyMixer.js` | restoreOriginalState()（279-284行目） |

**主要処理フロー**:
- **198-244行目**: apply() - 累積重みによるブレンド、変化検出、setValue呼び出し
- **250-274行目**: saveOriginalState() - オリジナル値取得、累積バッファ初期化
- **279-284行目**: restoreOriginalState() - オリジナル値の復元

#### Step 4: ミックス関数を理解する

各タイプのミックス関数の実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PropertyMixer.js | `src/animation/PropertyMixer.js` | _select()（324-336行目） |
| 4-2 | PropertyMixer.js | `src/animation/PropertyMixer.js` | _slerp()（338-342行目） |
| 4-3 | PropertyMixer.js | `src/animation/PropertyMixer.js` | _slerpAdditive()（344-354行目） |
| 4-4 | PropertyMixer.js | `src/animation/PropertyMixer.js` | _lerp()（356-368行目） |
| 4-5 | PropertyMixer.js | `src/animation/PropertyMixer.js` | _lerpAdditive()（370-380行目） |

**主要処理フロー**:
- **324-336行目**: _select - t >= 0.5で値を選択（離散補間）
- **338-342行目**: _slerp - Quaternion.slerpFlatを使用
- **344-354行目**: _slerpAdditive - multiplyQuaternionsFlat + slerp
- **356-368行目**: _lerp - 線形補間 s = 1 - t
- **370-380行目**: _lerpAdditive - 加算: dst += src * t

### プログラム呼び出し階層図

```
PropertyMixer
    │
    ├─ constructor()
    │      └─ typeName分岐
    │             ├─ 'quaternion' → slerp/slerpAdditive, Float64Array(6)
    │             ├─ 'bool'/'string' → select, Array(5)
    │             └─ default → lerp/lerpAdditive, Float64Array(5)
    │
    ├─ saveOriginalState()
    │      ├─ binding.getValue()
    │      ├─ accu0/accu1 = orig
    │      └─ _setIdentity()
    │
    ├─ accumulate()
    │      ├─ cumulativeWeight == 0 → コピー
    │      └─ cumulativeWeight > 0 → _mixBufferRegion()
    │
    ├─ accumulateAdditive()
    │      ├─ cumulativeWeightAdditive == 0 → _setIdentity()
    │      └─ _mixBufferRegionAdditive()
    │
    ├─ apply()
    │      ├─ weight < 1 → _mixBufferRegion(orig)
    │      ├─ weightAdditive > 0 → _mixBufferRegionAdditive(add)
    │      └─ 変化検出 → binding.setValue()
    │
    ├─ restoreOriginalState()
    │      └─ binding.setValue(orig)
    │
    └─ ミックス関数 [内部]
           ├─ _select()
           ├─ _slerp() → Quaternion.slerpFlat
           ├─ _slerpAdditive() → multiplyQuaternionsFlat + slerpFlat
           ├─ _lerp()
           └─ _lerpAdditive()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

Interpolant結果 ────────▶ buffer[incoming]
                                │
                                ▼
accumulate(weight) ────────────▶ buffer[accu] に累積
                                │
                                ▼
apply() ───────────────────────▶ 最終値計算
                                │
                                ├─ weight < 1 → orig とブレンド
                                ├─ additive → add を加算
                                │
                                ▼
                         変化検出
                                │
                                ▼
                         binding.setValue()
                                │
                                ▼
                         Object3D プロパティ更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PropertyMixer.js | `src/animation/PropertyMixer.js` | ソース | メインクラス定義 |
| PropertyBinding.js | `src/animation/PropertyBinding.js` | ソース | プロパティへのアクセス |
| Quaternion.js | `src/math/Quaternion.js` | ソース | slerpFlat, multiplyQuaternionsFlat |
| AnimationMixer.js | `src/animation/AnimationMixer.js` | ソース | PropertyMixerの管理 |
| AnimationAction.js | `src/animation/AnimationAction.js` | ソース | accumulate呼び出し元 |
