# 機能設計書: IK（逆運動学）

## 1. 機能概要

### 1.1 機能名
IK（逆運動学） - CCDIK、FABRIKによるインバースキネマティクス

### 1.2 機能説明
IK（Inverse Kinematics）は、目標位置にエンドエフェクタ（手先や足先などの末端ボーン）を到達させるために、ボーンチェーン全体の回転を逆算するシステムである。Godotでは主にFABRIK（Forward And Backward Reaching Inverse Kinematics）とCCDIK（Cyclic Coordinate Descent Inverse Kinematics）の2つのアルゴリズムを提供し、キャラクターの手足を特定の位置に向けたり、オブジェクトを掴んだりする動作を実現する。

### 1.3 関連画面
- 3Dビューポート（IKターゲット表示）
- インスペクターパネル（IKノードプロパティ）

## 2. 機能詳細

### 2.1 主要機能一覧

| 機能ID | 機能名 | 説明 |
|--------|--------|------|
| F35-01 | FABRIK | Forward And Backward Reaching IKアルゴリズム |
| F35-02 | CCDIK | Cyclic Coordinate Descent IKアルゴリズム |
| F35-03 | ターゲット追従 | 外部ノードをターゲットとして追従 |
| F35-04 | マグネット位置 | 中間ボーンの誘導位置指定 |
| F35-05 | 回転制約 | ボーンの回転範囲制限 |
| F35-06 | イテレーション制御 | 収束までの反復回数制限 |
| F35-07 | 2D IK | 2Dスケルトン用のIK処理 |

### 2.2 クラス構造

#### 3D IK
```
SkeletonModifier3D (基底クラス)
    │
    ├── SkeletonIK3D (FABRIKベース)
    │       └── FabrikInverseKinematic (内部実装)
    │               ├── Chain
    │               ├── ChainItem
    │               └── EndEffector
    │
    └── CCDIK3D (CCDIKベース)
            └── IterateIK3DSetting
                    └── IKModifier3DSolverInfo
```

#### 2D IK（Skeleton Modification）
```
SkeletonModificationStack2D
    │
    └── SkeletonModification2D (基底)
            ├── SkeletonModification2DCCDIK
            └── SkeletonModification2DFABRIK
```

### 2.3 主要プロパティ（SkeletonIK3D）

| プロパティ名 | 型 | デフォルト値 | 説明 |
|-------------|-----|-------------|------|
| root_bone | StringName | "" | IKチェーンのルートボーン |
| tip_bone | StringName | "" | IKチェーンの先端ボーン（エンドエフェクタ） |
| target_node | NodePath | "" | ターゲットノードへのパス |
| use_magnet | bool | false | マグネット位置使用フラグ |
| magnet | Vector3 | (0,0,0) | マグネット位置 |
| override_tip_basis | bool | true | 先端ボーンの回転をターゲットに合わせる |
| max_iterations | int | 10 | 最大反復回数 |
| min_distance | float | 0.01 | 収束判定閾値 |
| interpolation | float | 1.0 | アニメーションとのブレンド率 |

### 2.4 主要プロパティ（CCDIK3D）

| プロパティ名 | 型 | デフォルト値 | 説明 |
|-------------|-----|-------------|------|
| target | NodePath | "" | ターゲットノードへのパス |
| tip_bone | StringName | "" | 先端ボーン名 |
| joint_count | int | 0 | ジョイント数 |
| rotation_axis | int | ALL | 回転軸制約 |
| limitation | IKLimitation3D | null | 回転制限リソース |

### 2.5 主要メソッド（SkeletonIK3D）

| メソッド名 | 戻り値 | 説明 |
|-----------|--------|------|
| start(one_time) | void | IK処理を開始 |
| stop() | void | IK処理を停止 |
| is_running() | bool | IK処理が実行中か |
| get_parent_skeleton() | Skeleton3D | 親スケルトンを取得 |

### 2.6 FABRIKアルゴリズム概要

FABRIKは以下の2フェーズで動作する：

#### Backwardフェーズ（後方到達）
1. エンドエフェクタをターゲット位置に移動
2. 各ボーンを親方向に向かって順に、元の長さを維持しながら位置を更新

#### Forwardフェーズ（前方到達）
1. ルートボーンを元の位置に固定
2. 各ボーンを子方向に向かって順に、元の長さを維持しながら位置を更新

この2フェーズを収束するまで繰り返す。

### 2.7 CCDIKアルゴリズム概要

CCDIKは以下の手順で動作する：

