# 通知設計書 28-NOTIFICATION_DRAW

## 概要

本ドキュメントは、Godot EngineのCanvasItemクラスにおける`NOTIFICATION_DRAW`通知の設計仕様を記載する。この通知は2Dアイテムの描画が必要な時に発火し、カスタム描画処理を実行するための重要な通知である。

### 本通知の処理概要

NOTIFICATION_DRAWは、CanvasItem派生クラス（Control、Node2Dなど）で描画処理が必要なタイミングで発行されるシステム通知である。この通知内でのみ、draw_line()、draw_rect()、draw_texture()などの描画APIを呼び出すことができる。

**業務上の目的・背景**：2Dゲーム開発において、カスタム描画は重要な機能である。スプライト、図形、テキスト、エフェクトなど、様々なビジュアル要素をスクリプトやC++コードから直接描画する必要がある。NOTIFICATION_DRAWは、描画コマンドが正しく処理される適切なタイミングを提供し、カスタムビジュアルの実装を可能にする。

**通知の送信タイミング**：queue_redraw()呼び出し後、描画フレームで_redraw_callback()が実行される際。具体的には以下の条件で発火：
1. ノードがシーンツリー内にある（is_inside_tree() == true）
2. ノードが可視状態（is_visible_in_tree() == true）
3. queue_redraw()が呼ばれて再描画がリクエストされている

**通知の受信者**：CanvasItemおよびそのすべての派生クラス（Control、Node2D、Polygon2D、Line2D、Label、Spriteなど）。

**通知内容の概要**：通知IDは整数値30（NOTIFICATION_DRAW = 30）。追加のパラメータは含まない。

**期待されるアクション**：
- draw_*系メソッドを使用したカスタム描画
- テクスチャ、図形、テキストの描画
- デバッグ情報の描画
- エフェクトやオーバーレイの描画

## 通知種別

エンジン内部通知（Engine Internal Notification）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 遅延（Deferred） |
| 優先度 | 描画フレームでの処理 |
| リトライ | 無し |

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

1. queue_redraw()が呼ばれ、pending_update = true に設定
2. 描画フレームで_redraw_callback()がcall_deferredで呼び出される
3. is_inside_tree()とis_visible_in_tree()をチェック
4. 描画コマンドがクリアされる（draw_commands_dirty時）
5. drawing = true に設定
6. NOTIFICATION_DRAWが送信される
7. drawシグナルが発行される
8. _draw()仮想関数が呼ばれる
9. drawing = false に設定

## 通知テンプレート

### システム通知の場合

| 項目 | 内容 |
|-----|------|
| 通知ID | 30 |
| 通知名 | NOTIFICATION_DRAW |
| 定義箇所 | scene/main/canvas_item.h |

### 通知ハンドリング例

```cpp
void MyCanvasItem::_notification(int p_notification) {
    switch (p_notification) {
        case NOTIFICATION_DRAW: {
            // カスタム描画処理
            draw_rect(Rect2(0, 0, 100, 100), Color::named("red"));
            draw_line(Vector2(0, 0), Vector2(100, 100), Color::named("blue"), 2.0);
        } break;
    }
}
```

```gdscript
func _notification(what):
    if what == NOTIFICATION_DRAW:
        # カスタム描画処理
        draw_rect(Rect2(0, 0, 100, 100), Color.RED)
        draw_line(Vector2.ZERO, Vector2(100, 100), Color.BLUE, 2.0)

# または _draw() オーバーライドを使用
func _draw():
    draw_rect(Rect2(0, 0, 100, 100), Color.RED)
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| p_notification | 通知ID（30） | エンジン内部 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | CanvasItem.queue_redraw() | is_inside_tree() == true | 再描画リクエスト |
| 自動 | シーンツリー追加時 | 初回描画 | ENTER_TREE後の初回 |
| 自動 | 可視性変更時 | visible → true | show()呼び出し時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| is_inside_tree() == false | シーンツリー外 |
| is_visible_in_tree() == false | 非表示状態 |
| pending_update == false | 再描画リクエストなし |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[queue_redraw呼び出し] --> B{is_inside_tree?}
    B -->|No| C[処理なし]
    B -->|Yes| D{pending_update?}
    D -->|Yes| C
    D -->|No| E[pending_update = true]
    E --> F[_redraw_callback.call_deferred]
    F --> G[描画フレーム]
    G --> H{is_inside_tree?}
    H -->|No| I[pending_update = false]
    H -->|Yes| J{draw_commands_dirty?}
    J -->|Yes| K[canvas_item_clear]
    J -->|No| L[スキップ]
    K --> L
    L --> M{is_visible_in_tree?}
    M -->|No| I
    M -->|Yes| N[drawing = true]
    N --> O[notification\nNOTIFICATION_DRAW\]
    O --> P[emit_signal\draw\]
    P --> Q[GDVIRTUAL_CALL\n_draw\]
    Q --> R[drawing = false]
    R --> I
```

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

### 参照データ一覧

| データ | 用途 | 備考 |
|--------|------|------|
| CanvasItem::visible | 可視性チェック | bool |
| CanvasItem::parent_visible_in_tree | 親の可視性 | bool |
| CanvasItem::pending_update | 再描画待ちフラグ | bool |
| CanvasItem::drawing | 描画中フラグ | bool |

