Commit Graph

109 Commits

Author SHA1 Message Date
Maxim Dolgolyov 59ea5e7d65 fix(lab-measure): оверлей измерений на весь экран (SVG не растягивался по inset)
#lm-svg — это <svg> (заменяемый элемент с intrinsic 300x150); inset:0 без явных
размеров его не растягивал, поэтому линейка/угол рисовались за пределами видимой
области и казались нерабочими (панель-div при этом видна). Добавлены width:100vw;
height:100vh — оверлей теперь покрывает вьюпорт, инструменты видны и перетаскиваются.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:26:31 +03:00
Maxim Dolgolyov 254f373522 fix(lab-measure): «Скрыть» (×) закрывает панель целиком, а не только оверлей
Кнопка off раньше прятала линейку/угол, но оставляла тулбар на экране —
теперь снимает и bar (полное закрытие, как ожидается от ×).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:17:22 +03:00
Maxim Dolgolyov cd0ce17a60 feat(lab-graph): удобство и красота — скрытие функций, точки, контролы вида, пинч
«График функции», большой апгрейд UX:
- у каждой функции кнопки «глаз» (скрыть/показать, не удаляя) и «очистить»;
  скрытая — приглушена и зачёркнута, исключается из графика/hover/значений
- плавающие контролы вида поверх canvas: зум +/−, сброс вида, тумблер «Особые точки»
- ОСОБЫЕ ТОЧКИ: нули функций, y-перехваты и пересечения кривых — ringed-точки
  с подписью координат (бисекция по смене знака; правка: точные нули на узлах
  сетки больше не теряются; дедуп; подписи скрываются при «частоколе» >22 точек)
- пинч-зум двумя пальцами к центру жеста (к 1-пальцевой панораме)

Движок: setHidden/setShowPoints/_drawPoints/_findZeros/_visible; hover и
инфобар уважают скрытие. Только фронт. node --check OK; zero-finder 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:11:21 +03:00
Maxim Dolgolyov fa29332bcd feat(lab-graph): введённые функции — редактируемое KaTeX-поле
Введённая функция показывается отрисованной формулой KaTeX прямо в строке;
клик по формуле → правка текста на месте (raw input + живое превью под полем),
клик мимо/blur → снова формула. Реализовано без MathQuill: .fn-field держит
<input> и .fn-math (KaTeX), класс has-math переключает отображение по фокусу.

- renderFnMath() рисует формулу в строке; _fnDisplay() решает режим (фокус+значение)
- focus/blur/mousedown-обработчики в _initGraphPanel (идемпотентно)
- живое превью .fn-preview теперь видно ТОЛЬКО при правке (:focus-within), цвет функции
- graphInsert/applyPreset/state-apply/clearAll/default-fn0 обновляют math-поле
- _katexInto() — общий безопасный рендер

Только фронт. node --check OK; логика вставки 5/5 (прошлый прогон).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:02:08 +03:00
Maxim Dolgolyov 000e42f9b3 feat(lab-graph): KaTeX-формулы + панель ввода как редактор формул
«График функции»:
- примеры (чипы) и живой предпросмотр каждой функции рендерятся в KaTeX
  (data-tex на чипах, _initGraphPanel рендерит при открытии)
- предпросмотр теперь всегда виден, крупный и в цвет функции; пустое поле
  показывает плейсхолдер-формулу приглушённо
- НОВОЕ: keypad вставки структур (x², xⁿ, √, a/b, |x|, π, sin/cos/tg/ln/eˣ, ())
  — клик вставляет в активное поле по каретке (как редактор формул в PowerPoint)
- graphInsert(token) с маркером каретки |; активное поле отслеживается по focus

Только фронт. Проверено: node --check, логика вставки 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:56:31 +03:00
Maxim Dolgolyov 4c1ce8394c feat(trigcircle): шкала значений по оси Y на графике (координатная плоскость)
- y-ось графика теперь подписана значениями (KaTeX):
  sin/cos — 1, ½, 0, −½, −1; tg/ctg — 3, 2, 1, 0, −1, −2, −3
- пунктирные линии уровней + подписи слева от панели
- подписи прячутся при смене функции (лишние уровни tg/ctg)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:44:29 +03:00
Maxim Dolgolyov 0640efc82c feat(trigcircle): развёртка угла на графике + KaTeX-подписи граф-вида
- развёртка: участок кривой [0, α] выделяется ярче (с свечением) —
  видно, как угол на окружности «разворачивается» в график
- подпись текущего угла (π/3 и т.п.) на вертикальном маркере, KaTeX
- подписи делений оси X (π/2, π, 3π/2, 2π) — теперь KaTeX-оверлеем
- название функции (y = sin x / cos x / tg x / ctg x) — KaTeX-оверлеем
- _ovLabel: любая LaTeX-команда (\pi, \sin…) теперь рендерится через KaTeX

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:35:28 +03:00
Maxim Dolgolyov 7562d1a77b fix(trigcircle): координатная подпись больше не перекрывает угол
Координатный тултип (cos; sin) выносится радиально НАРУЖУ за точку (вдоль луча от центра),
а не просто со смещением — так KaTeX-плашка значений всегда дальше от центральной дуги угла
и её подписи (π/3 и т.п.), наложения нет.