1. エンドエフェクタから順にルート方向へ各ボーンを処理
2. 各ボーンについて、エンドエフェクタがターゲットに近づくように回転
3. 回転制約がある場合は適用
4. 収束するまで繰り返し

## 3. 処理フロー

### 3.1 FABRIK処理フロー

```
[SkeletonIK3D::start() または毎フレーム処理]
      │
      ▼
[FabrikInverseKinematic::solve()]
      │
      ├─ [_update_chain()] - チェーン位置の更新
      │
      ├─ [make_goal()] - ターゲット位置の計算
      │
      ├─ マグネット使用？
      │       ├─ Yes → [solve_simple(task, true)] - マグネット解決
      │       └─ No  → スキップ
      │
      └─ [solve_simple(task, false)] - メイン解決
              │
              └─ [while (distance > threshold && iterations < max)]
                      │
                      ├─ [solve_simple_backwards()] - 後方到達
                      │       │
                      │       └─ エンドエフェクタ → ルート方向に処理
                      │               goal = current_pos + (direction * length)
                      │
                      └─ [solve_simple_forwards()] - 前方到達
                              │
                              └─ ルート → エンドエフェクタ方向に処理
                                      origin = current_pos + (direction * length)
```

### 3.2 CCDIK処理フロー

```
[CCDIK3D::_solve_iteration()]
      │
      ▼
[Backwardsループ: ancestor = joint_size-1 → 0]
      │
      └─ [Forwardsループ: i = ancestor → joint_size]
              │
              ├─ [current_head = chain[HEAD]]
              │
              ├─ [head_to_effector = effector - current_head]
              │
              ├─ [head_to_destination = target - current_head]
              │
              ├─ [to_rot = Quaternion(head_to_effector, head_to_destination)]
              │
              ├─ [update_chain_coordinate_fw()] - チェーン座標更新
              │
              ├─ 回転軸制約あり？
              │       └─ [get_projected_rotation()] - 軸投影
              │
              └─ 回転制限あり？
                      └─ [get_limited_rotation()] - 制限適用
```

### 3.3 データフロー図

```
[ターゲットノード (Node3D)]
        │
        │ global_transform
        ▼
┌───────────────────────────────────────┐
│         SkeletonIK3D / CCDIK3D        │
│                                        │
│  ┌────────────────────────────────┐   │
│  │          IK Task/Chain          │   │
│  │  - root_bone                    │   │
│  │  - tip_bone (end effector)      │   │
│  │  - goal_transform               │   │
│  └────────────────────────────────┘   │
│              │                        │
│              ▼                        │
│  ┌────────────────────────────────┐   │
│  │      IK Solver (FABRIK/CCDIK)   │   │
│  │  - solve_simple_backwards()     │   │
│  │  - solve_simple_forwards()      │   │
│  │  - _solve_iteration()           │   │
│  └────────────────────────────────┘   │
└───────────────────────────────────────┘
        │
        │ set_bone_global_pose()
        ▼
┌───────────────────────────────────────┐
│            Skeleton3D                  │
│  - ボーンポーズの更新                  │
│  - スキニング処理                      │
└───────────────────────────────────────┘
```

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

### 4.1 推奨読解順序

1. **データ構造の理解**
   - `scene/3d/skeleton_ik_3d.h`: SkeletonIK3Dクラス定義
   - `scene/3d/skeleton_ik_3d.cpp` (33-48行目): ChainItem構造体

2. **FABRIKアルゴリズム**
   - `skeleton_ik_3d.cpp` **build_chain()** (51-124行目): チェーン構築
   - `skeleton_ik_3d.cpp` **solve_simple_backwards()** (141-171行目): 後方到達
   - `skeleton_ik_3d.cpp` **solve_simple_forwards()** (173-207行目): 前方到達

3. **CCDIKアルゴリズム**
   - `scene/3d/ccd_ik_3d.cpp` **_solve_iteration()** (33-71行目): 反復解法

### 4.2 重要な処理ポイント

#### FABRIK後方到達（skeleton_ik_3d.cpp: 141-171行目）
```cpp
void FabrikInverseKinematic::solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet) {
    Vector3 goal = r_chain.tips[0].end_effector->goal_transform.origin;
    ChainItem *sub_chain_tip = r_chain.tips[0].chain_item;

    while (sub_chain_tip) {
        sub_chain_tip->current_pos = goal;
        if (sub_chain_tip->parent_item) {
            const Vector3 look_parent = (sub_chain_tip->parent_item->current_pos -
                                         sub_chain_tip->current_pos).normalized();
            goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length);
        }
        sub_chain_tip = sub_chain_tip->parent_item;
    }
}
```

