# 機能設計書 4-Raycaster

## 概要

本ドキュメントは、Three.jsライブラリにおけるレイキャスティング機能を提供するRaycasterクラスの機能設計について記述する。Raycasterは、3D空間内でレイ（光線）を発射し、シーン内のオブジェクトとの交差判定を行う機能を提供する。主にマウスピッキング（クリックしたオブジェクトの特定）やコリジョン検出に使用される。

### 本機能の処理概要

Raycasterクラスは、原点と方向を持つレイ（光線）を定義し、シーン内の3Dオブジェクトとの交差点を効率的に検出する。交差結果には、交差点の座標、距離、オブジェクト参照、面情報、UV座標などの詳細情報が含まれる。

**業務上の目的・背景**：3Dアプリケーションでは、ユーザーがマウスやタッチでシーン内のオブジェクトを選択する機能が不可欠である。Raycasterは、2Dスクリーン座標を3D空間のレイに変換し、そのレイがどのオブジェクトと交差するかを判定することで、直感的なオブジェクト選択を実現する。ゲームでは弾道計算や視線判定、エディタではオブジェクト選択など、幅広い用途で活用される。

**機能の利用シーン**：
- マウスクリックで3Dオブジェクトを選択する場面（ピッキング）
- ホバー時にオブジェクトをハイライトする場面
- VR/ARコントローラーによるオブジェクト指示
- ゲームでの弾道計算やレーザー照準
- 地形との交差判定による高さ取得
- コリジョン検出の補助

**主要な処理内容**：
1. **レイ設定**: set()、setFromCamera()、setFromXRController()によるレイの原点・方向設定
2. **交差判定**: intersectObject()、intersectObjects()によるオブジェクトとの交差判定
3. **レイヤーフィルタ**: layersプロパティによる判定対象の絞り込み
4. **距離フィルタ**: near、farプロパティによる判定距離の制限
5. **結果ソート**: 交差結果を距離順にソート

**関連システム・外部連携**：
- Rayクラスによるレイの数学的表現
- Layersクラスによるオブジェクトフィルタリング
- Camera（PerspectiveCamera、OrthographicCamera）との連携
- WebXRControllerとの連携
- Object3Dのraycast()メソッドとの連携

**権限による制御**：特になし（ライブラリレベルの機能のため、アプリケーション側での権限管理は行わない）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Viewport | 主機能 | マウスピッキングによるオブジェクト選択 |

## 機能種別

オブジェクト判定 / 座標変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| origin | Vector3 | No | レイの原点 | コンストラクタで設定可能 |
| direction | Vector3 | No | レイの方向（正規化必須） | コンストラクタで設定可能 |
| near | number | No | 最小判定距離 | デフォルト: 0、負の値不可 |
| far | number | No | 最大判定距離 | デフォルト: Infinity、near以上 |
| camera | Camera | No | カメラ参照（ビュー依存オブジェクト用） | setFromCamera()で自動設定 |
| layers | Layers | No | レイヤーマスク | デフォルト: layer 0 |
| params | Object | No | オブジェクト種別ごとの設定 | threshold等 |

### 入力データソース

- setFromCamera(): マウス座標（NDC）とカメラから設定
- setFromXRController(): XRコントローラーから設定
- set(): 直接Vector3で設定
- アプリケーションコードからの直接設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| distance | number | レイ原点から交差点までの距離 |
| point | Vector3 | ワールド座標系での交差点 |
| face | Object | 交差した面の情報（a, b, c, normal, materialIndex） |
| faceIndex | number | 交差した面のインデックス |
| object | Object3D | 交差したオブジェクト |
| uv | Vector2 | 交差点のUV座標（テクスチャ座標） |
| uv1 | Vector2 | 第2UV座標（ライトマップ用等） |
| normal | Vector3 | 交差点の補間法線 |
| instanceId | number | InstancedMeshの場合のインスタンスID |

### 出力先

- intersectObject()/intersectObjects()の戻り値（配列）
- 引数として渡されたintersects配列への追加

## 処理フロー

### 処理シーケンス

```
1. Raycaster生成
   └─ Rayインスタンスの生成
   └─ near, far, layers, paramsの初期化

2. レイ設定
   └─ setFromCamera(coords, camera)
       └─ PerspectiveCameraの場合
           └─ origin = camera.matrixWorld.position
           └─ direction = unproject(coords) - origin
       └─ OrthographicCameraの場合
           └─ origin = unproject(coords)
           └─ direction = (0, 0, -1).transformDirection(camera.matrixWorld)

3. 交差判定
   └─ intersectObject(object, recursive, intersects)
       └─ intersect(object, this, intersects, recursive)
           └─ layers.test(raycaster.layers)
           └─ object.raycast(raycaster, intersects)
           └─ 再帰的に子オブジェクトを処理
       └─ intersects.sort(ascSort) // 距離順ソート

4. 結果取得
   └─ 交差点配列（距離順ソート済み）
```