Verified: node --check; смоук — coord-подпись дальше от центра, чем сама точка.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:08:09 +03:00
Maxim Dolgolyov 1707a510a9 feat(trigcircle): KaTeX-оверлей для подписей на canvas (координаты, значения, угол)
На <canvas> KaTeX не рисуется (fillText), поэтому подписи, которые были юникод-текстом
(√2/2, координаты точки, π/4, значение на графике), переведены на HTML-оверлей #trig-overlay
поверх холста с KaTeX-рендером и точным позиционированием (transform по CSS-px = координаты
canvas). Переведены: координатная подсказка (cos; sin), бейджи значений sin/cos, метка угла
у дуги, бейдж значения на графике. Подписи-слова sin/cos/tg/ctg и мелкие точки табличных
углов остаются на canvas (не математика / 16 мелких меток).

Механика: _ov/_ovLabel/_ovClearUnused — кэш по ключу (ре-рендер только при смене LaTeX),
KaTeX лишь для дробей/корней, простые числа — текстом (быстро при перетаскивании), неис-
пользованные за кадр подписи прячутся. Старые canvas-методы _badge/_tooltip больше не зовутся.

Verified: node --check; headless-смоук оверлея 12/12 (coord/vsin/vcos/angle/gval создаются,
KaTeX-LaTeX для √2/2 и π/4, позиционирование/плашка, десятичные как текст, скрытие при
выкл. слоя/графика). Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:03:39 +03:00
Maxim Dolgolyov 48158ea88d feat(trigcircle): Фаза 5 — чётность/нечётность (−α) + периоды
Тумблер «Чётность (−α)»: на окружности рисуется зеркальная точка −α (отражение через
ось Ox, пунктир P↔−α) — наглядно нечётность sin и чётность cos. Блок-справка на KaTeX
(строится один раз): sin(−α)=−sin α, cos(−α)=cos α, tg(−α)=−tg α, периоды
T_sin=T_cos=2π, T_tg=T_ctg=π. (Формулы приведения для текущего угла — уже Фаза 2.)

Аддитивно: this.showParity + _drawParity + хук в draw(); glue trigToggleParity;
тумблер + #trig-parity в панели.

Verified: node --check; headless-смоук 9/9 (_drawParity без throw для 30/150/210/300;
toggle строит блок один раз с верными тождествами+периодами, показ/скрытие). Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:51:55 +03:00
Maxim Dolgolyov fe6df8fb98 feat(trigcircle): Фаза 4 — таблица значений (особые углы, KaTeX)
Тумблер «Таблица значений» → компактная таблица первой четверти (0/30/45/60/90°)
с точными sin/cos/tg/ctg на KaTeX (строится один раз). Строка опорного острого угла
текущего положения подсвечивается (150° → подсвечена строка 30°). По симметрии/приведению
(Фаза 2) это покрывает все 16 углов.

Glue: _trigBuildValueTable (один раз) + trigToggleTable; подсветка строки в _trigUpdateUI
по refDeg. Панель: тумблер + #trig-table.

Verified: node --check; headless-смоук 10/10 (5 строк, заголовки, значения 30/45/90,
tg «не опр.», toggle показ/скрытие). Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:49:33 +03:00
Maxim Dolgolyov 244df71aec feat(trigcircle): вся математика панели на KaTeX (значения + угол)
Значения sin/cos/tg/ctg в панели и стат-баре теперь рендерятся KaTeX для дробей/корней
(\tfrac{1}{2}, \tfrac{\sqrt{3}}{2}, …), а простые числа (0, ±1, десятичные) — текстом
(быстро при перетаскивании, без лишних KaTeX-вызовов). Бейдж угла — KaTeX π-доли по
таблице 16 углов (150° = 5π/6, 210° = 7π/6, …) + радианы + котерминальные.

Хелперы: _tex (общий рендер с фолбэком), _angleLatex/_piLabelToLatex (рад → LaTeX π-доли),
setMathVal (KaTeX только для нетривиальных форм). Формулы значений/приведения и уравнений
уже были на KaTeX.

Verified: node --check; headless-смоук 9/10 (10-я — артефакт стаба: в реальном DOM
textContent= очищает прежний innerHTML; логика LaTeX верна). Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:47:30 +03:00
Maxim Dolgolyov dfa0535b63 feat(trigcircle): Фаза 6 — простейшие тригонометрические уравнения
Режим уравнения fn(x)=a (sin/cos/tg): окружность подсвечивает ВСЕ решения на [0,2π)
(точки + направляющая линия значения), а панель показывает общую формулу через KaTeX:
  sin x=a → x=(-1)ⁿ·arcsin a + πn;  cos x=a → x=±arccos a + 2πn;  tg x=a → x=arctg a + πn.
Для табличных значений главное значение подставляется точно (arcsin½=π/6 и т.п.), для
нетабличных — символьно (\arcsin a). |a|>1 для sin/cos → «нет решений». Список решений
в градусах. setEquation встаёт на первое решение; clearEquation выходит из режима.

Аддитивно: новое поле this.eq + методы setEquation/clearEquation/_drawEquation + хук в draw();
glue trigSetEqFn/trigSolve/trigClearEq/trigEqKey; секция «Уравнение» в панели labs-bodies.

