# 機能設計書 167-ShapePath

## 概要

本ドキュメントは、Three.jsライブラリにおけるShapePath機能の設計仕様を記述する。ShapePathは、一連のパスをShape配列に変換するためのクラスであり、特にフォントやSVGの解析に使用される。

### 本機能の処理概要

ShapePathクラスは、複数のサブパスを管理し、それらをShape配列に変換する機能を提供する。フォントグリフやSVGパスデータの解析結果を受け取り、外形と穴を持つShapeオブジェクトの配列として出力する。巻き方向（時計回り/反時計回り）に基づいて外形と穴を自動判別する機能を持つ。

**業務上の目的・背景**：フォントやSVGデータは、複数の閉じたパス（サブパス）で構成される複雑な形状を含むことがある。例えば、文字「B」は外形と2つの穴から構成される。ShapePathはこれらの複雑な構造を自動的に解析し、正しく穴が割り当てられたShape配列を生成する。

**機能の利用シーン**：
- フォントローダーでのグリフ変換
- SVGローダーでのパス変換
- 複数のパスを含む複雑な形状の処理
- テキストの3D化

**主要な処理内容**：
1. subPaths - サブパス（Path）の配列を管理
2. currentPath - 現在構築中のパス
3. moveTo/lineTo/bezierCurveTo/quadraticCurveTo/splineThru - 描画メソッド
4. toShapes() - サブパスをShape配列に変換

**関連システム・外部連携**：FontLoader、SVGLoader、Shape、Path、ShapeUtilsと連携。

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

## 関連画面

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

## 機能種別

計算処理 / データ変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| isCCW | boolean | No | 巻き方向の反転フラグ（デフォルト: false） | - |
| x, y | number | Yes | 座標値 | 有効な数値 |
| aCPx, aCPy | number | Yes | 制御点座標（2次ベジェ） | 有効な数値 |
| aCP1x, aCP1y, aCP2x, aCP2y | number | Yes | 制御点座標（3次ベジェ） | 有効な数値 |
| pts | Array<Vector2> | Yes | スプライン用点配列 | Vector2の配列 |

### 入力データソース

フォントローダーやSVGローダーから生成されたパスデータ。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| shapes | Array<Shape> | 生成されたShape配列 |
| color | Color | シェイプの色 |
| subPaths | Array<Path> | サブパスの配列 |

### 出力先

toShapes()メソッドでShape配列として返却。

## 処理フロー

### 処理シーケンス

