# 機能設計書 117-AnimationUtils

## 概要

本ドキュメントは、Three.jsにおける`AnimationUtils`クラスの機能設計を記述する。AnimationUtilsは、アニメーション関連のユーティリティ関数群を提供する静的クラスであり、配列変換、キーフレームの並べ替え、クリップの部分抽出、加算アニメーション変換などの機能を持つ。

### 本機能の処理概要

AnimationUtilsは、アニメーションデータの操作に必要な各種ユーティリティ機能を提供するヘルパークラスである。キーフレームトラックやアニメーションクリップの作成、編集、変換をサポートする静的メソッド群を提供する。

**業務上の目的・背景**：アニメーションシステムの様々な場面で、配列のソート、型変換、クリップの部分抽出、加算フォーマットへの変換などが必要となる。AnimationUtilsはこれらの共通処理を集約し、コードの再利用性と保守性を向上させる。

**機能の利用シーン**：
- AnimationClipの部分抽出（subclip）による特定フレーム範囲のアニメーション切り出し
- 加算アニメーションの作成（makeClipAdditive）
- キーフレームデータの型変換と並べ替え
- JSONキーフレームデータのフラット化

**主要な処理内容**：
1. 配列の型変換（convertArray）
2. キーフレームの時間順序でのソート（getKeyframeOrder, sortedArray）
3. JSONキーフレームデータのフラット化（flattenJSON）
4. アニメーションクリップの部分抽出（subclip）
5. 加算アニメーションフォーマットへの変換（makeClipAdditive）

**関連システム・外部連携**：AnimationClip、KeyframeTrack、各種ローダーから呼び出される。

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

## 関連画面

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

## 機能種別

ユーティリティ / データ変換 / アニメーション処理

## 入力仕様

### 主要メソッドのパラメータ

#### convertArray
| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| array | TypedArray\|Array | Yes | 変換元配列 |
| type | TypedArray.constructor | Yes | 変換先の型コンストラクタ |

#### subclip
| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| sourceClip | AnimationClip | Yes | 元のクリップ |
| name | string | Yes | 新しいクリップ名 |
| startFrame | number | Yes | 開始フレーム |
| endFrame | number | Yes | 終了フレーム |
| fps | number | No | FPS（デフォルト: 30） |

#### makeClipAdditive
| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| targetClip | AnimationClip | Yes | 加算化するクリップ |
| referenceFrame | number | No | 参照フレーム（デフォルト: 0） |
| referenceClip | AnimationClip | No | 参照クリップ（デフォルト: targetClip） |
| fps | number | No | FPS（デフォルト: 30） |

### 入力データソース

- AnimationClipインスタンス
- KeyframeTrackのtimes/values配列
- JSONアニメーションデータ

## 出力仕様

### 出力データ

| メソッド | 戻り値 | 説明 |
|---------|--------|------|
| convertArray | TypedArray | 変換された配列 |
| isTypedArray | boolean | TypedArrayかどうか |
| getKeyframeOrder | Array<number> | ソート順インデックス |
| sortedArray | Array<number> | ソート済み配列 |
| subclip | AnimationClip | 部分クリップ |
| makeClipAdditive | AnimationClip | 加算クリップ |

### 出力先

- AnimationClip、KeyframeTrack等のアニメーションオブジェクト

## 処理フロー

### 処理シーケンス

```
1. convertArray() - 配列型変換
   └─ 同じ型なら元の配列を返す
   └─ TypedArrayならnew type(array)
   └─ Arrayなら Array.prototype.slice.call(array)

2. subclip() - クリップ部分抽出
   └─ sourceClipをclone()
   └─ 各トラックでstartFrame-endFrame範囲のキーを抽出
   └─ 最小開始時間を計算してシフト
   └─ resetDuration()

3. makeClipAdditive() - 加算フォーマット変換
   └─ 参照フレームでの値を計算
   └─ quaternionは共役を使用
   └─ 全キーフレームから参照値を減算
   └─ blendModeをAdditiveAnimationBlendModeに設定
```

### フローチャート

