# 通知設計書 6-pinchend

## 概要

本ドキュメントは、three.jsライブラリにおける`pinchend`イベント通知の設計について記載する。`pinchend`イベントは、WebXRハンドトラッキングにおいてピンチジェスチャーが終了した際に発火するイベントである。

### 本通知の処理概要

`pinchend`イベントは、WebXR APIのハンドトラッキング機能を使用したアプリケーションにおいて、ユーザーの親指と人差し指によるピンチジェスチャーの終了を検知するためのイベントである。

**業務上の目的・背景**：ピンチジェスチャーの終了は、オブジェクトのドロップ、UIボタンのリリース、ドラッグ操作の完了等を示す重要なシグナルである。`pinchend`イベントにより、アプリケーションはピンチ操作の終了を検知し、進行中のインタラクションを適切に完了させることができる。

**通知の送信タイミング**：XRフレームごとの更新処理で、ピンチ中（inputState.pinching === true）かつ親指と人差し指の距離が閾値（0.02m + 0.005m = 0.025m）を超えた際に発火される。

**通知の受信者**：`pinchend`イベントの受信者は、XRコントローラーの各座標空間（targetRay、grip、hand）を表すGroupオブジェクトに登録されたリスナーである。

**通知内容の概要**：イベントオブジェクトには`type: 'pinchend'`、`handedness`（左右の手を示す）、`target`（WebXRController参照）が含まれる。

**期待されるアクション**：受信者は、ドラッグ中のオブジェクトをドロップ、UI要素のディアクティベート、選択操作の確定等を実行する。

## 通知種別

アプリ内イベント通知（EventDispatcherパターン）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | 無 |

### 送信先決定ロジック

WebXRControllerクラスの`dispatchEvent()`メソッドにより、targetRay、grip、handの3つのGroupオブジェクトに対して順次イベントが送信される。

## 通知テンプレート

### イベントオブジェクト形式

| 項目 | 内容 |
|-----|------|
| type | 'pinchend' |
| handedness | 'left' または 'right' |
| target | WebXRControllerインスタンス |

### イベントオブジェクト例

```javascript
{
  type: 'pinchend',
  handedness: 'right',  // または 'left'
  target: <WebXRController>
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | イベントタイプ | 固定値 'pinchend' | Yes |
| handedness | 左右の手識別 | inputSource.handedness | Yes |
| target | コントローラー参照 | this（WebXRController） | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| フレーム更新 | WebXRController.update() | 距離が閾値超かつ以前はpinching=true | ピンチジェスチャー終了時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| hand.inputState.pinching === false | ピンチ中でない場合 |
| distance <= distanceToPinch + threshold | 指間距離が閾値以下の場合（0.025m以下） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[update処理 - handモード] --> B[indexTip/thumbTipの位置取得]
    B --> C[distance = 指間距離計算]
    C --> D{hand.inputState.pinching?}
    D -->|false| E[pinchstart判定へ]
    D -->|true| F{distance > 0.025?}
    F -->|No| G[処理スキップ]
    F -->|Yes| H[hand.inputState.pinching = false]
    H --> I[dispatchEvent type: pinchend]
    I --> J[終了]
```

## データベース参照・更新仕様

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| リスナーエラー | リスナー関数内で例外が発生 | 例外はキャッチされず呼び出し元に伝播 |

### リトライ仕様

リトライなし

## 配信設定

### レート制限

制限なし

### 配信時間帯

制限なし（XRセッション中、ハンドトラッキング有効時のみ発火）

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

- ハンドトラッキングデータはWebXR API経由で取得され、ユーザー許可が必要

## 備考

- ピンチ検出の閾値: distanceToPinch = 0.02m、threshold = 0.005m
- ピンチ終了条件: distance > 0.02 + 0.005 = 0.025m
- ピンチ開始と終了で異なる閾値を使用（ヒステリシス）することで、チャタリングを防止

---

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

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

### 推奨読解順序

#### Step 1: ピンチ終了検出ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WebXRController.js | `src/renderers/webxr/WebXRController.js` | 270-278行目のピンチ終了判定処理 |

**主要処理フロー**:
1. **270行目**: pinching状態のチェック
2. **270行目**: distance > distanceToPinch + threshold のチェック
3. **272行目**: inputState.pinching = false に設定
4. **273-277行目**: pinchendイベント発火

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

```
WebXRController.update()
    │
    ├─ hand && inputSource.hand の確認
    │
    ├─ jointPose更新
    │
    └─ ピンチ終了検出
           │
           ├─ distance = indexTip.position.distanceTo(thumbTip.position)
           │
           └─ pinching && distance > 0.025
                  │
                  ├─ inputState.pinching = false
                  │
                  └─ this.dispatchEvent({
                         type: 'pinchend',
                         handedness: inputSource.handedness,
                         target: this
                     })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| WebXRController.js | `src/renderers/webxr/WebXRController.js` | ソース | pinchendイベント発火、ピンチ検出ロジック |