### フローチャート

```mermaid
flowchart TD
    A[Raycaster生成] --> B{レイ設定方法}
    B -->|マウス| C[setFromCamera]
    B -->|XR| D[setFromXRController]
    B -->|直接| E[set]
    C --> F{カメラタイプ}
    F -->|Perspective| G[origin=カメラ位置, direction=unprojected]
    F -->|Orthographic| H[origin=unproject, direction=カメラ向き]
    D --> I[XRコントローラーから取得]
    E --> J[origin, direction直接設定]
    G --> K[交差判定]
    H --> K
    I --> K
    J --> K
    K --> L[intersectObject/Objects]
    L --> M{layers.test?}
    M -->|No| N[スキップ]
    M -->|Yes| O[object.raycast]
    O --> P{recursive?}
    P -->|Yes| Q[子オブジェクトを再帰処理]
    Q --> M
    P -->|No| R[結果をソート]
    N --> P
    R --> S[交差結果配列を返す]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 距離順ソート | 交差結果は常にレイ原点からの距離順にソート | intersectObject/Objects実行時 |
| BR-02 | レイヤーフィルタ | raycaster.layersとobject.layersで共通レイヤーがないとスキップ | 交差判定時 |
| BR-03 | near/farフィルタ | near以下、far以上の距離の交差点は無視 | 交差判定時 |
| BR-04 | 再帰オプション | recursiveがtrueの場合のみ子オブジェクトを処理 | intersectObject/Objects実行時 |
| BR-05 | 面の向き | デフォルトではレイに向いた面のみ検出、Material.sideで変更可能 | Meshの交差判定時 |

### 計算ロジック

**PerspectiveCameraからのレイ設定:**
```javascript
ray.origin.setFromMatrixPosition(camera.matrixWorld);
ray.direction.set(coords.x, coords.y, 0.5)
    .unproject(camera)
    .sub(ray.origin)
    .normalize();
