# 機能設計書 110-ノート配置

## 概要

本ドキュメントは、K-Shoot Editor(譜面エディタ)における各種オブジェクト(ノート/アナログ/スピン/Tilt/Zoom/STOP等)のPencilツールによる配置・削除操作の設計を記述する。

### 本機能の処理概要

本機能は、譜面エディタ上でPencilツール(editmode=MODE_PENCIL)使用時の左クリックによるオブジェクト配置、およびEraseツール(editmode=MODE_ERASE)または右クリックによるオブジェクト削除を提供する。edittargetの値に応じて操作対象が切り替わり、BPM/拍子/コメントの設定、チップBT/FXノート、ロングBT/FXノート(ドラッグ対応)、アナログL/R(ドラッグ対応)、スピン、STOP(ドラッグ対応)、Tilt(ダイアログ入力)、Zoom(ダイアログ入力)等の配置・削除を処理する。

**業務上の目的・背景**: 譜面に各種オブジェクトを配置することが譜面制作の中心的操作であり、本機能はエディタの最も基本的な編集機能を提供する。

**機能の利用シーン**: Pencilツール選択時に譜面上を左クリック/ドラッグで配置、右クリックで削除。Eraseツール選択時は左クリックで削除、右クリックで配置。

**主要な処理内容**:
1. TARGET_POS: BPM変更(クリック)、拍子変更(Ctrl+クリック)、コメント挿入(Ctrl+Shift+クリック)
2. TARGET_NOTE: チップBTノートの配置/削除
3. TARGET_LNOTE2: チップFXノートの配置/削除
4. TARGET_NOTE2: ロングBTノートの配置(ドラッグで長さ指定)/削除
5. TARGET_LNOTE: ロングFXノートの配置(ドラッグで長さ指定)/削除、FXエフェクトパラメータ変更(Ctrl+Shift+クリック)
6. TARGET_ANALOGL/R: アナログオブジェクトの配置(ドラッグ)/削除
7. TARGET_SPIN: スピンオブジェクトの配置/削除
8. TARGET_ROTATE: Tiltエフェクトの挿入(ダイアログ)/削除
9. TARGET_ZOOMTOP/ZOOMBOTTOM/ZOOMSIDE: Zoom値の挿入(ダイアログ)/削除
10. TARGET_STOP: STOPオブジェクトの配置(ドラッグで長さ指定)/削除
11. TARGET_ANALOGPFILTER: LASERエフェクト強度変更(ダイアログ)/フィルタ種別切替(Ctrl+クリック)
12. TARGET_ANALOGVOL: LASER音量変更(ダイアログ)

**関連システム・外部連携**: なし。

**権限による制御**: なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 譜面エディタ | 主画面 | Pencil/Eraseツール使用時のマウスクリック/ドラッグ操作 |

## 機能種別

データ操作(オブジェクト配置・削除)

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| editmode | 整数 | Yes | 編集モード(MODE_PENCIL/MODE_ERASE) | - |
| edittarget | 整数 | Yes | 編集対象(TARGET_POS~TARGET_STOP) | - |
| iparam | 整数 | Yes | マウスボタン(0:左, 3:右) | - |
| cc | 整数 | Yes | クリック位置のカウント値(クオンタイズ済み) | - |
| cbtn | 整数 | Yes | クリック位置のBTレーン番号(0-3) | - |
| clbtn | 整数 | Yes | クリック位置のFXレーン番号(4-5) | - |
| ctrl_key | 整数 | Yes | Ctrlキー押下状態(0/1) | - |
| shift_key | 整数 | Yes | Shiftキー押下状態(0/1) | - |
| editdiv | 整数 | Yes | グリッド分割数(ノート長の基本単位決定) | - |

### 入力データソース

マウスクリック/ドラッグ位置、キーボード修飾キー状態、ダイアログ入力(BPM/拍子/Tilt/Zoom等)

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| note配列 | 整数配列[131072][6] | ノートデータ(.0:レーン, .1:位置, .2:長さ, .3:FXタイプ, .4:FXパラメータ1, .5:FXパラメータ2) |
| analog配列 | 整数配列[131072][6] | アナログデータ(.0:L/R, .1:位置, .2:長さ, .3:始点X, .4:終点X, .5:接続フラグ) |
| spin配列 | 整数配列 | スピンデータ(.0:種別, .1:位置, .2:長さ, .3:振幅, .4:回数, .5:減衰) |
| stp配列 | 整数配列 | STOPデータ(.0:位置, .1:長さ) |
| tilt配列 | 整数配列 | Tiltデータ(.0:位置, .1:値1, .2:値2) |
| ztop/zbot/zside配列 | 整数配列 | Zoom上部/下部/左右データ(.0:位置, .1:値1, .2:値2) |
| bpml配列 | 整数配列 | BPMデータ(.0:位置, .1:BPM*1000) |
| beatl配列 | 整数配列 | 拍子データ(.0:位置, .1:拍子分子, .2:拍子分母) |
| comment配列 | 文字列配列 | コメントデータ |
| notenum等 | 整数 | 各配列の要素数 |

