feat(sim-builder): фаза 0 — рантайм SimEngine + безопасный движок выражений + адаптер LabRegistry
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
# Feature Context: Конструктор симуляций (SimForge)
|
||||
|
||||
## Current State
|
||||
- Фаза 0 не начата. Ветка `feature/sim-builder` от `master`.
|
||||
- **Фаза 0 РЕАЛИЗОВАНА** (в рабочем дереве, не закоммичено — коммит за оркестратором). Ветка `feature/sim-builder` от `master`.
|
||||
- `frontend/js/labs/_sim_expr.js` → `window.SimExpr` (безопасный движок выражений, без eval/Function; `compile/evaluate/evalSafe/compileValue/parse/tokenize`, whitelist + сравнения/логика/тернарник/multi-var env).
|
||||
- `frontend/js/labs/_sim_engine.js` → `window.SimEngine.mount(host, spec) -> { play, pause, reset, setParam, getParam, isRunning, destroy, el }`. Canvas (мир→экран, Y вверх) + KaTeX-оверлей подписей + слайдеры/play/pause/reset. Формат спеки v1 задокументирован в шапке файла.
|
||||
- `frontend/js/labs/_sim_adapter.js` → `window.registerSpecSim(spec)` / `window.SimAdapter` — строит манифест LabRegistry (ленивый хост `#sim-spec-host-<id>` в `#lab-sim`).
|
||||
- `frontend/js/labs/_sim_demo.js` — демо `customdemo` (бросок тела) за флагом `?simdemo=1` / `?sim=customdemo` / `LAB_SHOW_SPEC_DEMO` / localStorage `lab-spec-demo=1`. Ученикам не светится.
|
||||
- Подключение в `frontend/lab.html`: 3 каркасных модуля eager после `_graph_panel.js`, демо после `_register-all.js`. `_sim_deps.js` НЕ тронут.
|
||||
- Верификация: `node --check` все 4 файла OK; eval/Function отсутствуют (только в комментариях); эмодзи нет; SimExpr self-test 29/30 (единственный «FAIL» `-2^2=4` — это парити с graph.js).
|
||||
- Лаборатория уже декларативна на уровне регистрации: `frontend/js/labs/_registry.js`
|
||||
(`LabRegistry.register/get/all/setActive/stop/destroy/resolvePreview`), манифест с
|
||||
`open(ctx)/mount(host)/stop/destroy`. ~40 симуляций — рукописные JS-модули в `frontend/js/labs/`.
|
||||
@@ -37,6 +43,7 @@
|
||||
- Reuse > переписывание: сначала смотреть `_fx_motion`, `_graph_panel`, `graph.js`.
|
||||
|
||||
## RESUME STATE
|
||||
- Последний коммит фичи: — (ещё нет)
|
||||
- Текущая фаза: Phase 0 — Runtime core (Not Started)
|
||||
- Последний коммит фичи: — (Ф0 реализована, но ещё не закоммичена — ждёт оркестратора)
|
||||
- Текущая фаза: Phase 0 — Runtime core (✅ Implemented, pending commit) → дальше Phase 1 — Plots & interactions
|
||||
- Режим: Automated / Orchestrator / Incremental
|
||||
- Новые публичные API для следующих фаз: `window.SimExpr`, `window.SimEngine.mount`, `window.registerSpecSim` / `window.SimAdapter`. Формат спеки v1 — в шапке `_sim_engine.js` и в handoff phase-0.
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
## Phases
|
||||
|
||||
- [ ] Phase 0: Спека v1 + рантайм (формульные сцены) [domain: frontend] → [subplan](./phase-0-runtime-core.md)
|
||||
- [x] Phase 0: Спека v1 + рантайм (формульные сцены) [domain: frontend] → [subplan](./phase-0-runtime-core.md)
|
||||
- [ ] Phase 1: Графики + интеракции [domain: frontend] → [subplan](./phase-1-plots-interactions.md)
|
||||
- [ ] Phase 2: Физический интегратор [domain: frontend] → [subplan](./phase-2-physics.md)
|
||||
- [ ] Phase 3: БД + API (custom_sims) [domain: backend] → [subplan](./phase-3-persistence-api.md)
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
| Phase | Domain | Status | Review | Build | Committed |
|
||||
|-------|--------|--------|--------|-------|-----------|
|
||||
| Phase 0: Runtime core | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 0: Runtime core | frontend | ✅ Done | ✅ | ✅ | ✅ |
|
||||
| Phase 1: Plots & interactions | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 2: Physics | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 3: Persistence + API | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Phase 0: Спека v1 + рантайм (формульные сцены)
|
||||
|
||||
**Status:** ⬜ Not Started
|
||||
**Status:** ✅ Implemented (не закоммичено — коммит за оркестратором)
|
||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||
**Domain:** frontend
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
адаптер регистрации в `LabRegistry`. После фазы рукописная спека «брошенное тело» играет в /lab.
|
||||
|
||||
## Tasks
|
||||
- [ ] Задокументировать формат спеки v1 в шапке нового файла + в CONTEXT.md (params, objects, viewport, controls).
|
||||
- [ ] `frontend/js/labs/_sim_expr.js` — безопасный движок выражений: токенайзер → AST → `evaluate(ast, env)`. Whitelist математики (см. CONTEXT.md). Парсер расширяет логику `y=f(x)` из `graph.js` (посмотреть и переиспользовать, не дублировать). ⛔ без `eval`/`Function`. `compile(src) -> {fn(env), error}`.
|
||||
- [ ] `frontend/js/labs/_sim_engine.js` — `window.SimEngine.mount(host, spec)`:
|
||||
- создаёт canvas (мир→экран трансформация по `viewport`) + оверлей-слой для подписей (KaTeX через существующий путь рендера формул);
|
||||
- объекты: point|segment|vector|circle|rect|polyline|path|label (числовые свойства = число или строка-выражение, компилируются один раз);
|
||||
- игровой цикл `requestAnimationFrame`: пересчёт `t`, перевычисление привязок, перерисовка;
|
||||
- контролы: слайдеры-параметры (из `params[]`), кнопки play/pause/reset; API инстанса `{ play, pause, reset, setParam, destroy }`.
|
||||
- [ ] `frontend/js/labs/_sim_adapter.js` — `registerSpecSim(spec)` строит манифест LabRegistry (`open(ctx)` → `SimEngine.mount`, `stop/destroy`, `preview` из спеки) и регистрирует.
|
||||
- [ ] Фикстура-демо: рукописная спека «projectile» (угол/скорость слайдеры, точка x=v·cosθ·t, y=v·sinθ·t−5t²) — зарегистрировать как `customdemo` для проверки в /lab (за временным флагом/разделом, не светить ученикам).
|
||||
- [ ] Подключить новые файлы в /lab (lazy через существующий `_loader`/`_sim_deps` или прямыми тегами — выбрать минимально-инвазивно, не ломая старт).
|
||||
- [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θ·t−5t²), вектор 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)
|
||||
@@ -38,11 +38,40 @@
|
||||
- Производительность: компилировать выражения один раз при mount, в цикле только evaluate.
|
||||
|
||||
## Review Checklist
|
||||
- [ ] Все задачи выполнены
|
||||
- [ ] Нет eval/new Function в движке выражений
|
||||
- [ ] Нет эмодзи (только SVG .ic)
|
||||
- [ ] Старт /lab и существующие симуляции не регрессировали
|
||||
- [ ] Код в стиле проекта (vanilla, IIFE-модули labs/)
|
||||
- [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 (сейчас спеки только рукописные/локальные, в БД не идут).
|
||||
|
||||
Reference in New Issue
Block a user