# 機能設計書 163-Curve

## 概要

本ドキュメントは、Three.jsライブラリにおけるCurve機能の設計仕様を記述する。Curveは、2Dまたは3D空間における曲線を表現するための抽象基底クラスであり、補間やサンプリングのためのメソッドを提供する。

### 本機能の処理概要

Curveクラスは、曲線オブジェクトを作成するための抽象基底クラスとして機能する。補間係数（0〜1）を与えることで曲線上の任意の点を取得でき、等距離サンプリング、接線計算、Frenet Frame生成など、曲線処理に必要な基本機能を提供する。

**業務上の目的・背景**：3Dグラフィックスやアニメーションにおいて、曲線は様々な用途で使用される。パスに沿った移動、チューブやエクストルードジオメトリの生成、2Dシェイプの定義など、曲線ベースの処理は3D開発の基盤となる。Curveクラスは、これらの共通機能を抽象化し、様々な曲線タイプ（ベジェ、スプライン、楕円など）の基底となる。

**機能の利用シーン**：
- TubeGeometryやExtrudeGeometryでのパス定義
- アニメーションパスの生成
- 2Dシェイプの輪郭定義
- カメラやオブジェクトのパス移動

**主要な処理内容**：
1. getPoint() - 補間係数tに対応する曲線上の点を取得（抽象メソッド）
2. getPointAt() - 等距離サンプリングに基づく点の取得
3. getPoints() / getSpacedPoints() - 曲線のサンプリング
4. getLength() / getLengths() - 曲線長の計算とキャッシュ
5. getTangent() / getTangentAt() - 接線ベクトルの取得
6. computeFrenetFrames() - Frenet Frameの計算（法線、従法線、接線）
7. clone() / copy() / toJSON() / fromJSON() - オブジェクト操作

**関連システム・外部連携**：CurvePath、Path、Shape、TubeGeometry、ExtrudeGeometryなど、曲線ベースのジオメトリ生成クラスと連携。

**権限による制御**：特になし。すべてのユーザーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 直接的な画面関連なし（基盤クラス） |

## 機能種別

計算処理 / データ構造

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| t | number | Yes | 補間係数（0〜1） | 0以上1以下 |
| divisions | number | No | サンプリング分割数 | 正の整数 |
| segments | number | No | Frenet Frame計算用セグメント数 | 正の整数 |
| closed | boolean | No | 閉曲線かどうか | - |

### 入力データソース

派生クラスで実装されるgetPoint()メソッドにより、曲線固有のパラメータから点を生成。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| point | Vector2/Vector3 | 曲線上の点 |
| points | Array<Vector2/Vector3> | サンプリングされた点の配列 |
| length | number | 曲線の全長 |
| lengths | Array<number> | 累積長の配列 |
| tangent | Vector2/Vector3 | 接線ベクトル |
| frenetFrames | Object | {tangents, normals, binormals} |

### 出力先

メモリ上に返却され、呼び出し元で利用される。

## 処理フロー

### 処理シーケンス

```
1. 曲線インスタンス生成
   └─ 派生クラスで曲線パラメータを設定
2. getPoint(t) 呼び出し
   └─ 補間係数tに対応する点を計算（派生クラスで実装）
3. getLengths() 呼び出し
   └─ arcLengthDivisionsに基づいて曲線長をサンプリング
   └─ cacheArcLengthsにキャッシュ
4. getPointAt(u) 呼び出し
   └─ getUtoTmapping(u)で等距離補間係数を計算
   └─ getPoint(t)で点を取得
5. computeFrenetFrames() 呼び出し
   └─ 各セグメントの接線を計算
   └─ 法線と従法線を逐次計算
```

### フローチャート

