Files
Learn_System/plans/sim-builder/phase-1-plots-interactions.md
T

7.8 KiB
Raw Blame History

Phase 1: Графики + интеракции

Status: Implemented (не закоммичено — коммит за оркестратором) Parent plan: PLAN.md Domain: frontend

Objective

Добавить в рантайм графики (plot-объекты), перетаскиваемые ручки (drag → параметр), векторы и числовые readout. После фазы спека со слайдером, draggable-точкой и live-графиком работает.

Tasks

  • Plot-объект в спеке: { type:'plot', expr:'...', var:'x', range:[a,b], samples, trace? } — рисует график выражения; trace:true — накапливает след по t. Решение: рисуем на canvas движка в мир-координатах (НЕ тянем GraphPanelUI — он stacked time-series в фикс. оверлее, не y=f(x) инлайн). samples деф. 200, клампится 2..2000.
  • Draggable-ручка: point/circle с drag:{ param, axis:'x|y|xy', min,max, paramY? } — перетаскивание мышью/тачем (pointer events) меняет параметр(ы); позиция ручки следует за параметром (x/y объекта = тот же параметр). Хит-тест в экранных px (допуск 16px), приоритет ручек. Clamp по drag.min/max И по диапазону самого параметра.
  • Readout: { type:'readout', label, expr, unit, precision, x?, y? } — живой бейдж; мягкая ошибка через evalSafe (NaN/синтаксис → «—», не роняет цикл). Без позиции — авто-столбик в верх-правом углу.
  • Vector-объект с привязкой к origin:[ox,oy] + dx/dy-выражениям + стрелка (x1/y1/x2/y2 тоже поддержаны). Стрелка уже была в Ф0.
  • Тач-поддержка drag (pointer events + touchAction:none), не ломая логику лабы (слушатели только на canvas движка, снимаются в destroy).
  • Обновить демо-спеку: +слайдеры x0/y0, draggable-старт (axis xy), plot траектории y(x), 2 readout (дальность R, высота H).

Files to Modify/Create

  • frontend/js/labs/_sim_engine.js — типы plot/readout/vector, drag-интеракции (modify)
  • frontend/js/labs/_sim_demo.js — расширить демо (modify)

Acceptance Criteria

  • Перетаскивание ручки меняет параметр; зависимые объекты/график обновляются.
  • График строится по выражению; trace накапливает след во времени.
  • Readout показывает живое значение. Тач работает.

Notes

  • Drag не должен конфликтовать с pan/zoom рантайма (если есть). Приоритет хит-теста — ручки.
  • Сэмплинг графика разумный (без фриза на больших range).

Review Checklist

  • Все задачи выполнены
  • Drag работает мышью и тачем (pointer events; headless-тест clamp/sync OK)
  • Нет регрессий рантайма Ф0 (рендер всех 8 типов демо × 6 кадров без ошибок; point/segment/circle/rect/polyline/path/vector/label не тронуты в логике)
  • Нет эмодзи, стиль проекта (скан кодпойнтов — чисто; IIFE-стиль)

Handoff to Next Phase

Что готово (Phase 1) — только _sim_engine.js + _sim_demo.js

  • plot — график выражения f(var) на отрезке, рисуется на canvas движка в мир-координатах.
  • dragpoint/circle становятся ручками; pointer events (мышь+тач); clamp двойной (drag.min/max + диапазон параметра).
  • readout — живой бейдж на оверлее (тот же _labelLayer), мягкая ошибка через SimExpr.evalSafe.
  • vector — добавлена форма origin:[ox,oy]+dx/dy (конец = origin + (dx,dy)); старая x1/y1/x2/y2 сохранена; стрелка из Ф0.

Формат новых типов спеки (точные поля)

// график выражения (мир-координаты)
{ type:'plot', expr:'sin(x)', var:'x',          // var деф. 'x'
  range:[a,b],                                   // числа/выражения; деф. xmin..xmax
  samples?:200,                                  // клампится 2..2000
  trace?:false,                                  // true: точка (var=t) пишется в trail по времени;
                                                 //   при trace без range статич. кривая НЕ рисуется
  color?, width? }

// перетаскиваемая ручка (на point/circle)
{ type:'point', x:'x0', y:'y0',
  drag:{ param:'x0',                             // axis x|y -> этот параметр; xy -> X
         axis:'x'|'y'|'xy',                      // деф. 'x'
         paramY:'y0',                            // ТОЛЬКО axis:'xy' -> Y (обязателен для 2D)
         min?, max? } }                          // деф. ±Infinity; доп. clamp по диапазону параметра

// живой числовой бейдж
{ type:'readout', expr:'...', label?:'R', unit?:'м', precision?:2,  // precision 0..8, деф.2
  x?, y?,                                          // мир-коорд.; без них — авто-столбик верх-право
  color? }

// вектор (новая форма)
{ type:'vector', origin:[ox,oy], dx:'...', dy:'...', color?, width? }

API инстанса (без изменений сигнатуры)

mount(host,spec) -> { play, pause, reset, setParam, getParam, isRunning, destroy, el }. Добавлены внутр.: _toWorld(px,py), _setupDrag, _applyDrag, _setParamClamped, _drawPlot, _drawReadout, _accumPlotTrace, _paramRange.

Осталось / риски / на Фазу 2 (физика)

  • Однопроходный env (obj.x/obj.y): из Ф0 — взаимные «вперёд»-ссылки дают значение прошлого кадра. Не трогал; при физике может потребоваться топосорт.
  • Drag только point/circle. Тащить за конец вектора/вершину polyline — не реализовано (не требовалось).
  • readout позиционирование на canvas — через DOM-оверлей (_labelLayer), как label. На сервере (Ф3) label/unit readout надо санитизировать как текст.
  • plot и trace на больших range: ограничены samples<=2000 и trail <=2000 точек — без фриза. Очень большие range с тонкой кривой при экстремальном zoom могут ступенчатить — норм для учебных сцен.
  • Физика (body/physics) — Фаза 2. Plot/drag/readout/vector полностью совместимы с физ-объектами (drag может задавать начальные условия, readout — читать body-величины, если их положить в env в Ф2).
  • lab.html и lab-glue.js НЕ трогались (зона параллельной сессии). Новых файлов не создавал — всё в _sim_engine.js/_sim_demo.js.