### 更新データ一覧

| データ | 操作 | 概要 |
|--------|------|------|
| CanvasItem::drawing | 設定 | true（描画開始）→ false（描画終了） |
| CanvasItem::pending_update | 設定 | false（描画完了後） |
| CanvasItem::draw_commands_dirty | 設定 | true（描画完了後） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 描画コンテキスト外 | drawing == false時にdraw_*呼び出し | ERR_DRAW_GUARDマクロでエラー出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | フレームレートに依存（通常60fps） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

描画フレームごとに発生可能（queue_redraw()が呼ばれた場合）

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

- 描画APIは描画コンテキスト内（drawing == true）でのみ使用可能
- ERR_DRAW_GUARDマクロにより不正な描画呼び出しは防止される

## 備考

- NOTIFICATION_DRAWは描画コンテキストを提供する唯一の通知
- GDScriptでは_draw()仮想関数のオーバーライドでも同等の処理が可能
- drawシグナルに接続することで、外部から描画を追加することも可能
- current_item_drawnでグローバルに現在描画中のアイテムを取得可能

---

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

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

### 推奨読解順序

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

CanvasItemの描画関連データ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | canvas_item.h | `scene/main/canvas_item.h` | NOTIFICATION_DRAW定数の定義（211行目） |
| 1-2 | canvas_item.h | `scene/main/canvas_item.h` | drawing、pending_updateフラグ（108、111行目） |
| 1-3 | canvas_item.h | `scene/main/canvas_item.h` | draw_commands_dirtyフラグ（109行目） |

**読解のコツ**: pending_updateは再描画要求の有無、drawingは現在描画中かどうかを示すフラグ。

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

描画処理の起点となる関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | canvas_item.cpp | `scene/main/canvas_item.cpp` | queue_redraw関数（472-484行目） |
| 2-2 | canvas_item.cpp | `scene/main/canvas_item.cpp` | _redraw_callback関数（133-157行目） |

**主要処理フロー**:
1. **472-479行目**: queue_redraw()でis_inside_tree()とpending_updateチェック
2. **133-157行目**: _redraw_callback()でNOTIFICATION_DRAW送信、drawシグナル発行、_draw()呼び出し

#### Step 3: 描画API保護を理解する

描画コンテキスト外での呼び出し防止メカニズムを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | canvas_item.cpp | `scene/main/canvas_item.cpp` | ERR_DRAW_GUARDマクロ定義（43-44行目） |

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

```
CanvasItem::queue_redraw()
    │
    ├─ is_inside_tree() チェック
    │
    ├─ pending_update チェック
    │
    ├─ pending_update = true
    │
    └─ _redraw_callback.call_deferred()
           │
           [描画フレーム]
           │
           └─ CanvasItem::_redraw_callback()
                  │
                  ├─ is_inside_tree() チェック
                  │
                  ├─ draw_commands_dirty → canvas_item_clear()
                  │
                  ├─ is_visible_in_tree() チェック
                  │
                  ├─ drawing = true
                  │
                  ├─ TextServer::set_current_drawn_item_oversampling()
                  │
                  ├─ current_item_drawn = this
                  │
                  ├─ notification(NOTIFICATION_DRAW)
                  │      └─ CanvasItem::_notification()
                  │             └─ [派生クラスの_notification]
                  │
                  ├─ emit_signal("draw")
                  │      └─ [接続された関数]
                  │
                  ├─ GDVIRTUAL_CALL(_draw)
                  │      └─ GDScript::_draw() or 派生クラス::_draw()
                  │
                  ├─ current_item_drawn = nullptr
                  │
                  ├─ drawing = false
                  │
                  └─ pending_update = false
```

### データフロー図

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

queue_redraw() ─────────▶ pending_update = true ────────▶ call_deferred
       │                       │
       │                       ▼
       │              [描画フレーム]
       │                       │
       │                       ▼
       │              _redraw_callback()
       │                       │
       │                       ▼
       │              canvas_item_clear() [条件付き]
       │                       │
       │                       ▼
       │              drawing = true
       │                       │
       └───────────────────────┴───────────────────────▶ NOTIFICATION_DRAW
                                                              │
                                                              ▼
                                                        draw_*() 呼び出し可能
                                                              │
                                                              ▼
                                                        RenderingServer描画コマンド
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| canvas_item.h | `scene/main/canvas_item.h` | ヘッダー | CanvasItem基底クラス定義、通知定数 |
| canvas_item.cpp | `scene/main/canvas_item.cpp` | ソース | queue_redraw、_redraw_callback、draw_*メソッドの実装 |
| control.cpp | `scene/gui/control.cpp` | ソース | ControlでのNOTIFICATION_DRAW処理 |
| node_2d.cpp | `scene/2d/node_2d.cpp` | ソース | Node2DでのNOTIFICATION_DRAW処理 |
| rendering_server.h | `servers/rendering/rendering_server.h` | ヘッダー | 描画コマンドAPI定義 |
| text_server.h | `servers/text/text_server.h` | ヘッダー | テキスト描画API定義 |
