# Phase 2: Физический интегратор **Status:** ✅ Implemented (не закоммичено — коммит за оркестратором) **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** frontend ## Objective Добавить настоящую физику: тела с массой, гравитация/пружины/столкновения/трение, перетаскивание тел силой, траектории. Динамика считается движком, а не формулой. После фазы маятник/столкновения/брошенное тело идут динамически из спеки. ## Tasks - [x] Блок `physics` в спеке: `{ enabled, gravity:{x,y}, friction, walls:[...], restitution, dt?, springs? }`. gravity/friction/restitution — числа ИЛИ выражения от params (вычисляются на reset). - [x] Тело-объект: `body:{ mass, vx, vy, fixed }` на point/circle — интегрируется фикс-шагом (накопитель dt). Нач. позиция/vx/vy/масса — числа или выражения от params (вычисляются при reset/init, далее интегрируются). Опора на математику `_fx_motion` (полу-неявный Эйлер); см. ниже. - [x] Пружины: `springs:[{ a, b, k, length, damping? }]` — концы: id тела ИЛИ якорь-точка `[x,y]`. Сила Гука + демпфирование вдоль оси. Рисуются зигзагом. - [x] Столкновения: упругие круг-круг (по нормали, импульс + позиционная коррекция по обратным массам) и круг-стена (restitution). Broadphase O(n^2) (N мало). - [x] Drag тела: тащишь — задаёт позицию (тело «приколото», не интегрируется); отпустил — сообщает скорость (бросок, кламп 40 м/с). Формульные объекты Ф0/Ф1 сосуществуют (drag-ручки и физ-тела в одной сцене). - [x] Траектория: тело с `trail:true` пишет след центра (переиспользован существующий механизм trail; позиция берётся из env-полей тела). - [x] Демо-спеки за флагом: «пружинный маятник» (`customphys`: тело+пружина+гравитация+drag) и «упругие шары» (`customballs`: 3 тела + 4 стены, g/упругость от слайдеров). ## Files to Modify/Create - `frontend/js/labs/_sim_engine.js` — физический режим, интеграция с _fx_motion (modify) - `frontend/js/labs/_sim_physics.js` — обёртка интегратора/коллизий, если чище отдельно (new, опц.) - `frontend/js/labs/_sim_demo.js` — физ-демо (modify) ## Acceptance Criteria - Тело под гравитацией падает/летит по параболе через интегратор (не по формуле). - Пружина колеблет груз; шары упруго сталкиваются; стены отражают. - Drag тела работает; формульные объекты Ф0 продолжают работать в той же сцене. ## Notes - Шаг интегратора фиксированный (накопитель dt) для стабильности. - Не переусложнять коллизии — школьный уровень (круги/стены). ## Review Checklist - [x] Все задачи выполнены - [x] Опирается на математику `_fx_motion.spring` (полу-неявный/симплектический Эйлер `v+=a·dt; x+=v·dt`) — обобщена на связанные тела в `SimPhysics`; API tween/spring-фабрики `_fx_motion` (rAF-замыкания на одно значение) не подходит для N связанных тел, поэтому тонкий модуль поверх той же математики, без дубля иного интегратора. - [x] Стабильность (нет взрыва энергии): фикс-шаг dt (кламп 1/2000..1/30), накопитель + кап подшагов (8), кламп скорости (1e4), вязкое трение через `exp(-friction·dt)`. Headless-прогон: падение/маятник/шары — конечные, без NaN/∞. - [x] Нет регрессий Ф0/Ф1: формульные point/segment/circle/rect/polyline/path/vector/label/plot/readout/drag работают; тела и формульные объекты в одной сцене (тест mixed). ## Handoff to Next Phase ### Что реализовано (Phase 2) — только `_sim_engine.js` + `_sim_demo.js` Файл `_sim_physics.js` НЕ создавался: его нельзя подключить без правки `frontend/lab.html` (зона параллельной сессии). Интегратор живёт внутри `_sim_engine.js` и экспортируется как `window.SimPhysics` (переиспользуемо headless/билдером/доской). Если Ф4+ захочет вынести в отдельный файл — добавить `