feat(labs): wave 3 — 5 new sims + optics merger

Оптическая скамья (opticsbench) — merger thinlens + mirror + refraction
- 4 режима: «Свободная сборка» / «Линза» / «Зеркало» / «Преломление»
- Все 3 движка слиты в OpticsBenchSim (1583 строк)
- Backward compat: #thinlens / #mirrors / #refraction → #opticsbench
- Удалены: thinlens.js, mirror.js, refraction.js

Радиоактивный распад (radioactive) — новая сима
- Monte-Carlo распад: λ·dt вероятность на тик, частицы меняют цвет, эмитируются α/β/γ
- Real-time N(t) график с теоретической кривой N₀·exp(-λt)
- 7 изотопов: ¹⁴C, ¹³¹I, ¹³⁷Cs, ²²⁶Ra, ⁴⁰K, ²³⁸U-chain, ²³⁵U-chain
- Цепочки распадов (U-238: 14 шагов сокращены до 5 ключевых)
- Dating mode для C-14: t = ln(N₀/N)/λ
- HUD: периодов прошло, % распалось, активность в Бк

Тепловые двигатели (heatengine) — новая сима
- 4 цикла: Карно / Отто / Дизель / Брайтон
- PV-диаграмма с замкнутым циклом, заполненной площадью работы
- Аналитически точные изотермы (PV=nRT) и адиабаты (PV^γ=const)
- Анимированный поршень с резервуарами (красный T_h / синий T_c)
- Частицы газа, скорость ∝ √T
- Hover-tooltips с формулами для каждого сегмента

Логические схемы (logic) — новая сима для информатики
- Drag-drop конструктор: 12 типов компонентов (INPUT/CLOCK/OUTPUT/AND/OR/NOT/XOR/NAND/NOR/XNOR/BUF/wire)
- Топологическая сортировка для propagation, цветовая подсветка HIGH/LOW
- Авто-генерация булевого выражения (∧ ∨ ¬ ⊕)
- Авто-таблица истинности (до 2^6 = 64 строк)
- 6 пресетов: полусумматор, полный сумматор, RS-триггер, D-триггер, декодер 2-в-4, мультиплексор 2-в-1

Стехиометрия (stoichiometry) — новая сима
- 10 реакций: Zn+HCl, H₂+O₂, CH₄+O₂, N₂+H₂ (Габер), Al+CuSO₄, Mg+O₂, CaCO₃→, HCl+NaOH, KMnO₄→, C₂H₅OH+O₂
- Sliders с переключением m/n/V (для газов V=n·22.4 при н.у.)
- Анимация частиц при реакции, подсветка лимитирующего реагента
- Пошаговый расчёт m→n→n_product→m_product с KaTeX
- HUD: лимит, избытки, теоретический выход

