Files
Learn_System/plans/sim-builder/phase-0-runtime-core.md
T

78 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 0: Спека v1 + рантайм (формульные сцены)
**Status:** ✅ Implemented (не закоммичено — коммит за оркестратором)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** frontend
## Objective
Заложить ядро: формат JSON-спеки v1, безопасный движок выражений, рантайм `SimEngine`,
адаптер регистрации в `LabRegistry`. После фазы рукописная спека «брошенное тело» играет в /lab.
## Tasks
- [x] Задокументировать формат спеки v1 в шапке нового файла + в CONTEXT.md (params, objects, viewport, controls). → шапка `_sim_engine.js` (полный JSON-формат) + CONTEXT.md.
- [x] `frontend/js/labs/_sim_expr.js` — безопасный движок выражений: токенайзер → AST → `evaluate(ast, env)`. Whitelist математики (см. CONTEXT.md). Парсер расширяет логику `y=f(x)` из `graph.js` (тот же подход к токенам/неявному умножению; добавлены сравнения, логика, тернарник, multi-var env, min/max/mod/log(b,x)). ⛔ без `eval`/`Function`. `compile(src) -> {ast, fn(env), error}`.
- [x] `frontend/js/labs/_sim_engine.js``window.SimEngine.mount(host, spec)`:
- canvas с мир→экран (равные оси, вписан в viewport, Y вверх) + оверлей-слой `<div>` для подписей (KaTeX `renderToString`, как в graph.js);
- объекты: point|segment|vector|circle|rect|polyline|path|label (числовые свойства = число или строка-выражение, компилируются один раз в mount);
- rAF-цикл: `t += dt*speed`, loop/duration, перевычисление привязок, перерисовка, трассы (`trail`);
- контролы: слайдеры из `params[]` + play/pause/reset; API `{ play, pause, reset, setParam, getParam, isRunning, destroy, el }`.
- [x] `frontend/js/labs/_sim_adapter.js``registerSpecSim(spec)` строит манифест LabRegistry (`open(ctx)` → ленивый собственный хост-div + `SimEngine.mount`; `stop` прячет хост+pause; `destroy` уничтожает инстанс; `preview` из спеки или авто-SVG) и регистрирует.
- [x] Фикстура-демо: рукописная спека «projectile» (слайдеры θ 0..90 / v 0..30, точка x=v·cosθ·t, y=max(0, v·sinθ·t5t²), вектор v0, земля, подпись) — зарегистрирована как `customdemo` за флагом `?simdemo=1` / `?sim=customdemo` / `LAB_SHOW_SPEC_DEMO` / localStorage `lab-spec-demo=1`. Ученикам не светится (карточки в SIMS нет; добавляется только при включённом флаге).
- [x] Подключить новые файлы в /lab прямыми `<script>` (минимально-инвазивно): `_sim_expr/_sim_engine/_sim_adapter` — eager после `_graph_panel.js`; `_sim_demo` — после `_register-all.js`. Lazy-схема `_sim_deps` не тронута (каркасные модули должны быть до диспетчера). Старт /lab и ~40 симуляций не затронуты.
## Files to Modify/Create
- `frontend/js/labs/_sim_expr.js` — движок выражений (new)
- `frontend/js/labs/_sim_engine.js` — рантайм (new)
- `frontend/js/labs/_sim_adapter.js` — адаптер LabRegistry (new)
- `frontend/js/labs/_sim_demo.js` — демо-спека-фикстура (new, временная)
- `frontend/lab.html` или `_sim_deps.js` — подключение файлов (минимальная правка)
## Acceptance Criteria
- В /lab открывается демо-симуляция, слайдеры меняют движение, play/pause/reset работают.
- Движок выражений не использует eval/Function; некорректная формула не роняет рантайм (показывает ошибку/0).
- Существующие ~40 симуляций и старт /lab не сломаны.
## Notes
- Подписи с LaTeX — переиспользовать существующий рендер формул (KaTeX), не тянуть новый.
- Мир-координаты с осью Y вверх (математические), трансформация в экранные внутри движка.
- Производительность: компилировать выражения один раз при mount, в цикле только evaluate.
## Review Checklist
- [x] Все задачи выполнены
- [x] Нет eval/new Function в движке выражений (grep -nE "eval\(|new Function|Function\(" → только упоминания в комментариях, ни одного call-site)
- [x] Нет эмодзи (скан: только ASCII + кириллица + θ/∞/box-drawing — те же баннеры, что в graph.js)
- [x] Старт /lab и существующие симуляции не регрессировали (не тронуты SIMS/OPEN/манифесты; добавлены только новые `<script>` + register())
- [x] Код в стиле проекта (vanilla, IIFE с экспортом в window.*, как _registry/_simbase/_graph_panel)
## Handoff to Next Phase
### Что готово (Phase 0)
- **Движок выражений** `frontend/js/labs/_sim_expr.js``window.SimExpr`:
- `compile(src) -> { ast, fn, error }`. `fn(env) -> number`, НИКОГДА не бросает (NaN/∞/деление на 0 → 0). `error` — строка при синтаксической ошибке (не бросается).
- `evaluate(ast, env) -> number` (то же, безопасно), `evalSafe(ast, env) -> { value, error }` (для билдера/отладки — отличает NaN от валидного 0).
- `compileValue(value) -> { fn, error, constant, ast }` — число вернёт константу, строку скомпилирует (используется движком для свойств-привязок).
- `parse(src) -> ast` (бросает при ошибке), `tokenize(src)`, `FUNCTIONS` / `CONSTANTS` (объекты-«множества» имён, для подсветки в билдере).
- Whitelist: `+ - * / ^ %`, унарный `-`/`+`/`!`, скобки, сравнения `< <= > >= == !=`, логика `&& ||`, тернарник `?:`; функции `sin cos tan tg ctg cot asin acos atan arcsin arccos arctan arctg sqrt abs exp ln log(x|base,x) log2 log10 floor ceil round sign min max mod atan2 pow hypot`; константы `pi e tau`. Идентификаторы (вкл. точечные `obj.x`) берутся ТОЛЬКО из `env`.
- ⚠️ Степень право-ассоциативна, базой служит unary → `-2^2 == 4` (паритет с graph.js, задокументировано). Для `-(2^2)` писать скобки.
- Самопроверка: 29/30 логических кейсов PASS (единственный «FAIL» — `-2^2`, это и есть парити-поведение, не баг).
- **Рантайм** `frontend/js/labs/_sim_engine.js``window.SimEngine`:
- `mount(host, spec) -> instance`. `instance`: `play() pause() reset() setParam(name,val) getParam(name) isRunning() destroy()` + поле `el` (корневой DOM для скрытия/показа).
- Сцена: панель слайдеров+play/pause/reset слева, `<canvas>` + оверлей `<div>` подписей справа. Мир→экран: равные оси, центрирование, Y вверх. Выражения компилируются 1 раз в mount; в rAF — только evaluate. `env` = `{ t, <params>, w, h, xmin/xmax/ymin/ymax, <objId>.x, <objId>.y }`.
- Объекты: `point segment vector circle rect polyline path label`. Подписи — KaTeX (фолбэк на текст). Трассы (`trail:true`).
- **Адаптер** `frontend/js/labs/_sim_adapter.js``window.registerSpecSim(spec)` / `window.SimAdapter`:
- Строит манифест LabRegistry из спеки, кладёт `_spec`/`_instance`/`instance()` для будущих фаз. Каждая спек-симуляция получает ленивый хост `#sim-spec-host-<id>` внутри `#lab-sim`. `stop` прячет хост (важно при switch — openSim не знает наших хостов), `destroy` уничтожает инстанс.
- **Демо** `frontend/js/labs/_sim_demo.js``customdemo` за флагом, для приёмки.
- **Подключение** в `frontend/lab.html`: 3 каркасных `<script>` после `_graph_panel.js`, демо после `_register-all.js`.
### Формат спеки v1 (поддержан фактически)
`{ specVersion?, id, cat?, meta:{title,desc}, viewport:{xmin,xmax,ymin,ymax,grid?,axes?,bg?}, time:{autoplay?,loop?,duration?,speed?}, params:[{name,label,min,max,step,value,unit?}], objects:[{id?,type,...числа|строки-выражения, color?,fill?,width?,trail?,trailColor?,latex?,size?,text?,points?,closed?}], theory?, subject?,grade?,topics? }`
Полная спецификация полей по типам объектов — в шапке `_sim_engine.js`.
### Осталось на Фазу 1 / временные решения / риски
- **Drag-интеракции, графики, оси-подписи с числами** — Фаза 1 (сейчас оси без числовых меток, есть сетка). `GraphPanelUI` из `_graph_panel.js` готов к переиспользованию для plots.
- **Физика** (`body`/`physics`) — Фаза 2, объекты сейчас чисто кинематические (формульные).
- **Однопроходный env для `obj.x/obj.y`**: центры объектов вычисляются в порядке объявления за один проход — взаимные ссылки «вперёд» (объект ссылается на тот, что объявлен ниже) дадут значение предыдущего кадра (или 0 на первом). Достаточно для типовых сцен; при необходимости в Ф1 сделать топосортировку/итерации.
- **`r` точки** трактуется как экранный радиус в пикселях (не мир-единицы) — намеренно, чтобы точки не «таяли» при масштабе; для circle радиус мировой.
- **Демо `customdemo`** — временный раздел, удалить/спрятать после билдера (Фаза 4). Карточка добавляется в каталог только при включённом флаге.
- **Санитизация подписей/валидация спеки на сервере** — Фаза 3 (сейчас спеки только рукописные/локальные, в БД не идут).