Verified: node --check; headless-смоук 13/13 (решения sin/cos/tg/один/нет; формулы
(-1)ⁿ/±/+πn/none/нетабличное→arcsin) + изолированная отрисовка _drawEquation без throw.
Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:41:07 +03:00
Maxim Dolgolyov cefb5e0836 feat(trigcircle): рендер формул через KaTeX
Блок «Точные значения · приведение» теперь рендерится KaTeX (katex.renderToString,
как в graph.js/_sim_engine), с фолбэком на сырой LaTeX если katex ещё не загрузился.
Добавлен _latexVal (точное значение → LaTeX: \tfrac{1}{2}, \tfrac{\sqrt{3}}{2}, …),
функции \sin/\cos/\operatorname{tg}/\operatorname{ctg}, цвет наследуется от CSS-цвета
контейнера (без \textcolor). Формула приведения и значения — те же (проверено).

Verified: node --check; headless-смоук LaTeX-вывода (дамп + проверки) — 150°=180°−30°
sin=½ cos=−√3/2 tg=−√3/3; 45° без приведения √2/2; 90° tg «не опр.»; 210°=180°+30°;
300°=360°−60°; 137° нетабличный. Эмодзи нет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:35:25 +03:00
Maxim Dolgolyov 5eed248702 feat(trigcircle): Фаза 2 — точные значения + формулы приведения
При исследовании выяснилось: Пифагор (sin²+cos²=1, _pythBar) и знаки по четвертям
(_quadSigns) уже рисуются на canvas. Поэтому Фаза 2 даёт главное недостающее по программе —
блок «Точные значения · приведение»: для текущего угла показывает sin/cos/tg/ctg точными
значениями (½, √2/2, √3/2, √3/3, √3) и для нетривиальных четвертей — формулу приведения
к острому углу (напр. 150° = 180°−30°, cos 150° = −cos 30° = −√3/2). Нетабличный угол →
сообщение. Без KaTeX (чистый HTML + готовый форматтер _f), без новых зависимостей.

Verified: node --check; headless-смоук рендера 11/11 (150° приведение+знаки, 45° QI без
головы, 210° QIII tg+, 137° нетабличный). Эмодзи нет.

sec/csc (5-я/6-я функции) — вторичны для школьной программы, отложены (предложу опционально).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:30:02 +03:00
Maxim Dolgolyov d395e1083b feat(trigcircle): Фаза 1 — работа с углами + обзор (тренажёр тригонометрии)
План тренажёра в plans/trig-circle/PLAN.md (всё по теме на окружности, кроме графиков функций).
Фаза 1 (аддитивно к рабочему режиму):
- Ввод угла в градусах (поле + Enter/кнопка) → goToAngle (нормализует, показывает
  котерминальность). Подсказка «+360°·k» в бейдже угла.
- Тумблер «График/функции» — скрыть график (тема «функции») → круг на всю ширину
  (переиспользует существующий слой graph + _layout).
- Полная сетка табличных углов (16: 0…330°) вместо 8.
- Опорный (острый) угол к оси Ox в выводе (основа формул приведения) + знаки sin/cos/tg
  по текущей четверти. stats() расширен полями refAngle/refDeg.

Verified: node --check; headless-смоук (vm + canvas-Proxy) 9/9 — опорный угол 30/150/210→30°,
300→60°, 90→90°, 0→0; знаки по четвертям (II: sin+ cos− tg−; IV: sin− cos+); новые
глобальные glue-функции определены. Эмодзи нет (стрелка — inline SVG .ic, tg-неопр. — em-dash).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:23:31 +03:00
Maxim Dolgolyov e9fe4dabb9 fix(stereo): прямой угол (90°) рисуется квадратиком, а не дугой
В инструменте «∠ рёбер» общий рисовальщик _drawAngleArc всегда чертил дугу,
включая случай 90° — должен быть квадратный маркер прямого угла.

- _drawAngleArc: при |angle−90|<0.5° рисует угловой «квадратик» (p1=center+
  n1·r, p3=center+(n1+n2)·r, p2=center+n2·r, r=radius·0.7) вместо дуги.
  Подпись «∠ABC = 90.0°» и лучи угла рисуются отдельно в обработчике —
  не затронуты. Для не-прямых углов поведение прежнее (дуга).

Верификация: node --check OK; headless-смоук 10/10 (90° → 3-точечный квадрат
с верной геометрией в любой плоскости; 89.6° в допуске → квадрат; 60/88/130°
→ дуга; полный поток _onEdgeAngleClick на угле куба → квадрат); эмодзи/eval/
new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:20:09 +03:00
Maxim Dolgolyov ce99c15895 feat(stereo): мастер-тумблер «Фигура» — скрыть тело с поля
Не было способа убрать само тело со сцены. Добавил тумблер «Фигура» в
начале секции «Отображение»: скрывает грани, рёбра, вершины и подписи тела,
оставляя сетку/оси и ВСЕ построения, точки, сечения и выделения — удобно
работать с конструкциями на «пустом» поле.

- StereoSim: флаг showFigure (деф. true) + toggleFigure(v) — переключает
  _figGroup.visible/_labelGroup.visible (флаг переживает _clearGroup, поэтому
  фигура остаётся скрытой и после перестроения при смене параметров). При
  смене типа фигуры (setFigure) тело снова показывается.
- Панель: st-toggle-row #stg-figure; диспетчер stereoToggleSt('figure');
  setStereoFigure возвращает тумблер в «вкл» для новой фигуры.