Каталог: 33 → 35 сим (5 новых − 3 удалённых merger)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-23 13:25:16 +03:00
parent 8f30a8cef6
commit 8b3159b529
13 changed files with 5347 additions and 2232 deletions
+79 -11
View File
@@ -22,9 +22,7 @@
var photosynSim = null;
var quadSim = null;
var eqSim = null;
var lensSim = null;
var titrSim = null;
var refrSim = null;
var probSim = null;
var bohrSim = null;
var elecSim = null;
@@ -34,17 +32,17 @@
var ALL_SIM_BODIES = ['sim-graph','sim-proj','sim-coll','sim-tri','sim-trigcircle','sim-emfield',
'sim-molphys',
'sim-circuit','sim-chemistry','sim-dynamics',
'sim-crystal','sim-orbitals','sim-stereo','sim-chemsandbox',
'sim-crystal','sim-orbitals','sim-stereo','sim-chemsandbox','sim-stoichiometry',
'sim-celldivision','sim-photosynthesis','sim-angrybirds',
'sim-quadratic','sim-normaldist','sim-graphtransform',
'sim-pendulum','sim-equilibrium','sim-thinlens','sim-titration',
'sim-refraction','sim-mirrors','sim-isoprocess','sim-probability','sim-bohratom','sim-electrolysis',
'sim-waves','sim-hydro','sim-geometry'];
'sim-pendulum','sim-equilibrium','sim-opticsbench','sim-titration',
'sim-isoprocess','sim-probability','sim-bohratom','sim-electrolysis',
'sim-waves','sim-hydro','sim-radioactive','sim-geometry','sim-heatengine','sim-logic'];
var ALL_CTRL_BARS = ['ctrl-graph','ctrl-proj','ctrl-coll','ctrl-tri','ctrl-trigcircle','ctrl-emfield',
'ctrl-molphys',
'ctrl-circuit','ctrl-chemistry','ctrl-dynamics','ctrl-chemsandbox',
'ctrl-celldivision','ctrl-photosynthesis','ctrl-angrybirds','ctrl-waves','ctrl-hydro',
'ctrl-geometry'];
'ctrl-radioactive','ctrl-geometry'];
/* ── sim routing ── */
@@ -88,18 +86,24 @@
if (id === 'graphtransform') _openGraphTransform();
if (id === 'pendulum') _openPendulum();
if (id === 'equilibrium') _openEquilibrium();
if (id === 'thinlens') _openThinLens();
if (id === 'mirrors') _openMirror();
if (id === 'opticsbench') _openOpticsBench('lens');
if (id.startsWith('opticsbench:')) _openOpticsBench(id.split(':')[1]);
if (id === 'thinlens') _openOpticsBench('lens'); // backward compat
if (id === 'mirrors') _openOpticsBench('mirror'); // backward compat
if (id === 'refraction') _openOpticsBench('refraction'); // backward compat
if (id === 'isoprocess') _openIsoprocess();
if (id === 'titration') _openTitration();
if (id === 'refraction') _openRefraction();
if (id === 'probability') _openProbability();
if (id === 'bohratom') _openBohrAtom();
if (id === 'electrolysis') _openElectrolysis();
if (id === 'waves') _openWaves();
if (id === 'hydrostatics') _openHydro();
if (id.startsWith('hydrostatics:')) _openHydro(id.split(':')[1]);
if (id === 'radioactive') _openRadioactive();
if (id === 'geometry') _openGeometry();
if (id === 'logic') _openLogic();
if (id === 'heatengine') _openHeatEngine();
if (id === 'stoichiometry') _openStoich();
}
function _simShow(elId) {
@@ -181,7 +185,11 @@
if (probSim) probSim.stop();
if (bohrSim) bohrSim.stop();
if (elecSim) elecSim.stop();
if (wavesSim) wavesSim.stop();
if (wavesSim) wavesSim.stop();
if (radioactiveSim) radioactiveSim.stop();
if (heSim) heSim.stop();
if (mirrorSim && mirrorSim._playing) mirrorSim._stopAnim();
if (mirrorSim && mirrorSim._photonRaf) mirrorSim._stopPhotons();
// tSim, csSim, quadSim, ndSim, gtSim, lensSim, refrSim have no animation loops — nothing to stop
document.getElementById('stereo-stats').style.display = 'none';
document.getElementById('lab-sim').classList.remove('open');
@@ -452,6 +460,17 @@
{ head: 'Окислительное фосфорилирование', text: 'Электрон-транспортная цепь на внутренней мембране митохондрий. Основной выход АТФ (~34).' },
]
},
stoichiometry: {
title: 'Стехиометрия',
sections: [
{ head: 'Молярная масса', formula: 'M = \\frac{m}{n}', vars: [['m','масса вещества, г'],['n','количество моль'],['M','молярная масса, г/моль']] },
{ head: 'Количество вещества', formula: 'n = \\frac{m}{M}', text: 'Основная формула перехода от массы к молям.' },
{ head: 'Стехиометрический расчёт', formula: 'n_2 = \\frac{b}{a} \\cdot n_1', vars: [['a','коэффициент реагента'],['b','коэффициент продукта'],['n₁','моли реагента'],['n₂','моли продукта']] },
{ head: 'Объём газа (н.у.)', formula: 'V = n \\cdot 22{,}4\\text{ л/моль}', text: 'При нормальных условиях (0°C, 101.3 кПа) 1 моль любого газа занимает 22.4 л.' },
{ head: 'Лимитирующий реагент', text: 'Для каждого реагента вычислить n_i / a_i. Наименьшее значение — лимитирующий реагент (определяет выход).' },
{ head: 'Избыток реагента', formula: 'n_{\\text{изб}} = n_{\\text{дано}} - n_{\\text{израсх}}', text: 'После реакции остаётся непрореагировавший избыток нелимитирующего реагента.' },
]
},
chemsandbox: {
title: 'Химическая песочница',
sections: [
@@ -482,6 +501,17 @@
{ head: 'Энергия активации', formula: 'k = A \\cdot e^{-E_a / RT}', text: 'Уравнение Аррениуса. Чем ниже Ea, тем быстрее реакция.' },
]
},
opticsbench: {
title: 'Оптическая скамья',
sections: [
{ head: 'Формула тонкой линзы', formula: '\\frac{1}{f} = \\frac{1}{d} + \\frac{1}{d\'}', vars: [['f','фокусное расстояние'],['d','расстояние до предмета'],["d'",'расстояние до изображения']] },
{ head: 'Увеличение', formula: 'M = -\\frac{d\'}{d} = \\frac{h\'}{h}', text: '|M| > 1 — увеличенное, |M| < 1 — уменьшенное. M < 0 — перевёрнутое.' },
{ head: 'Формула зеркала', formula: '\\frac{1}{f} = \\frac{1}{d} + \\frac{1}{d\'}', text: 'Аналогична линзе. Вогнутое: f > 0, выпуклое: f < 0. Плоское: f = ∞.' },
{ head: 'Закон Снеллиуса', formula: 'n_1 \\sin\\theta_1 = n_2 \\sin\\theta_2', text: 'Угол преломления зависит от соотношения показателей преломления двух сред.' },
{ head: 'Полное внутреннее отражение', formula: '\\theta_c = \\arcsin\\frac{n_2}{n_1}', text: 'При n₁ > n₂ и θ₁ > θc — свет полностью отражается.' },
{ head: 'Показатель преломления', formula: 'n = \\frac{c}{v}', text: 'Воздух ≈ 1.00, вода = 1.33, стекло ≈ 1.5, алмаз = 2.42.' },
]
},
thinlens: {
title: 'Тонкая линза',
sections: [
@@ -564,6 +594,20 @@
{ head: 'Электролит CuSO₄', text: 'Катод: Cu²⁺ + 2e⁻ <svg class="ic" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg> Cu<svg class="ic" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg> (осадок). Анод: 2H₂O 4e⁻ <svg class="ic" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg> O₂<svg class="ic" viewBox="0 0 24 24"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg> + 4H⁺.' },
]
},
logic: {
title: 'Логические схемы',
sections: [
{ head: 'Конъюнкция (AND)', formula: 'A \land B', text: 'Истина, когда оба операнда истинны. Таблица: 0∆0, 0∆0, 1∆0, 1∆1 = 0,0,0,1.' },
{ head: 'Дизъюнкция (OR)', formula: 'A \lor B', text: 'Истина, когда хотя бы один операнд истинен. Результат 0 только при A=B=0.' },
{ head: 'Инверсия (NOT)', formula: '\lnot A', text: 'Меняет значение: NOT 0 = 1, NOT 1 = 0.' },
{ head: 'XOR', formula: 'A \oplus B', text: 'Исключающее ИЛИ: истина, когда операнды различны. A XOR A = 0 всегда.' },
{ head: 'NAND / NOR', text: 'NAND = NOT(AND). NOR = NOT(OR). Функционально полные базисы — любую схему можно собрать только на NAND-вентилях.' },
{ head: 'Полусумматор', formula: 'S = A \oplus B,\; C = A \land B', text: 'Складывает два бита. S — сумма, C — перенос.' },
{ head: 'Полный сумматор', text: 'Три входа: A, B, Cin. Выходы: S = A⊕B⊕Cin, Cout = (A∧B) (Cin∧(A⊕B)).' },
{ head: 'RS-триггер', text: 'Два перекрёстных NOR-вентиля. S=1: Q→1. R=1: Q→0. S=R=0: состояние хранится. S=R=1: запрещено.' },
{ head: 'D-триггер', text: 'Q = D при CLK=1 (прозрачный режим). При CLK=0 состояние хранится.' },
]
},
waves: {
title: 'Волны и звук',
sections: [
@@ -575,6 +619,30 @@
{ head: 'Биения', text: 'Если f\u2081 \u2260 f\u2082, результирующая амплитуда периодически меняется с частотой |f\u2081\u2212f\u2082|. Применяется в акустике для настройки инструментов.' },
]
},
radioactive: {
title: 'Радиоактивный распад',
sections: [
{ head: 'Закон радиоактивного распада', formula: 'N(t) = N_0 \cdot e^{-\lambda t}', vars: [['N_0','начальное число ядер'],['\lambda','постоянная распада (с⁻¹)'],['t','время']] },
{ head: 'Период полураспада', formula: 'T_{1/2} = rac{\ln 2}{\lambda}', text: 'Время, за которое распадается половина ядер.' },
{ head: 'Активность', formula: 'A = \lambda N = rac{\ln 2}{T_{1/2}} N', text: 'Число распадов в единицу времени. Единица — беккерель (Бк = 1 распад/с).' },
{ head: 'Радиоуглеродное датирование', formula: 't = rac{\ln(N_0 / N)}{\lambda}', text: 'По остаточной активности ¹⁴C определяется возраст органического образца (до ~50 000 лет).' },
{ head: 'Цепочки распадов', text: '²³⁸U → ²³⁴Th → ... → ²⁰⁶Pb (14 шагов). В симуляции используются 4-5 основных нуклидов цепочки.' },
{ head: 'Типы распадов', text: 'α-распад: ядро теряет ⁴He (масса -4, заряд -2). β-распад: нейтрон → протон + e⁻ + ν̅. γ-излучение: энергетический переход без изменения нуклидов.' },
]
},
heatengine: {
title: 'Тепловые двигатели',
sections: [
{ head: 'Первое начало термодинамики', formula: 'Q = \\Delta U + W', text: 'Теплота Q идёт на изменение внутренней энергии ΔU и совершение работы W.' },
{ head: 'КПД цикла Карно', formula: '\\eta = 1 - \\frac{T_c}{T_h}', text: 'Максимальный КПД теплового двигателя. Не зависит от рабочего тела, только от температур резервуаров.' },
{ head: 'Связь теплот и работы', formula: 'W = Q_h - |Q_c|,\\quad \\eta = \\frac{W}{Q_h}', vars: [['Q_h','теплота от горячего резервуара'],['Q_c','теплота, отданная холодному'],['W','работа за цикл']] },
{ head: 'Изотермический процесс', formula: 'W = nRT\\ln\\frac{V_2}{V_1}', text: 'T = const, ΔU = 0, Q = W. Рабочее тело в тепловом контакте с резервуаром.' },
{ head: 'Адиабатический процесс', formula: 'PV^\\gamma = \\text{const},\\quad W = \\frac{P_1V_1 - P_2V_2}{\\gamma - 1}', text: 'Q = 0 — нет теплообмена. γ = 1.4 для двухатомного идеального газа.' },
{ head: 'Цикл Отто (ДВС)', formula: '\\eta_{\\text{Отто}} = 1 - r^{1-\\gamma}', vars: [['r','степень сжатия'],['\\gamma = 1.4','показатель адиабаты']], text: '2 адиабаты + 2 изохоры. Типичная η ≈ 25–40%.' },
{ head: 'Цикл Дизеля', text: '2 адиабаты + 1 изобара + 1 изохора. Более высокая степень сжатия, чем Отто. η ≈ 3545%.' },
{ head: 'Цикл Брайтона (ГТД)', text: '2 адиабаты + 2 изобары. Основа авиадвигателей и газовых турбин. η зависит от степени повышения давления.' },
]
},
};
/* ══════════════════════════════════════════════