```mermaid
flowchart TD
    A[曲線インスタンス生成] --> B{メソッド呼び出し}
    B -->|getPoint| C[派生クラスで点を計算]
    B -->|getPointAt| D[getUtoTmapping呼び出し]
    D --> E[getLengths呼び出し]
    E --> F{キャッシュあり?}
    F -->|Yes| G[キャッシュから取得]
    F -->|No| H[曲線長を計算]
    H --> I[キャッシュに保存]
    I --> G
    G --> J[二分探索でtを計算]
    J --> C
    B -->|getTangent| K[t±deltaの2点を取得]
    K --> L[差分ベクトルを正規化]
    B -->|computeFrenetFrames| M[各セグメントの接線を計算]
    M --> N[初期法線を設定]
    N --> O[法線・従法線を逐次計算]
    O --> P{closed?}
    P -->|Yes| Q[最初と最後を接続]
    P -->|No| R[結果を返却]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 抽象メソッド | getPoint()は派生クラスで必ず実装する | 常時 |
| BR-002 | キャッシュ無効化 | needsUpdate=trueで曲線長キャッシュを無効化 | パラメータ変更時 |
| BR-003 | デフォルト分割数 | arcLengthDivisions = 200 | 初期値 |
| BR-004 | 接線計算 | delta=0.0001で前後点から近似 | getTangent() |

### 計算ロジック

**getUtoTmapping計算**: 累積長配列に対して二分探索を行い、等距離パラメータuから元のパラメータtへの変換を実行。セグメント間は線形補間。

**Frenet Frame計算**: 接線ベクトルから、最小成分方向に初期法線を設定し、回転行列で法線を伝播させる。

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

該当なし（クライアントサイドのみで動作）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Warning | getPoint()が未実装 | 派生クラスで実装する |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- 曲線長はcacheArcLengthsにキャッシュされ、再計算を回避
- arcLengthDivisionsを増やすと精度向上するが計算コスト増加
- needsUpdate=trueにすると次回getLengths()でキャッシュを再計算

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

特になし

## 備考

- 抽象クラスのため、直接インスタンス化して使用することは想定されていない
- 派生クラス: LineCurve, QuadraticBezierCurve, CubicBezierCurve, SplineCurve, EllipseCurve, CatmullRomCurve3など

---

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

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

### 推奨読解順序

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

Curveは曲線の抽象基底クラスであり、arcLengthDivisionsとcacheArcLengthsでキャッシュ機構を持つ。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Curve.js | `src/extras/core/Curve.js` | クラス定義とプロパティ構造（18-57行目） |

**読解のコツ**: arcLengthDivisions（デフォルト200）が曲線長計算の精度を決定する。needsUpdateフラグでキャッシュ制御を行っている。

#### Step 2: コアメソッドを理解する

getPoint()は抽象メソッドで、派生クラスで実装される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Curve.js | `src/extras/core/Curve.js` | getPoint (68-72行目) - 抽象メソッド |
| 2-2 | Curve.js | `src/extras/core/Curve.js` | getPointAt (83-88行目) - 等距離サンプリング |
| 2-3 | Curve.js | `src/extras/core/Curve.js` | getPoints (97-109行目) - 点配列生成 |
| 2-4 | Curve.js | `src/extras/core/Curve.js` | getSpacedPoints (121-133行目) - 等距離点配列 |

**主要処理フロー**:
1. **68-72行目**: getPoint()は抽象メソッド、派生クラスで実装必須
2. **85行目**: getUtoTmapping()でu→t変換
3. **86行目**: getPoint(t)で実際の点を取得
4. **101-103行目**: divisions+1個の点を生成

#### Step 3: 曲線長計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Curve.js | `src/extras/core/Curve.js` | getLength (140-145行目) |
| 3-2 | Curve.js | `src/extras/core/Curve.js` | getLengths (153-184行目) |
| 3-3 | Curve.js | `src/extras/core/Curve.js` | getUtoTmapping (208-281行目) |

**主要処理フロー**:
- **155-160行目**: キャッシュがあり有効なら再利用
- **166-182行目**: 各分割点間の距離を累積計算
- **229-254行目**: 二分探索でターゲット長に対応するインデックスを検索
- **273-277行目**: セグメント内で線形補間

#### Step 4: 接線とFrenet Frameを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Curve.js | `src/extras/core/Curve.js` | getTangent (293-313行目) |
| 4-2 | Curve.js | `src/extras/core/Curve.js` | computeFrenetFrames (338-450行目) |

**主要処理フロー**:
- **295-302行目**: delta=0.0001で前後点から接線を近似
- **353-358行目**: 各セグメントの接線を計算
- **364-394行目**: 最小成分方向に初期法線を設定
- **399-418行目**: 回転行列で法線を逐次伝播
- **423-441行目**: 閉曲線の場合、最初と最後を接続

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

```
Curve
    │
    ├─ getPoint(t) [抽象メソッド]
    │      └─ 派生クラスで実装
    │
    ├─ getPointAt(u)
    │      └─ getUtoTmapping(u)
    │             └─ getLengths()
    │                    └─ getPoint(t) [繰り返し]
    │
    ├─ getPoints(divisions)
    │      └─ getPoint(t) [繰り返し]
    │
    ├─ getSpacedPoints(divisions)
    │      └─ getPointAt(u) [繰り返し]
    │
    ├─ getTangent(t)
    │      └─ getPoint(t-delta)
    │      └─ getPoint(t+delta)
    │
    └─ computeFrenetFrames(segments, closed)
           └─ getTangentAt(u) [繰り返し]
           └─ Matrix4.makeRotationAxis