### 出力先

メモリ上の各種配列

## 処理フロー

### 処理シーケンス

```
配置処理(左クリック+Pencilツール or 右クリック+Eraseツール):
  ├─ TARGET_POS (カーソル位置設定)
  │   ├─ Ctrl=0, Shift=0: BPM変更(ダイアログ入力)
  │   ├─ Ctrl=1, Shift=0: 拍子変更(ダイアログ入力)
  │   └─ Ctrl=1, Shift=1: コメント挿入(ダイアログ入力)
  ├─ TARGET_NOTE (チップBT)
  │   ├─ Shift=1: 既存ノートを選択(配置しない)
  │   ├─ 同位置同レーンにノート存在: 配置中断
  │   └─ 新規ノート追加: note[stat1] = {cbtn, cc, 0, 0, 0, 0}
  ├─ TARGET_LNOTE2 (チップFX)
  │   ├─ Ctrl=1: 効果音設定(ダイアログ)
  │   ├─ Shift=1: 既存ノートを選択
  │   └─ 新規ノート追加: note[stat1] = {clbtn, cc, 0, 0, 0, 0}
  ├─ TARGET_NOTE2 (ロングBT)
  │   ├─ Shift=1: 既存ノートを選択
  │   ├─ 新規ノート追加: note[stat1] = {cbtn, cc, UNIT_MEASURE/editdiv, ...}
  │   ├─ ドラッグで長さ変更: note.stat1.2 = cc2 - cc
  │   └─ リリース後に重複ノート解消(4パターン処理)
  ├─ TARGET_LNOTE (ロングFX)
  │   ├─ Ctrl+Shift=0: FXエフェクト変更(notefxplus)
  │   ├─ Ctrl+Shift=1: FXパラメータリセット
  │   ├─ Shift=1: Retrigger/Gate/Wobble/Echo/PitchShift/BitCrusher/TapeStop速度変更(ダイアログ)
  │   └─ 新規ノート追加(ドラッグ対応)
  ├─ TARGET_ANALOGL/R (アナログ)
  │   ├─ 新規アナログ追加(ドラッグでX位置指定)
  │   └─ リリース後に重複解消/接続処理
  ├─ TARGET_SPIN (スピン)
  │   ├─ Ctrl=0: 新規スピン追加
  │   ├─ Ctrl=1,Shift=0: スピン種別変更(ノーマル↔バネ)
  │   └─ Ctrl+Shift=1: バネエフェクトパラメータ設定(ダイアログ)
  ├─ TARGET_ROTATE (Tilt)
  │   ├─ Ctrl=0: Tilt種別選択(ダイアログ)
  │   └─ Ctrl=1: Tilt手動指定(ダイアログ)
  ├─ TARGET_ZOOMTOP/ZOOMBOTTOM/ZOOMSIDE (Zoom)
  │   └─ Zoom値入力(ダイアログ)
  ├─ TARGET_STOP
  │   └─ 新規STOP追加(ドラッグで長さ指定)
  └─ TARGET_ANALOGPFILTER/ANALOGVOL
      ├─ ANALOGPFILTER: エフェクト強度変更(ダイアログ) / Ctrl:フィルタ切替
      └─ ANALOGVOL: 音量変更(ダイアログ)

削除処理(右クリック+Pencilツール or 左クリック+Eraseツール):
  ├─ TARGET_POS
  │   ├─ BPM削除(先頭以外)
  │   ├─ 拍子削除(先頭以外)
  │   └─ コメント削除
  ├─ TARGET_NOTE: 同位置同レーンのチップBT削除
  ├─ TARGET_NOTE2: カーソル位置のロングBT削除
  ├─ TARGET_LNOTE2: 同位置同レーンのチップFX削除
  ├─ TARGET_LNOTE
  │   ├─ Ctrl+Shift=0(Pencil): FXエフェクトダウン(notefxminus)
  │   ├─ Ctrl+Shift=1(Pencil): FXパラメータリセット(note.3=9,note.4=16)
  │   └─ 通常: ロングFX削除
  ├─ TARGET_ROTATE: Tilt削除
  └─ TARGET_STOP: STOP削除
```