#### CCDIK回転計算（ccd_ik_3d.cpp: 54-61行目）
```cpp
Quaternion to_rot = Quaternion(head_to_effector.normalized(),
                               head_to_destination.normalized());
Vector3 to_tail = p_setting->chain[TAIL] - current_head;
p_setting->update_chain_coordinate_fw(p_skeleton, TAIL,
                                      current_head + to_rot.xform(to_tail));
```

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

```
SkeletonIK3D::_notification(NOTIFICATION_INTERNAL_PROCESS)
    │
    └── SkeletonIK3D::_solve_chain()
            │
            └── FabrikInverseKinematic::solve(task, override_tip_basis, use_magnet, magnet)
                    │
                    ├── _update_chain(skeleton, &chain.chain_root)
                    │
                    ├── make_goal(task, skeleton.inverse())
                    │
                    ├── [if use_magnet]
                    │       └── solve_simple(task, true, origin_pos)  // マグネット解決
                    │
                    └── solve_simple(task, false, origin_pos)  // メイン解決
                            │
                            └── while (収束条件)
                                    │
                                    ├── solve_simple_backwards(chain, p_solve_magnet)
                                    │
                                    └── solve_simple_forwards(chain, p_solve_magnet, origin)

                    // 結果適用
                    └── skeleton->set_bone_global_pose(bone, new_pose)

CCDIK3D::_process_modification()
    │
    └── _solve_iteration(delta, skeleton, setting, destination)
            │
            └── [Backwards: ancestor = joint_size-1 → 0]
                    │
                    └── [Forwards: i = ancestor → joint_size]
                            │
                            ├── Quaternion計算
                            ├── update_chain_coordinate_fw()
                            ├── get_projected_rotation() [if rotation_axis制約]
                            └── get_limited_rotation() [if limitation]
```

### 4.4 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| scene/3d/skeleton_ik_3d.h | ヘッダー | SkeletonIK3D定義 |
| scene/3d/skeleton_ik_3d.cpp | 実装 | FABRIKアルゴリズム実装 |
| scene/3d/ccd_ik_3d.h | ヘッダー | CCDIK3D定義 |
| scene/3d/ccd_ik_3d.cpp | 実装 | CCDIKアルゴリズム実装 |
| scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h | ヘッダー | 2D CCDIK定義 |
| scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h | ヘッダー | 2D FABRIK定義 |
| editor/scene/3d/skeleton_ik_3d_editor_plugin.h | ヘッダー | エディタプラグイン |

## 5. 設計上の考慮事項

### 5.1 パフォーマンス
- 最大反復回数（max_iterations）で計算量を制限
- 収束判定閾値（min_distance）で早期終了
- チェーン構造のキャッシュによる再計算回避

### 5.2 精度と安定性
- FABRIKは直感的で安定した結果を生成
- CCDIKは局所最適解に陥りやすいが高速
- 回転制約により不自然なポーズを防止

### 5.3 使い分けの指針
- **FABRIK**: 一般的なIK用途、滑らかな動き
- **CCDIK**: 高速性が必要な場合、多関節チェーン

## 6. 使用例

### 6.1 手を目標に向ける（FABRIK）
```gdscript
@onready var skeleton = $Skeleton3D
@onready var ik = $Skeleton3D/SkeletonIK3D
@onready var target = $Target

func _ready():
    ik.root_bone = "UpperArm"
    ik.tip_bone = "Hand"
    ik.target_node = target.get_path()
    ik.start()

func _process(delta):
    # ターゲットを移動させるとIKが追従
    target.global_position = get_mouse_world_position()
```

### 6.2 マグネット位置の使用
```gdscript
func _ready():
    ik.use_magnet = true
    # 肘の位置を体の外側に誘導
    ik.magnet = Vector3(0.5, 0, -0.3)
```

### 6.3 アニメーションとのブレンド
```gdscript
func blend_ik_with_animation(blend_amount: float):
    # 0.0 = 完全にアニメーション
    # 1.0 = 完全にIK
    ik.interpolation = blend_amount
```

### 6.4 ワンショットIK
```gdscript
func reach_for_object(target_pos: Vector3):
    # ターゲット位置を設定
    $Target.global_position = target_pos

    # 1回だけIKを解決
    ik.start(true)  # one_time = true
```

## 7. 関連機能
- [No.34 スケルトン/ボーン](./34-スケルトン_ボーン.md) - 骨格構造の管理
- [No.31 AnimationPlayer](./31-AnimationPlayer.md) - IKとのブレンド用