```

### データフロー図

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

t (0-1) ───────────────▶ getPoint(t) ─────────────────▶ Vector2/Vector3
                           └─ 派生クラス実装

u (0-1) ───────────────▶ getPointAt(u) ───────────────▶ Vector2/Vector3
                           ├─ getUtoTmapping(u)
                           │     └─ 二分探索
                           └─ getPoint(t)

divisions ─────────────▶ getPoints/getSpacedPoints ───▶ Array<Vector>

segments, closed ──────▶ computeFrenetFrames ─────────▶ {tangents,
                           └─ 法線・従法線計算           normals,
                                                        binormals}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Curve.js | `src/extras/core/Curve.js` | ソース | 本機能のメイン実装（抽象基底クラス） |
| CurvePath.js | `src/extras/core/CurvePath.js` | ソース | 複数曲線を連結するパス |
| Path.js | `src/extras/core/Path.js` | ソース | 2Dパス |
| LineCurve.js | `src/extras/curves/LineCurve.js` | ソース | 直線曲線（派生クラス） |
| QuadraticBezierCurve.js | `src/extras/curves/QuadraticBezierCurve.js` | ソース | 2次ベジェ曲線 |
| CubicBezierCurve.js | `src/extras/curves/CubicBezierCurve.js` | ソース | 3次ベジェ曲線 |
| SplineCurve.js | `src/extras/curves/SplineCurve.js` | ソース | スプライン曲線 |
| EllipseCurve.js | `src/extras/curves/EllipseCurve.js` | ソース | 楕円曲線 |
| CatmullRomCurve3.js | `src/extras/curves/CatmullRomCurve3.js` | ソース | Catmull-Romスプライン（3D） |
| Vector2.js | `src/math/Vector2.js` | ソース | 2Dベクトル |
| Vector3.js | `src/math/Vector3.js` | ソース | 3Dベクトル |
| Matrix4.js | `src/math/Matrix4.js` | ソース | 4x4行列（Frenet Frame計算用） |
| MathUtils.js | `src/math/MathUtils.js` | ソース | clamp関数など |
| TubeGeometry.js | `src/geometries/TubeGeometry.js` | ソース | 曲線に沿ったチューブ生成 |
| ExtrudeGeometry.js | `src/geometries/ExtrudeGeometry.js` | ソース | 押し出しジオメトリ |