### フローチャート

```mermaid
flowchart TD
    A[マウスクリック] --> B{Pencil+左click or Erase+右click?}
    B -->|配置| C{edittarget?}
    C -->|POS| D[BPM/拍子/コメント ダイアログ]
    C -->|NOTE| E[チップBT追加]
    C -->|NOTE2| F[ロングBT追加 ドラッグ]
    C -->|LNOTE| G[ロングFX追加 ドラッグ]
    C -->|LNOTE2| H[チップFX追加]
    C -->|ANALOG| I[アナログ追加 ドラッグ]
    C -->|SPIN| J[スピン追加]
    C -->|ROTATE| K[Tilt ダイアログ]
    C -->|ZOOM| L[Zoom ダイアログ]
    C -->|STOP| M[STOP追加 ドラッグ]
    B -->|削除| N{edittarget?}
    N -->|NOTE/LNOTE2| O[該当ノート削除]
    N -->|NOTE2/LNOTE| P[該当ロングノート削除]
    N -->|POS| Q[BPM/拍子/コメント削除]
    N -->|ROTATE/STOP| R[該当オブジェクト削除]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-110-01 | 重複防止 | 同位置・同レーンに既にノートが存在する場合は配置を中断する | TARGET_NOTE/LNOTE2(チップ) |
| BR-110-02 | Undo保存 | 配置・削除操作前に*undokakuhoでUndoスナップショットを保存する | 全対象 |
| BR-110-03 | ロングノートドラッグ | ロングBT/FX/STOP配置時はマウスボタンを押したままドラッグで長さを指定できる | TARGET_NOTE2/LNOTE/STOP |
| BR-110-04 | 最小ノート長 | ロングノートの最小長はUNIT_MEASURE/editdiv(グリッド1分割分) | TARGET_NOTE2/LNOTE/STOP |
| BR-110-05 | 重複ノート解消 | ロングノート配置後、既存ノートとの重複を4パターン([( )], ( [ ] ), [ ( ) ], ( [ ) ])で解消する | TARGET_NOTE2/LNOTE/STOP |
| BR-110-06 | 先頭BPM/拍子保護 | BPMおよび拍子の先頭(index=0)は削除不可 | TARGET_POS(削除時) |
| BR-110-07 | 拍子分母制約 | 拍子の分母は192の約数でなければならない(192%分母=0) | TARGET_POS(拍子変更) |
| BR-110-08 | BPM範囲制約 | BPM値は0より大きく65536未満 | TARGET_POS(BPM変更) |
| BR-110-09 | スピン種別 | spin.0の値: 0-1(ノーマル左/右)、2-3(ノーマル上/下)、4-5(バネ左/右) | TARGET_SPIN |
| BR-110-10 | アナログ接続 | アナログ配置後に始点/終点の自動接続とparent参照の解決を行う | TARGET_ANALOGL/R |
| BR-110-11 | 修飾キー操作 | Shift+クリックで既存オブジェクトを選択(配置しない)、Ctrl+クリックで特殊操作(FXパラメータ変更等) | 各対象 |

### 計算ロジック

クオンタイズ位置計算:
- `cc` = クリック位置をeditdivでクオンタイズした結果
- ドラッグ時の終端位置: `cc2 = b2c(c2b(cc2_raw)) + ((cc2_raw - b2c(c2b(cc2_raw))) / (UNIT_MEASURE/editdiv) + 1) * (UNIT_MEASURE/editdiv)`

ソート挿入:
- 新規ノートはnote配列を位置順にソートされた状態を維持して挿入(for文で後方シフト)

スピン種別変換(Ctrl+クリック):
- ノーマル(0-1)→バネ(+2): `spin.0 += 2`
- バネ(4-5)→ノーマル: `spin.0 = spin.0 % 2`

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 配置中断 | 同位置同レーンに既存チップノートあり | 配置せずにreturn |
| - | 入力エラー | BPM値が範囲外/拍子分母が192の約数でない | ダイアログでエラーメッセージ表示 |
| - | キャンセル | ダイアログでキャンセル(stat=-1) | 配置せずに続行 |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- ノート配列の挿入はソート位置を検索してから後方シフトするため、ノート数に比例した処理時間がかかる
- ドラッグ中はawait 0ループで継続的に画面を再描画する

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

特になし。

## 備考

- 配置と削除の左右クリックは、PencilツールとEraseツールで反転する(Pencil:左=配置/右=削除、Erase:左=削除/右=配置)
  - コード: `iparam=0 & editmode=MODE_PENCIL` または `iparam=3 & editmode=MODE_ERASE`(配置)
  - コード: `iparam=3 & editmode=MODE_PENCIL` または `iparam=0 & editmode=MODE_ERASE`(削除)
- TARGET_NOTEのEdittarget例外: Eraseツール左クリック時にもTARGET_NOTEの削除処理が動作する(`iparam=3 & editmode=MODE_ERASE & edittarget=TARGET_NOTE`)
- ロングノート/STOP配置時、ドラッグの終端が始点より前(cc2-cc<0)の場合は最小長(UNIT_MEASURE/editdiv)に補正される
- アナログ配置はドラッグ中にリアルタイムでX座標を更新し、リリース後に重複解消処理を実行する
- コメント挿入時、直接編集モード(isDirectChartEditingEnabled)無効の場合は先頭に"//"が自動付与される
- FXエフェクトパラメータ変更はRetrigger/Gate/Wobble/Echo/PitchShift/BitCrusher/TapeStopの各種に対応
- notesefname配列でチップノートに効果音ファイル(wav)を紐付け可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | kshooteditor.hsp | `kshooteditor.hsp` | 155-173行目: MODE_xxx, TARGET_xxxのenum定義 |
| 1-2 | kshooteditor.hsp | `kshooteditor.hsp` | 181行目: UNIT_MEASURE=192の定義 |
| 1-3 | kshooteditor.hsp | `kshooteditor.hsp` | 400-480行目: note[131072][6], analog[131072][6]等の配列宣言 |

#### Step 2: クリック判定ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kshooteditor.hsp | `kshooteditor.hsp` | 10942-10943行目: 配置条件分岐(Pencil+左クリック or Erase+右クリック+TARGET_NOTE) |
| 2-2 | kshooteditor.hsp | `kshooteditor.hsp` | 13038-13039行目: 削除条件分岐(Pencil+右クリック or Erase+左クリック) |

#### Step 3: 各対象の配置処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kshooteditor.hsp | `kshooteditor.hsp` | 10945-11103行目: TARGET_POS(BPM/拍子/コメント) |
| 3-2 | kshooteditor.hsp | `kshooteditor.hsp` | 11104-11163行目: TARGET_NOTE(チップBT配置) |
| 3-3 | kshooteditor.hsp | `kshooteditor.hsp` | 11164-11290行目: TARGET_LNOTE2(チップFX配置) |
| 3-4 | kshooteditor.hsp | `kshooteditor.hsp` | 11291-11501行目: TARGET_NOTE2(ロングBT配置、ドラッグ+重複解消) |
| 3-5 | kshooteditor.hsp | `kshooteditor.hsp` | 11502-11878行目: TARGET_LNOTE(ロングFX配置、FXパラメータ変更) |
| 3-6 | kshooteditor.hsp | `kshooteditor.hsp` | 11879-12049行目: TARGET_STOP(STOP配置、ドラッグ+重複解消) |
| 3-7 | kshooteditor.hsp | `kshooteditor.hsp` | 12050-12172行目: TARGET_ROTATE(Tilt配置、ダイアログ) |
| 3-8 | kshooteditor.hsp | `kshooteditor.hsp` | 12173-12300行目: TARGET_ANALOGPFILTER(エフェクト強度/フィルタ) |
| 3-9 | kshooteditor.hsp | `kshooteditor.hsp` | 12793-12873行目: TARGET_SPIN(スピン配置、種別変更、パラメータ設定) |
| 3-10 | kshooteditor.hsp | `kshooteditor.hsp` | 12874-13000行目: TARGET_ZOOMTOP/ZOOMBOTTOM/ZOOMSIDE(Zoom値設定) |

**主要処理フロー(TARGET_NOTE - チップBT配置)**:
- **11104-11121行目**: Shift+クリックで既存ノートの選択(配置しない)
- **11122行目**: `gosub *undokakuho` でUndo保存
- **11125-11133行目**: 同位置同レーンの既存ノートチェック(stat2=1なら中断)
- **11140-11148行目**: for文で後方シフトしてソート位置に挿入スペース確保
- **11149-11155行目**: note.stat1に新規ノートデータ設定(レーン=cbtn, 位置=cc, 長さ=0)
- **11156-11162行目**: 選択状態設定、notenum++、変更フラグ設定

**主要処理フロー(TARGET_NOTE2 - ロングBT配置)**:
- **11310行目**: `gosub *undokakuho` でUndo保存
- **11324-11339行目**: ソート位置に挿入し、初期長=UNIT_MEASURE/editdivに設定
- **11340-11341行目**: `enowstat=1` `enowl=stat_1` でドラッグ状態設定
- **11349行目**: `oncmd gosub *OnClick2, 0x0202` でマウスアップハンドラ登録
- **11350-11372行目**: ドラッグループ(マウス位置からcc2を計算し、note.2を更新、await 0で再描画)
- **11374-11497行目**: リリース後の重複ノート解消(4パターン: [()], ([]), [()], ([)])

#### Step 4: 各対象の削除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | kshooteditor.hsp | `kshooteditor.hsp` | 13040-13082行目: TARGET_POS(BPM/拍子/コメント削除) |
| 4-2 | kshooteditor.hsp | `kshooteditor.hsp` | 13083-13107行目: TARGET_NOTE(チップBT削除) |
| 4-3 | kshooteditor.hsp | `kshooteditor.hsp` | 13108-13134行目: TARGET_NOTE2(ロングBT削除) |
| 4-4 | kshooteditor.hsp | `kshooteditor.hsp` | 13135-13180行目: TARGET_LNOTE(ロングFX削除/FXパラメータ変更) |
| 4-5 | kshooteditor.hsp | `kshooteditor.hsp` | 13181-13205行目: TARGET_LNOTE2(チップFX削除) |
| 4-6 | kshooteditor.hsp | `kshooteditor.hsp` | 13206-13220行目: TARGET_ROTATE(Tilt削除) |
| 4-7 | kshooteditor.hsp | `kshooteditor.hsp` | 13221-13250行目: TARGET_STOP(STOP削除) |

#### Step 5: Pointツールでの選択処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | kshooteditor.hsp | `kshooteditor.hsp` | 10782-10834行目: TARGET_ANALOGL/R選択(Ctrl:追加選択、Shift:範囲選択) |
| 5-2 | kshooteditor.hsp | `kshooteditor.hsp` | 10835-10886行目: TARGET_SPIN選択 |
| 5-3 | kshooteditor.hsp | `kshooteditor.hsp` | 10887-10938行目: TARGET_STOP選択 |

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

```
マウスクリックイベント
    │
    ├─ 配置処理 (iparam=0 & MODE_PENCIL) | (iparam=3 & MODE_ERASE & TARGET_NOTE)
    │   ├─ TARGET_POS
    │   │   ├─ _input (BPM/拍子/コメント ダイアログ)
    │   │   └─ *undokakuho → 配列挿入
    │   ├─ TARGET_NOTE/LNOTE2
    │   │   ├─ *undokakuho
    │   │   └─ note配列にソート挿入
    │   ├─ TARGET_NOTE2/LNOTE/STOP (ドラッグ対応)
    │   │   ├─ *undokakuho
    │   │   ├─ note/stp配列にソート挿入
    │   │   ├─ oncmd *OnClick2 (マウスアップ登録)
    │   │   ├─ ドラッグループ (await 0 + *draw)
    │   │   └─ 重複解消処理 (4パターン)
    │   ├─ TARGET_ANALOGL/R (ドラッグ対応)
    │   │   ├─ *undokakuho
    │   │   ├─ analog配列にソート挿入
    │   │   ├─ ドラッグループ (X座標更新)
    │   │   └─ 重複解消 + 接続処理
    │   ├─ TARGET_SPIN
    │   │   ├─ *undokakuho
    │   │   └─ spin配列にソート挿入
    │   └─ TARGET_ROTATE/ZOOM
    │       ├─ _input (ダイアログ)
    │       └─ *undokakuho → 配列挿入
    │
    └─ 削除処理 (iparam=3 & MODE_PENCIL) | (iparam=0 & MODE_ERASE)
        ├─ *undokakuho
        └─ 配列から要素削除 (後方シフト or ardel)
```

### データフロー図

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

マウスクリック位置        ──▶  クオンタイズ(cc/cbtn/clbtn) ──▶  配置位置決定
(mousex, mousey)               (editdiv基準)

ダイアログ入力           ──▶  BPM/拍子/Tilt/Zoom等        ──▶  bpml/beatl/tilt/ztop等配列
(BPM値/拍子/Tilt種別等)        値の検証                         更新

マウスドラッグ           ──▶  ロングノート/STOP長          ──▶  note.2/stp.1
(開始位置→終了位置)            (cc2 - cc)                       長さ更新

                                重複解消処理                ──▶  既存ノートとのマージ/削除
                                (4パターン判定)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| kshooteditor.hsp | `kshooteditor.hsp` | ソース | 配置処理(10942-12900行目)、削除処理(13038-13250行目)、Pointツール選択(10782-10938行目) |
