# BSP — `$.world.bsp`

Порядок отрезков «от дальних к ближним» для наклонной геометрии: когда стены
перестают быть регулярной сеткой, сеточный рейкастер не даёт правильного
порядка, а BSP-дерево даёт. Построение дорогое, обход дешёвый — дерево живёт
между кадрами.

```js
$.world.bsp.build($.world.bsp.fromLines(walls));
const order = $.world.bsp.order($.camera.pos());     // [{ index, x1, y1, x2, y2 }]
for (const s of order) $.gfx.draw.line(s.x1, s.y1, s.x2, s.y2, '#8899aa');
$.world.bsp.clear();                                  // в onExit сцены
```

---

## 1. Методы

| Вызов | Смысл |
|---|---|
| `build(segments, opts?)` | построить дерево; принимает массив или `Float32Array` |
| `ready()` / `count()` / `nodes()` / `depth()` | состояние дерева |
| `order(from, opts?)` | порядок от дальних к ближним ( `opts.near` — обратный ) |
| `indices(from, opts?)` | только индексы, без разбора в объекты |
| `segment(index)` | `{ index, x1, y1, x2, y2, tag, split }` |
| `clear()` | освободить дерево |

`from` — `{x, y}`, `[x, y]`, узел, обёртка или селектор.

## 2. Чистые помощники

| Функция | Смысл |
|---|---|
| `segmentsFromFlat(flat)` | плоский массив (stride 4) → список объектов |
| `segmentsToFlat(list, tagOf?)` | список → `Float32Array` (stride 5: четыре координаты и метка) |

`segmentsToFlat` принимает объекты `{x1,y1,x2,y2}`, четвёрки `[x1,y1,x2,y2]` и
пары точек `[[x1,y1],[x2,y2]]` (или `[{x,y},{x,y}]`).

## 3. Ограничения

* **спрайты дерево не упорядочивает**: только отрезки; спрайты сортируются по
  расстоянию через `$.world.sort`;
* **BSP-вставка точек не реализована осознанно** (`src/bsp.h`), спрайты идут
  отдельным списком;
* **дерево не перестраивается само**: при изменении геометрии зовите `build`
  заново (это дорого — делайте это при загрузке сцены, а не в кадре);
* **разрезание порождает новые отрезки**: `count()` может быть больше, чем
  подали на входе; признак `split` в `segment()` показывает порождённые.

## 4. Проверка

```bash
# перевод данных без движка
build/_deps/quickjs-build/qjs tests/js/bsp_test.mjs
# сам обход дерева (C)
./build/tests/r2d_bsp_test
```

На 2026-10-08 `count()` считает только достижимые активные отрезки; исходники,
заменённые разрезами, в счётчик не входят. Оба направления пересечения режутся.
На пределе глубины обход возвращает splitter и весь хвост ровно по одному разу;
точный painter-order внутри неразбитого хвоста не гарантирован. World raycast
проверяет все отрезки такого хвоста, поэтому для его queries это не ограничение.
Индексы сегментов локальны одному build; связывайте данные через `tag`.