Верификация: node --check OK; headless-смоук 13/13 (деф. видна; скрытие
прячет fig+labels, но grid/construct/poly/point-группы остаются; перестроение
сохраняет скрытие; обратное включение; setFigure ре-показывает; dispose);
эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:14:53 +03:00
Maxim Dolgolyov 1f461e96fd feat(stereo): выделение цветом — многоугольник по точкам (с палитрой)
Новый инструмент «Многоугольник по точкам» (секция «Выделение цветом»):
кликаешь точки/вершины по контуру → «Замкнуть» (или клик по первой точке)
→ область заливается полупрозрачным цветом + контур + вершины. Палитра из
6 цветов (свотчи), переключается. Можно выделить треугольник/грань/сечение
из выбранных точек, чтобы подсветить «фигуру по точкам».

- StereoSim: _polyMode/_polyPicks/_polyHighlights/_polyColor + _polyGroup;
  setPolyMode (взаимоисключение с другими инструментами), setPolyColor,
  closePoly (≥3 точек), removeLastPolyPick, clearPoly, _onPolyClick
  (авто-замыкание кликом по первой вершине), _rebuildPoly/_drawPolyHighlight/
  _drawPolyPreview (превью: пунктир + крупная 1-я точка-подсказка). Пикинг
  вершин/точек через _pickConstructPoint. Сброс в setFigure, очистка в dispose.