```mermaid
flowchart TD
    A[subclip呼び出し] --> B[sourceClipをclone]
    B --> C[各トラックを処理]
    C --> D{フレーム範囲内?}
    D -->|Yes| E[times/valuesに追加]
    D -->|No| F[スキップ]
    E --> G[次のキーフレーム]
    F --> G
    G --> H{全キー処理済?}
    H -->|No| D
    H -->|Yes| I[最小開始時間計算]
    I --> J[全トラックをシフト]
    J --> K[resetDuration]
    K --> L[新クリップ返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-117-01 | FPS検証 | fps <= 0の場合、30をデフォルトとして使用 | makeClipAdditive実行時 |
| BR-117-02 | 非数値トラックスキップ | bool/stringトラックはmakeClipAdditiveでスキップ | makeClipAdditive実行時 |
| BR-117-03 | 空トラック除外 | subclipでキーフレームが空になったトラックは除外 | subclip実行時 |

### 計算ロジック

**フレームから時間への変換**:
```javascript
time = frame / fps
```

**加算値計算**（数値）:
```javascript
additiveValue = originalValue - referenceValue
```

**加算値計算**（quaternion）:
```javascript
referenceQuat.conjugate()
additiveQuat = referenceQuat * originalQuat
```

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

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

## エラー処理

### エラーケース一覧

該当なし（ユーティリティ関数のため）

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- TypedArray変換による効率的なメモリ使用
- インプレース操作を避け、元データを保護

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

特になし

## 備考

- AnimationUtilsはクラスとして定義されているが、全てのメソッドは静的
- 関数スタイルでのエクスポートも提供（convertArray, getKeyframeOrder等）

---

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

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

### 推奨読解順序

#### Step 1: 基本ユーティリティを理解する

配列変換と型チェック関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AnimationUtils.js | `src/animation/AnimationUtils.js` | convertArray()（12-24行目） |
| 1-2 | AnimationUtils.js | `src/animation/AnimationUtils.js` | isTypedArray()インポート |

**読解のコツ**:
- convertArray: 同じ型ならそのまま、TypedArrayならnew type()、そうでなければslice()
- isTypedArrayはutils.jsからインポートされている

#### Step 2: キーフレーム操作を理解する

キーフレームのソートとフラット化を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AnimationUtils.js | `src/animation/AnimationUtils.js` | getKeyframeOrder()（32-48行目） |
| 2-2 | AnimationUtils.js | `src/animation/AnimationUtils.js` | sortedArray()（58-77行目） |
| 2-3 | AnimationUtils.js | `src/animation/AnimationUtils.js` | flattenJSON()（87-159行目） |

**主要処理フロー**:
- **32-48行目**: getKeyframeOrder() - 時間でソートしたインデックス配列を返す
- **58-77行目**: sortedArray() - orderに従って値をソート
- **87-159行目**: flattenJSON() - AOS形式のJSONをtimes/values配列に展開

#### Step 3: クリップ操作を理解する

subclipとmakeClipAdditiveを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AnimationUtils.js | `src/animation/AnimationUtils.js` | subclip()（171-240行目） |
| 3-2 | AnimationUtils.js | `src/animation/AnimationUtils.js` | makeClipAdditive()（251-372行目） |

**主要処理フロー**:
- **171-240行目**: subclip() - フレーム範囲でクリップを切り出し、開始時間を0にシフト
- **251-372行目**: makeClipAdditive() - 参照フレームの値を減算して加算形式に変換

#### Step 4: クラスインターフェースを理解する

AnimationUtilsクラスの静的メソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AnimationUtils.js | `src/animation/AnimationUtils.js` | AnimationUtilsクラス（379-484行目） |

**主要処理フロー**:
- 全メソッドが静的メソッドとしてクラスでラップ
- 関数とクラスメソッド両方でエクスポート

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

```
AnimationUtils
    │
    ├─ convertArray(array, type)
    │      ├─ 同じ型 → そのまま返す
    │      ├─ TypedArray → new type(array)
    │      └─ Array → Array.prototype.slice.call
    │
    ├─ isTypedArray(object) [utils.jsから]
    │
    ├─ getKeyframeOrder(times)
    │      └─ ソートインデックス配列を返す
    │
    ├─ sortedArray(values, stride, order)
    │      └─ orderに従ってvaluesを並べ替え
    │
    ├─ flattenJSON(jsonKeys, times, values, valuePropertyName)
    │      ├─ Array形式 → 展開
    │      ├─ toArray()あり → toArrayで展開
    │      └─ プリミティブ → 直接追加
    │
    ├─ subclip(sourceClip, name, startFrame, endFrame, fps)
    │      ├─ sourceClip.clone()
    │      ├─ 範囲内キーフレーム抽出
    │      ├─ track.shift(-minStartTime)
    │      └─ clip.resetDuration()
    │
    └─ makeClipAdditive(targetClip, referenceFrame, referenceClip, fps)
           ├─ 参照フレームの値を取得
           │      ├─ 先頭/末尾キーフレーム
           │      └─ interpolant.evaluate()
           ├─ quaternion → conjugate
           ├─ 全キーから参照値を減算
           └─ blendMode = AdditiveAnimationBlendMode
```

### データフロー図

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

sourceClip ─────────────▶ subclip() ───────────────────▶ 部分クリップ
startFrame, endFrame          │
                              ▼
                        clone → フレーム抽出 → shift

targetClip ─────────────▶ makeClipAdditive() ──────────▶ 加算クリップ
referenceFrame               │
                              ▼
                        参照値計算 → 減算 → blendMode設定

times[] ────────────────▶ getKeyframeOrder() ──────────▶ インデックス配列
                              │
                              ▼
                        ソートインデックス生成

values[], order ────────▶ sortedArray() ───────────────▶ ソート済み配列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AnimationUtils.js | `src/animation/AnimationUtils.js` | ソース | メインユーティリティクラス |
| AnimationClip.js | `src/animation/AnimationClip.js` | ソース | AnimationUtilsを使用 |
| KeyframeTrack.js | `src/animation/KeyframeTrack.js` | ソース | AnimationUtilsを使用 |
| Quaternion.js | `src/math/Quaternion.js` | ソース | makeClipAdditiveで使用 |
| constants.js | `src/constants.js` | ソース | AdditiveAnimationBlendMode |
| utils.js | `src/utils.js` | ソース | isTypedArray |