```
1. ShapePathインスタンス生成
   └─ color, subPaths, currentPathを初期化
2. moveTo呼び出し
   └─ 新しいPathを作成
   └─ subPathsに追加
   └─ currentPathを更新
3. 描画メソッド呼び出し
   └─ currentPathのメソッドに委譲
4. toShapes(isCCW)呼び出し
   └─ 各サブパスの巻き方向を判定
   └─ 外形をShapeとして作成
   └─ 穴を対応するShapeに割り当て
   └─ Shape配列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[ShapePath生成] --> B[moveTo呼び出し]
    B --> C[新しいPath作成]
    C --> D[subPathsに追加]
    D --> E{描画メソッド?}
    E -->|lineTo等| F[currentPathに委譲]
    F --> E
    E -->|moveTo| B
    E -->|toShapes| G[巻き方向判定]
    G --> H{時計回り?}
    H -->|Yes| I[外形としてマーク]
    H -->|No| J[穴としてマーク]
    I --> K[Shape作成]
    J --> L[対応Shapeの穴に追加]
    K --> M[Shape配列を返却]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 巻き方向判定 | 時計回りは外形、反時計回りは穴（isCCWで反転可能） | toShapes() |
| BR-002 | 穴の割り当て | 穴は包含関係に基づいて対応するShapeに割り当て | toShapes() |
| BR-003 | 単一パス | subPathsが1つの場合、そのままShapeとして返却 | toShapes() |
| BR-004 | moveTo必須 | 新しいサブパスはmoveTo()で開始 | 常時 |

### 計算ロジック

**穴の割り当てアルゴリズム**: 各穴について、どのShapeの多角形に包含されるかをisPointInsidePolygon()で判定。複数のShapeに包含される場合は曖昧性あり（ambiguous）としてマーク。

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 特になし | - |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- toShapes()はサブパス数の二乗に比例した計算量（穴の割り当て）
- isPointInsidePolygon()は多角形の頂点数に比例

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

特になし

## 備考

- ShapeUtils.isClockWise()で巻き方向を判定
- isPointInsidePolygon()はレイキャスティング法で包含判定

---

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

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

### 推奨読解順序

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

ShapePathはsubPaths配列でサブパスを管理し、currentPathで現在のパスを追跡する。

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

**読解のコツ**: colorプロパティはSVG等でのスタイル情報に使用。subPathsは完了したパス、currentPathは構築中のパス。

#### Step 2: 描画メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ShapePath.js | `src/extras/core/ShapePath.js` | moveTo (51-59行目) |
| 2-2 | ShapePath.js | `src/extras/core/ShapePath.js` | lineTo, quadraticCurveTo, bezierCurveTo, splineThru (69-128行目) |

**主要処理フロー**:
1. **53行目**: moveTo()で新しいPathを作成
2. **54行目**: subPaths.push(currentPath)
3. **55行目**: currentPath.moveTo(x, y)
4. **71行目**: lineTo()はcurrentPath.lineTo()に委譲
5. **89行目**: quadraticCurveTo()もcurrentPathに委譲

#### Step 3: toShapes()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ShapePath.js | `src/extras/core/ShapePath.js` | toShapes (137-362行目) |
| 3-2 | ShapePath.js | `src/extras/core/ShapePath.js` | toShapesNoHoles (139-156行目) - 内部関数 |
| 3-3 | ShapePath.js | `src/extras/core/ShapePath.js` | isPointInsidePolygon (158-216行目) - 内部関数 |

**主要処理フロー**:
- **218行目**: ShapeUtils.isClockWiseを取得
- **221行目**: subPathsが空なら空配列を返却
- **226-233行目**: 単一サブパスはそのままShapeに変換
- **236行目**: 最初のサブパスの巻き方向を判定
- **250-277行目**: 各サブパスを外形か穴に分類
- **283-340行目**: 穴を対応するShapeに割り当て
- **344-356行目**: 最終的なShape配列を構築

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

```
ShapePath
    │
    ├─ moveTo(x, y)
    │      ├─ new Path()
    │      ├─ subPaths.push()
    │      └─ currentPath.moveTo()
    │
    ├─ lineTo(x, y)
    │      └─ currentPath.lineTo()
    │
    ├─ quadraticCurveTo / bezierCurveTo / splineThru
    │      └─ currentPath.{method}()
    │
    └─ toShapes(isCCW)
           ├─ ShapeUtils.isClockWise
           ├─ isPointInsidePolygon [内部関数]
           │      └─ レイキャスティング法
           ├─ 外形/穴の分類
           └─ 穴の割り当て
                  └─ betterShapeHoles構築
```

### データフロー図

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

moveTo/lineTo等 ───────▶ subPaths蓄積 ────────────────▶ Path配列

subPaths ──────────────▶ toShapes(isCCW) ─────────────▶ Shape配列
                           ├─ 巻き方向判定
                           ├─ 外形/穴分類
                           └─ 穴割り当て
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ShapePath.js | `src/extras/core/ShapePath.js` | ソース | 本機能のメイン実装 |
| Path.js | `src/extras/core/Path.js` | ソース | サブパスの実体 |
| Shape.js | `src/extras/core/Shape.js` | ソース | 出力形式 |
| ShapeUtils.js | `src/extras/ShapeUtils.js` | ソース | isClockWise判定 |
| Color.js | `src/math/Color.js` | ソース | シェイプの色 |