- Панель: секция «Выделение цветом» (кнопка, палитра .st-sw, Замкнуть/
  Отменить точку/Очистить, #poly-hint); glue stereoPolyMode/Color/Close/
  Undo/Clear; интеграция в _stereoDeactivateTools. CSS палитры в lab.css.

Верификация: node --check OK; headless-смоук 21/21 (режим+взаимоисключение,
пик→замыкание, дефолт/выбранный цвет, авто-замыкание по 1-й точке, требование
≥3, undo точки/выделения, clear, setFigure-сброс, dispose, счётчики
fill+контур+вершины); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:02:06 +03:00
Maxim Dolgolyov 5e6effa8cd feat(stereo): тумблер показа длин соединённых отрезков
В инструменте «Соединить» подпись длины у каждого отрезка рисовалась всегда.
Добавил переключатель «Длины отрезков» (секция «Инструменты»): прячет только
подписи длин, сами отрезки и точки остаются.

- StereoSim: флаг showConnectionLengths (деф. true), гард в
  _rebuildPointVisuals, метод toggleConnectionLengths(on). Предпочтение
  переживает смену фигуры (не сбрасывается в setFigure).
- Панель: st-toggle-row #stg-connlen + glue stereoToggleConnLen.

Верификация: node --check OK; headless-смоук 8/8 (деф. вкл, подпись
гейтится флагом, линия/маркеры сохраняются, предпочтение переживает
setFigure); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:52:26 +03:00
Maxim Dolgolyov 601f584181 feat(stereo): сворачиваемый аккордеон панели управления (UX)
Панель за фазы A–C разрослась до ~14 всегда-раскрытых секций (длинный
скролл, тяжело ориентироваться). Сделал её удобнее:

- _stereoInitPanel() (вызов из _openStereo, идемпотентно) оборачивает
  контролы каждой секции в .st-acc-body; заголовки .gp-section-title →
  кликабельные .st-acc-hdr с шевроном; состояние секций в localStorage.
- Тройку фигурных секций (Многогранники/Правильные/Тела вращения) слил в
  одну «Фигуры» (под-метки .st-sublabel). По умолчанию открыты «Фигуры» и
  «Параметры», остальное свёрнуто.
- Кнопки «Развернуть всё / Свернуть всё» (stereoAccAll), клавиатура
  (Enter/Space на заголовке), role=button/tabindex.
- Только раскладка: ни один контрол/обработчик не изменён (узлы лишь
  перемещены в тело секции). Затронуты stereo.js + lab.css.

Верификация: node --check OK; headless DOM-смоук (мини-DOM + реальный
stereo.js в vm) 22/22: 12 сворачиваемых секций, тройка фигур слита (2
под-метки внутри «Фигуры»), пары заголовок→тело, дефолт-открытие,
тоггл+персист, развернуть/свернуть всё, идемпотентная переинициализация,
ни одна строка контролов не потеряна. Эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:48:08 +03:00
Maxim Dolgolyov 9547a20875 feat(stereo): B — умные точки (деление m:n, координаты, перетаскивание)
Фаза B раунда «Конструктор» (умные точки для построений).

B1 — деление отрезка m:n: задаёшь m,n, кликаешь 2 точки A,B → точка делит
AB как AM:MB = m:n (t=m/(m+n)), создаётся как точка-построение M,N,K…
B2 — точка по координатам: поля x/y/z + кнопка → addPointAt.
B3 — перетаскивание построенных точек мышью: drag в плоскости, обращённой
к камере (нормаль фиксируется на старте), приоритет над орбитой; снапшот
истории на старте → undo откатывает весь drag. Непараметрично: downstream-
объекты за перетаскиванием не следуют (параметрический граф — бэклог).

- StereoSim: setDivideMode/setDivideRatio (+ ветка в _onConstructClick),
  addPointAt; setDragPointMode/_pickCPointAt/_beginCPointDrag/_rayPlaneHit/
  _dragCPointWithRay/_dragCPointAt/_endCPointDrag; pointer-хендлеры
  (down=начать drag, move=тащить, up=завершить); сброс в setFigure;
  интеграция в _stereoDeactivateTools.
- Панель: блок «Точки» (кнопки Деление/Тащить, поля m:n, поля x,y,z +
  «Точка (x,y,z)»); glue stereoDivideMode/DivideRatio/AddCoordPoint/
  DragPointMode.

Верификация: node --check OK; headless-смоук 25/25 (деление 1:1/1:2/3:1,
координатная точка + отказ NaN, ray∩plane вкл. parallel/behind, drag begin→
move→end с проверкой позиции и снапшота истории + undo, взаимоисключение
режимов, setFigure-сброс, dispose); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:28:22 +03:00
Maxim Dolgolyov 24403718bf feat(stereo): C1+C3 — плоскость как сечение + «натуральная величина»
Фаза C раунда «Конструктор» (C2 покрыта Фазой A, C4 отложена).

C1 — любую построенную плоскость можно показать сечением тела: клик по
плоскости в дереве (нормальный режим) → setSectionPlane: заливка
многоугольника + подписи вершин K,L,M… + площадь и периметр в readout-
панели. Удаление плоскости / очистка / смена фигуры сбрасывают сечение.

C3 — «Натуральная величина» сечения (getTrueShape): многоугольник сечения
разворачивается в свою плоскость (ортонормированный базис от нормали) с
сохранением истинных длин → 2D-SVG мини-панель со штриховкой (pattern),
подписями вершин, длинами сторон и S/P. Появляется автоматически при
активном сечении.

- StereoSim: _sectionPlaneId, setSectionPlane, _activeSectionPolygon,
  _sectionVertexLabel, getTrueShape; _drawPlaneObject заливает+подписывает
  активное сечение; getReadout добавляет S/P; getConstructions отдаёт
  sectionId + per-plane section; pickConstructObject в нормальном режиме
  тогглит сечение по плоскости.
- Панель: контейнер #construct-trueshape + подсказка; glue
  _stereoUpdateTrueShape (SVG-рендер) вызывается из _stereoUpdateUI; строки
  плоскостей в дереве всегда кликабельны, тег «(сечение)».

Верификация: node --check OK; headless-смоук 26/26 (квадрат y=2: S=16,P=16;
readout/дерево/тоггл; true-shape длины K,L,M,N=4, площадь=16; сохранение
длин и площади для прямого И наклонного сечения; 2D-shoelace=S; удаление/
очистка/setFigure сбрасывают сечение; dispose); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:15:22 +03:00
Maxim Dolgolyov 9382b063aa feat(stereo): A3 — параллели/перпендикуляры + общий undo/redo построений
Фаза A3 раунда «Конструктор». Построения через точку, опираясь на объект:
- lpar: прямая ∥ выбранной прямой;
- lperp: прямая ⟂ выбранной плоскости (вдоль нормали);
- ppar: плоскость ∥ выбранной плоскости;
- pperp: плоскость ⟂ выбранной прямой (= плоскость по точке+нормали,
  через _createPlaneFromPointNormal — мост к Фазе C).
Поток: кнопка op → выбор опоры в дереве → клик точки.

Общий undo/redo конструкторного слоя: JSON-снапшоты _undoStack/_redoStack
(кап 60), хуки _pushHistory в create/remove/clear; Ctrl+Z / Ctrl+Shift+Z /
Ctrl+Y + кнопки «Отменить»/«Вернуть». Видимость объекта — не шаг истории.

- StereoSim: setRelMode/_pickRelRef/_onRelClick/_createPlaneFromPointNormal;
  _snapshot/_pushHistory/_restoreSnapshot/undo/redo/canUndo/canRedo;
  pickConstructObject диспатчит rel/intersect; getConstructions отдаёт
  relMode + selected по опоре; _lastConstructMsg → flash в подсказку.
  Сброс rel/истории в setFigure, очистка в clearConstructions.
- Панель: 4 кнопки (∥/⟂ прямая/плоск.) + «Отменить»/«Вернуть»; интеграция в
  _stereoDeactivateTools; glue stereoRelMode/HistUndo/HistRedo; дерево —
  строки выбираемы и в rel-режиме.

Верификация: node --check OK; headless-смоук 30/30 (4 rel-операции с
проверкой параллельности направлений/нормалей, гард типа опоры, undo/redo
одиночный/многошаговый/redo-сброс/clear-undoable/vis-не-шаг/кап, setFigure-
сброс истории, dispose); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:07:43 +03:00
Maxim Dolgolyov abd1af2653 feat(stereo): A2 — пересечения построений + интерактивное дерево объектов
Фаза A2 раунда «Конструктор». Пересечения как list-based операция:
- прямая ∩ плоскость → точка (_cpoints, имена M,N,K…);
- плоскость ∩ плоскость → прямая;
- прямая ∩ прямая → точка либо «скрещиваются»/«параллельны».
Точки-пересечения пикабельны — по ним строятся новые прямые/плоскости.

- StereoSim: setIntersectMode/pickConstructObject (выбор 2 объектов),
  _computeIntersection + _intersectLinePlane/_intersectPlanePlane/
  _intersectLineLine, _createCPoint/_drawCPointObject/_cpointLabel,
  removeConstruction(id)/toggleConstructionVis(id), getConstructions
  переписан в дерево (id/type/hidden/selected/info), _pickConstructPoint
  теперь учитывает точки-пересечения. Сброс в setFigure, очистка/clear.
- Панель: кнопка «Пересечение»; список — интерактивные строки (выбор для
  пересечения, глаз=видимость, ×=удаление) через glue stereoIntersectMode/
  ConstructSelect/ConstructVis/ConstructDelete; интеграция в _stereoDeactivateTools.

Верификация: node --check OK; headless-смоук 34/34 (точная геометрия
line∩plane / plane∩plane / line∩line, параллельные/скрещ. без объекта,
list-pick поток, гард точки, дерево, видимость/удаление/remove-last,
setFigure-сброс, dispose); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:59:20 +03:00
Maxim Dolgolyov 53ac45bccd feat(stereo): конструкторное ядро A1 — прямые и плоскости как объекты
Фаза A раунда «Конструктор» (под ученика-самоучку). Прямая по 2 точкам
(имена a,b,c…) и плоскость по 3 точкам (имена α,β,γ…) как именованные
объекты сцены. Плоскость рисует полупрозрачный квад + пунктирную рамку +
сечение тела этой плоскостью (через _sliceByPlane) — сразу осмысленна.

- StereoSim: _lines/_planes (сериализуемые {x,y,z}), _constructGroup,
  setLineMode/setPlaneMode, _onConstructClick, _createLine/_createPlane,
  _rebuildConstructions/_drawLineObject/_drawPlaneObject, removeLast/clear,
  getConstructions (с уравнением плоскости). Сброс в setFigure, очистка в
  dispose, перерисовка подписей в toggleLabels, счётчик в info().
- Панель «Построения» в labs-bodies.html + glue (stereoLineMode/PlaneMode/
  ConstructUndo/Clear, _stereoUpdateConstructList); интеграция в
  _stereoDeactivateTools и _stereoUpdateUI.
- План: Фазы A и C в plans/STEREO_3D_IMPROVEMENT.md.

Верификация: node --check OK; headless-смоук 35/35 (создание/имена/нормаль/
коллинеарность/rebuild/summary/remove-last/clear/click-путь/setFigure-сброс/
dispose); эмодзи/eval/new Function — 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:27:27 +03:00
Maxim Dolgolyov 978448d99b @
feat(quantik-game): фаза 3 — граф-уровни (движение по f(x)) + зоны

Новый тип уровня: Квантик едет по кривой y=f(x), которую игрок собирает
слайдерами коэффициентов, проходя сквозь зоны-препятствия. Движок
(аддитивно): plot.runner → env-поля curve.runX/runY/runDone (f компилится
1 раз, питает И кривую, И бегунок-героя, без само-ссылки); type zone
(forbidden/target/collect) → булево env-поле zone.hit. Грамматика
выражений ЗАКРЫТА — никаких inzone()-предикатов, только именованные
env-поля (модель t/tries из Ф0), без eval. Глава-созвездие functions из
5 уровней (луч/синус/парабола/модуль/экспонента), разблокировка 9/11/13/
15/17 (цепочка проходима). validateSpec принимает zone+runner. Все 5
уровней независимо проверены на движке (2★ достижимы). npm test 253/8
baseline; custom-sims 26/26; lint:routes 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-06-13 17:07:33 +03:00
Maxim Dolgolyov 4b5c8077d3 @
feat(quantik-game): фаза 0 — слой целей в движке (goal/HUD/result)

Декларативный блок goal в спеке SimForge (булево SimExpr-условие победы),
вычисляемый каждый кадр: фиксация результата (победа/время/попытки/звёзды),
callback onGoal, HUD-оверлей (цель/звёзды/подсказка/баннер, inline SVG).
API инстанса: onGoal/getResult/resetResult. Серверный validateSpec
пропускает goal/game (длина выражений + escape текста, без исполнения).
Аддитивно: спека без goal ведёт себя как раньше. Смоук 40/40; npm test
238 pass/8 baseline; lint:routes 0. План фичи (7 фаз) + CONTEXT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-06-13 15:13:02 +03:00
Maxim Dolgolyov 69e219ae8c feat(sim-builder): улучшение P3 — графики: несколько кривых, заливка под кривой, маркеры, легенда 2026-06-13 14:26:36 +03:00
Maxim Dolgolyov 222005c0ba feat(sim-builder): улучшение P2 — графика объектов: dash/opacity/градиент/glow, стрелки, стили точек, затухающие трассы, палитра 2026-06-13 14:10:23 +03:00
Maxim Dolgolyov 4be3fbde50 feat(sim-builder): улучшение P1 — рабочее поле: фикс смещения (контролы оверлеем), сетка/оси с делениями, zoom/pan 2026-06-13 13:55:50 +03:00
Maxim Dolgolyov f26b522207 feat(sim-builder): фаза 7 — custom-sim на доске онлайн-урока (синхрон параметров классу, аннотации) 2026-06-13 13:25:24 +03:00
Maxim Dolgolyov cbb6edf372 feat(sim-builder): фаза 6 — раздача классу, клон, шаблоны, привязка к программе (custom_sims) 2026-06-13 13:06:30 +03:00
Maxim Dolgolyov 1bee332ae1 feat(sim-builder): фаза 5 — каталог custom-sims в /lab (LabCustom: ленивая регистрация, секция, deep-link) 2026-06-13 12:48:21 +03:00
Maxim Dolgolyov 572d479f12 feat(sim-builder): фаза 2 — физический интегратор (SimPhysics: гравитация/пружины/столкновения, drag тел) 2026-06-13 11:51:42 +03:00
Maxim Dolgolyov e51b57d9c7 feat(sim-builder): фаза 1 — графики (plot), drag-ручки, readout, векторы origin+dx/dy 2026-06-13 11:30:37 +03:00
Maxim Dolgolyov 4dd92f83a0 feat(sim-builder): фаза 0 — рантайм SimEngine + безопасный движок выражений + адаптер LabRegistry 2026-06-13 11:14:13 +03:00
Maxim Dolgolyov eca68e1a28 feat(labs): Фаза2 — измерительные инструменты (линейка + угломер)
LabMeasure (_measure.js): SVG-оверлей поверх сцены с pointer-events:none
(симуляция остаётся интерактивной), перетаскиваемые ручки. Линейка — длина
px + ≈ метры (PX_PER_M) + угол; угломер — угол при вершине с дугой.
Кнопка-тумблер в топбаре лаборатории. Самодостаточно, симуляции не трогает.
Этим Фаза 2 закрыта.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:13:41 +03:00
Maxim Dolgolyov 51fcb6e4b7 feat(labs): Фаза2 — сохранение/возобновление параметров симуляции
Поверх getState/applyState: в обычном режиме параметры активной симуляции
персистятся в localStorage (lab-sim-state-v1, дедуп, кап 8КБ, flush на pagehide)
и восстанавливаются при открытии. В embed/онлайн-уроке персист выключен —
состоянием управляет учитель. applyState обёрнут в try/catch (старые формы не ломают).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:07:14 +03:00
Maxim Dolgolyov c4ca8bcae7 refactor(labs): Фаза0 фундамент — убрать мёртвый SimUtil, добавить LabPalette + SimBase
- Удалён _util.js (SimUtil): 0 использований во всех симуляциях (проверено),
  грузился впустую.
- LabPalette (_palette.js): единый источник цветов canvas + PX_PER_M вместо
  хардкода в каждом файле; задел под светлую тему.
- SimBase (_simbase.js): опциональная база жизненного цикла (DPR-fit + RAF
  play/pause/reset/destroy). Существующие симуляции не трогаются; «дробовик»
  остаётся fallback. Адаптация — постепенно, по мере правок (нет фронт-тестов).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:52:27 +03:00
Maxim Dolgolyov c0442d6803 feat(labs): задания ещё для 12 симуляций + прогресс плана
LAB_TASKS расширен: waves, circuit, radioactive, heatengine, hydrostatics,
isoprocess, probability, emfield, geometry, photosynthesis, celldivision (+
ранее quadratic/trigcircle/normaldist/projectile/pendulum) — итого 17.
Только валидные single-concept id (мульти-модули molphys/chemistry пропущены).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:44:28 +03:00
Maxim Dolgolyov 15282c50b3 feat(labs): Фаза1 — фреймворк учебных заданий (LabTasks)
Превращает песочницы в учебные инструменты: задание → ответ числом с допуском →
проверка/подсказка/прогресс (по образцу race.js, но переиспользуемо).
- _tasks.js: LabTasks (панель, прогресс-точки, проверка с tol, KaTeX в условии).
- Интеграция в loadTheory (одна точка): панель «Задания» дописывается в теорию,
  бейдж на кнопке теории когда задания есть.
- Данные на 5 симуляций: quadratic, trigcircle, normaldist, projectile, pendulum.
Проверка на клиенте (учебные, не оценочные). XP — отдельным инкрементом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:42:17 +03:00
Maxim Dolgolyov 28db2de74f feat(labs): Фаза0 — эконом-режим FX + выбор симуляции из списка в редакторе
План улучшения симуляций — plans/simulations-improvement/README.md.
- LabFX: reduced-motion/эконом-режим (prefers-reduced-motion + тумблер
  localStorage labfx-economy). Тряска отключается, частицы ×0.25 — доступность
  и экономия на слабых устройствах сразу для всех ~50 симуляций. Кнопка-тумблер
  в lab.html рядом со звуком.
- lesson-editor: блок «Симуляция» — выпадающий список из /api/lab/sims
  (сгруппирован по предметам) вместо сырого ввода simId; неизвестный id не
  теряется, помечается «(не найдена)». Закрывает хрупкую вставку в урок.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:33:50 +03:00
Maxim Dolgolyov 5b103ab606 refactor(lab): превью симуляций вынесены в общий lab-previews.js (единый источник)
~45 SVG-превью (P_*) и хелперы _grid/_axes/_svg вынесены из lab-glue.js в
общий /js/lab-previews.js: window.LabPreviews (карта id→SVG, 40 симуляций) +
window.__LabP (по имени, lab-glue берёт алиасы оттуда). SIMS не тронут.
lab.html подключает lab-previews.js перед lab-glue.js. Теперь дашборд берёт
настоящие превью симуляций из того же источника → «Лаборатория дня» крутит
весь каталог, а не 6 захардкоженных. Дублирование 6 превью устранено.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:34:29 +03:00
Maxim Dolgolyov 6b0d556347 feat(lab-content-engine): phase 5 frontend — чип «Связано с программой»
Реальный фронт Ф5 (ранее ошибочно считал его сделанным параллельной сессией —
его не было). _loadRelated(simId) в lab-glue.js: GET /api/lab/sims/:id/related,
рендерит чипы-ссылки рядом с заголовком симуляции; контейнер #sim-related
создаётся динамически (без правок lab.html/CSS). Вызов из openSim (lab-init.js).
Тихо прячется при отсутствии связей/ошибке. Иконка — inline SVG .ic, без эмодзи.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 17:18:06 +03:00
Maxim Dolgolyov 9754773324 fix(lab-content-engine): браузерные баги Фаз 3-4 + чинка сломанного merge
1. cirSim ReferenceError в _pauseAllSims/closeSim (регрессия Фазы 3): глобалы
   экземпляров симуляций объявлены в ленивых файлах -> не существуют до открытия.
   Предсоздаём их как window-свойства (null) -> guard'ы безопасны. (lab-init.js)
2. theory-data.js (вынос THEORY параллельной сессией) не подключался в lab.html
   -> панель теории и fallback loadTheory ломались. Добавил перед _register-all.
3. _pilots.js удалён в Фазе 1, но lab.html ссылался -> 404. Убрал ссылку.
4. /api/lab/sims 500 на неотмигрированном/устаревшем инстансе -> деградация:
   возвращаем пустой каталог + needs_migration вместо 500. (routes/lab.js)

Проверка: vm-доказательство (_pauseAllSims без throw), node --check всех файлов,
lab-sims тесты 11/11. ВАЖНО: на работающем dev-сервере нужен ПЕРЕЗАПУСК (сервер
не авто-мигрирует) — таблица lab_sims уже в live БД.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:02:30 +03:00
Maxim Dolgolyov 201e94ea81 fix(lab-content-engine): phase 3 - устранён блокер ревью (loader не был подключён)
Два edit'а Фазы 3 не применились в fc1139f (упали по отступу), запушив
сломанное состояние: lab.html убрал eager sim-скрипты, но open остался
синхронным -> ReferenceError при клике на любую симуляцию кроме graph.

ИСПРАВЛЕНО:
- _register-all.js: open-обёртка LabLoader.ensure(id).then(rawOpen) + sync-фолбэк
- lab-init.js openSim: обработка Promise от open() (.then -> lucide, .catch -> log)

E2E vm-harness: click->ensure->load->rawOpen после загрузки; pendulum/stereo:cube/
molphys(4 файла)/alias magnetic — ALL PASS; node --check OK.
Независимое ревью поймало этот блокер.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:06:40 +03:00
Maxim Dolgolyov fc1139f51d feat(lab-content-engine): phase 3 - ленивая загрузка кода симуляций
Старт /lab грузит только каркас (~530KB) вместо ~2.9MB + three.js(~600KB):
- _loader.js — LabLoader.ensure(id): грузит файлы симуляции по манифесту +
  three.js при необходимости; кеш по URL; САМОВОССТАНОВЛЕНИЕ (если open-функция
  не определена после загрузки — грузит все ленивые файлы -> корректность
  гарантирована независимо от точности манифеста)
- _sim_deps.js — сгенерированный манифест SIM_DEPS{id:{open,files,three}} +
  LAB_LAZY_FILES; three:true только для crystal/orbitals/stereo/periodic
- _register-all.js — open-обёртка: LabLoader.ensure(id).then(rawOpen)
- lab-init.js openSim — обработка Promise от open() (lucide после init)
- lab.html — убраны 45 ленивых <script> + three.js из eager; каркас: registry,
  loader, sim_deps, fx-движки, общие визуалы, graph.js (GRID для 15 сим)

Проверка: vm-harness (per-sim load, three only 3D, кеш, self-heal) ALL PASS;
инвариант owner-in-files для всех 40; нет утечки ленивых в eager; node --check OK.
В БРАУЗЕРЕ НЕ ПРОВЕРЕНО.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:02:29 +03:00
Maxim Dolgolyov ebb2a9b37b feat(lab-content-engine): phase 1 - data-driven регистрация всех симуляций
- _register-all.js: строит манифесты из SIMS + THEORY + карта OPEN (40 id),
  регистрирует все симуляции в LabRegistry; LAB_SIM_ALIASES для deep-link
- openSim(): удалена if-цепочка (~60 строк), замена на нормализацию алиасов +
  диспетчеризацию через реестр (early return)
- lab.html: _pilots.js -> _register-all.js (defer, последним)
- _pilots.js удалён (поглощён _register-all.js)

Паритет проверен: исполняемый harness (40 регистраций, dispatch, алиасы,
:arg) ALL PASS; независимое ревью PASS (coverage 40/40, dispatch byte-for-byte).
Lifecycle пока на _pauseAllSims/closeSim (дробовик) — паритет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 13:49:19 +03:00
Maxim Dolgolyov 81d4c15442 feat(opticsbench): учебное построение характеристических лучей
Для «Предмет» + «Характ. лучи» (один предмет, одна линза):
- подписи лучей 1/2/3 у предмета
- точка изображения = пересечение финальных отрезков лучей 1 и 2
- стрелка-изображение (основание на оси → вершина в точке изображения)
- мнимое изображение: пунктирные продления расходящихся лучей назад к
  мнимой точке (слева от линзы); подпись «изображение»/«мнимое изобр.»
- проверено численно: предмет за 2F → реальное справа, внутри F → мнимое слева
- bump opticsbench.js?v=10

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 13:33:46 +03:00