```

**OrthographicCameraからのレイ設定:**
```javascript
ray.origin.set(coords.x, coords.y,
    (camera.near + camera.far) / (camera.near - camera.far))
    .unproject(camera);
ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld);
```

**交差結果のソート:**
```javascript
function ascSort(a, b) {
    return a.distance - b.distance;
}
```

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

### 操作別データベース影響一覧

該当なし（Raycasterはデータベースを使用しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | エラー | サポートされていないカメラタイプ | コンソールにエラー出力 |

### リトライ仕様

該当なし

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

該当なし（メモリ上のオブジェクト操作のみ）

## パフォーマンス要件

- 大規模シーン（多数のオブジェクト）でも高速に動作する必要がある
- 境界球/境界ボックスによる早期棄却を活用
- InstancedMeshでは効率的なインスタンス単位の判定
- LODオブジェクトでは適切な詳細度レベルでの判定

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

該当なし（クライアントサイドのグラフィックスライブラリ）

## 備考

- Line、Pointsなどのオブジェクトにはparams.Line.threshold、params.Points.thresholdで判定精度を設定可能
- Spriteはビュー依存オブジェクトのため、cameraプロパティの設定が必要
- raycast()メソッドがfalseを返すと子オブジェクトの判定もスキップされる

---

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

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

### 推奨読解順序

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

Raycasterが使用するレイの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Ray.js | `src/math/Ray.js` | レイの構造（origin, direction）と交差判定メソッド |
| 1-2 | Layers.js | `src/core/Layers.js` | レイヤーマスクの構造とtest()メソッド |

**読解のコツ**: Raycasterは内部でRayインスタンスを持ち、実際の交差計算はRayクラスおよび各Object3Dのraycast()メソッドが担当する。

#### Step 2: エントリーポイントを理解する

Raycasterクラスの基本構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Raycaster.js | `src/core/Raycaster.js` | コンストラクタでのプロパティ初期化 |

**主要処理フロー**:
1. **23-96行目**: コンストラクタ
2. **30行目**: Rayインスタンス生成
3. **38行目**: nearのデフォルト（0）
4. **46行目**: farのデフォルト（Infinity）
5. **56行目**: cameraの初期化（null）
6. **69行目**: Layersインスタンス生成
7. **88-94行目**: paramsの初期化（Mesh, Line, LOD, Points, Sprite）

#### Step 3: レイ設定メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Raycaster.js | `src/core/Raycaster.js` | set(), setFromCamera(), setFromXRController() |

**主要処理フロー**:
- **104-110行目**: set() - origin, directionの直接設定
- **119-139行目**: setFromCamera() - カメラタイプ別のレイ設定
- **147-156行目**: setFromXRController() - XRコントローラーからの設定

#### Step 4: 交差判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Raycaster.js | `src/core/Raycaster.js` | intersectObject(), intersectObjects(), intersect()関数 |

**主要処理フロー**:
- **194-202行目**: intersectObject() - 単一オブジェクトとの交差判定
- **214-226行目**: intersectObjects() - 複数オブジェクトとの交差判定
- **236-258行目**: intersect()関数 - 再帰的な交差判定の実装
- **230-234行目**: ascSort() - 距離順ソート関数

#### Step 5: オブジェクト側のraycast実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Mesh.js | `src/objects/Mesh.js` | Meshのraycast()メソッド |
| 5-2 | Line.js | `src/objects/Line.js` | Lineのraycast()メソッド |
| 5-3 | Points.js | `src/objects/Points.js` | Pointsのraycast()メソッド |

**読解のコツ**: Object3D.raycast()は空の実装。各レンダラブルオブジェクト（Mesh、Line、Points等）がオーバーライドして具体的な判定を実装。

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

```
Raycaster
    │
    ├─ constructor(origin, direction, near, far)
    │      ├─ new Ray(origin, direction)
    │      ├─ new Layers()
    │      └─ params初期化
    │
    ├─ setFromCamera(coords, camera)
    │      ├─ [PerspectiveCamera]
    │      │      ├─ ray.origin.setFromMatrixPosition(camera.matrixWorld)
    │      │      └─ ray.direction.set().unproject().sub().normalize()
    │      └─ [OrthographicCamera]
    │             ├─ ray.origin.set().unproject()
    │             └─ ray.direction.set().transformDirection()
    │
    ├─ intersectObject(object, recursive, intersects)
    │      ├─ intersect(object, this, intersects, recursive)
    │      │      ├─ object.layers.test(raycaster.layers)
    │      │      ├─ object.raycast(raycaster, intersects)
    │      │      │      └─ [Mesh] checkBufferGeometryIntersection()
    │      │      │      └─ [Line] ray.distanceSqToSegment()
    │      │      │      └─ [Points] ray.distanceSqToPoint()
    │      │      └─ [recursive] children.forEach(intersect)
    │      └─ intersects.sort(ascSort)
    │
    └─ intersectObjects(objects, recursive, intersects)
           ├─ objects.forEach(intersect)
           └─ intersects.sort(ascSort)
```

### データフロー図

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

マウス座標(NDC) ─────┐
                    │
Camera ─────────────┼─→ setFromCamera() ────→ ray.origin
                    │                         ray.direction
                                                   │
                                                   ↓
                                           ┌──────────────┐
Object3D[] ────────────────────────────────│intersectObject│
                                           │   /Objects   │
                                           └──────────────┘
                                                   │
                                                   ↓
                                           ┌──────────────┐
                                           │layers.test() │
                                           └──────────────┘
                                                   │
                                           ┌───────┴───────┐
                                           ↓               ↓
                                        [Pass]         [Fail]
                                           │               │
                                           ↓               ↓
                                    object.raycast()   Skip
                                           │
                                           ↓
                                    intersects.push({
                                      distance,
                                      point,
                                      face,
                                      object,
                                      uv, ...
                                    })
                                           │
                                           ↓
                                    ┌──────────────┐
                                    │ sort(ascSort)│
                                    └──────────────┘
                                           │
                                           ↓
                                    交差結果配列（距離順）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Raycaster.js | `src/core/Raycaster.js` | ソース | Raycasterクラス本体 |
| Ray.js | `src/math/Ray.js` | ソース | レイの数学的表現 |
| Layers.js | `src/core/Layers.js` | ソース | レイヤーマスク管理 |
| Matrix4.js | `src/math/Matrix4.js` | ソース | 座標変換用行列 |
| Vector3.js | `src/math/Vector3.js` | ソース | 原点・方向・交差点座標 |
| Vector2.js | `src/math/Vector2.js` | ソース | UV座標 |
| Mesh.js | `src/objects/Mesh.js` | ソース | Meshのraycast実装 |
| Line.js | `src/objects/Line.js` | ソース | Lineのraycast実装 |
| Points.js | `src/objects/Points.js` | ソース | Pointsのraycast実装 |
| Sprite.js | `src/objects/Sprite.js` | ソース | Spriteのraycast実装 |
