Files
Learn_System/frontend/js/admin/sections/sims.js
T
Maxim Dolgolyov 8b3159b529 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>
2026-05-23 13:25:16 +03:00

120 lines
6.8 KiB
JavaScript

'use strict';
/* admin → sims (simulations) section */
(function () {
'use strict';
let inited = false;
// Full list of available (non-null id) sims mirrored from /lab
const ADMIN_SIMS = [
{ id: 'graph', cat: 'Математика', title: 'График функции' },
{ id: 'graphtransform', cat: 'Математика', title: 'Трансформации графиков' },
{ id: 'geometry', cat: 'Математика', title: 'Планиметрия' },
{ id: 'triangle', cat: 'Математика', title: 'Геометрия треугольника' },
{ id: 'quadratic', cat: 'Математика', title: 'Корни квадратного уравнения' },
{ id: 'stereo', cat: 'Математика', title: 'Стереометрия 3D' },
{ id: 'probability', cat: 'Математика', title: 'Теория вероятностей' },
{ id: 'trigcircle', cat: 'Математика', title: 'Тригонометрическая окружность' },
{ id: 'normaldist', cat: 'Математика', title: 'Нормальное распределение' },
{ id: 'projectile', cat: 'Физика', title: 'Бросок тела' },
{ id: 'pendulum', cat: 'Физика', title: 'Маятник' },
{ id: 'collision', cat: 'Физика', title: 'Столкновение шаров' },
{ id: 'emfield', cat: 'Физика', title: 'Электромагнитные поля' },
{ id: 'circuit', cat: 'Физика', title: 'Электрические цепи' },
{ id: 'hydrostatics', cat: 'Физика', title: 'Гидростатика' },
{ id: 'dynamics', cat: 'Физика', title: 'Динамика' },
{ id: 'opticsbench', cat: 'Физика', title: 'Оптическая скамья' },
{ id: 'isoprocess', cat: 'Физика', title: 'Изопроцессы' },
{ id: 'waves', cat: 'Физика', title: 'Волны и звук' },
{ id: 'heatengine', cat: 'Физика', title: 'Тепловые двигатели' },
{ id: 'radioactive', cat: 'Физика', title: 'Радиоактивный распад' },
{ id: 'logic', cat: 'Физика', title: 'Логические схемы' },
{ id: 'molphys', cat: 'Химия', title: 'Молекулярная физика' },
{ id: 'chemistry', cat: 'Химия', title: 'Химические реакции' },
{ id: 'equilibrium', cat: 'Химия', title: 'Химическое равновесие' },
{ id: 'electrolysis', cat: 'Химия', title: 'Электролиз' },
{ id: 'bohratom', cat: 'Химия', title: 'Атом Бора' },
{ id: 'orbitals', cat: 'Химия', title: 'Молекулярные орбитали' },
{ id: 'titration', cat: 'Химия', title: 'pH и кривая титрования' },
{ id: 'chemsandbox', cat: 'Химия', title: 'Химическая песочница' },
{ id: 'stoichiometry', cat: 'Химия', title: 'Стехиометрия' },
{ id: 'crystal', cat: 'Химия', title: 'Кристаллическая решётка' },
{ id: 'celldivision', cat: 'Биология', title: 'Деление клетки' },
{ id: 'photosynthesis', cat: 'Биология', title: 'Фотосинтез и дыхание' },
{ id: 'angrybirds', cat: 'Игры', title: 'Angry Birds Physics' },
];
let _simsSettings = { module_disabled: false, disabled_ids: [] };
async function load() {
try {
const data = await LS.api('/api/settings/sims');
_simsSettings = data;
_render();
} catch(e) { LS.toast('Ошибка загрузки настроек: ' + e.message, 'error'); }
}
function _render() {
// master toggle
const masterChk = document.getElementById('sims-master-chk');
if (masterChk) masterChk.checked = !_simsSettings.module_disabled;
// per-sim cards
const grid = document.getElementById('sims-grid');
const dis = new Set(_simsSettings.disabled_ids || []);
// group by category
const byCat = {};
ADMIN_SIMS.forEach(s => { (byCat[s.cat] = byCat[s.cat] || []).push(s); });
let html = '';
Object.entries(byCat).forEach(([cat, sims]) => {
html += `<div style="grid-column:1/-1;font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:var(--text-3);margin-top:12px;margin-bottom:2px">${esc(cat)}</div>`;
sims.forEach(s => {
const enabled = !dis.has(s.id);
html += `<div class="perm-card${enabled ? ' enabled' : ''}" id="simcard-${s.id}">
<div class="perm-info">
<div class="perm-label">${esc(s.title)}</div>
<div class="perm-desc" style="font-size:11px;margin-top:2px;opacity:.7">${esc(s.id)}</div>
</div>
<label class="perm-toggle" title="${enabled ? 'Отключить' : 'Включить'}">
<input type="checkbox" ${enabled ? 'checked' : ''} onchange="simToggleOne('${s.id}', this.checked)" />
<span class="perm-track"></span>
<span class="perm-thumb"></span>
</label>
</div>`;
});
});
grid.innerHTML = html;
if (window.lucide) lucide.createIcons();
}
async function simsMasterToggle(checked) {
try {
await LS.api('/api/settings/sims', { method: 'PUT', body: JSON.stringify({ module_disabled: !checked }) });
_simsSettings.module_disabled = !checked;
LS.toast(checked ? 'Модуль симуляций включён' : 'Модуль симуляций отключён', checked ? 'success' : 'warning');
} catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
async function simToggleOne(simId, enabled) {
const dis = new Set(_simsSettings.disabled_ids || []);
if (enabled) dis.delete(simId); else dis.add(simId);
const disabled_ids = [...dis];
try {
await LS.api('/api/settings/sims', { method: 'PUT', body: JSON.stringify({ disabled_ids }) });
_simsSettings.disabled_ids = disabled_ids;
const card = document.getElementById('simcard-' + simId);
if (card) card.classList.toggle('enabled', enabled);
LS.toast(enabled ? ${simId}» включена` : ${simId}» отключена`, enabled ? 'success' : 'warning');
} catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
window.simsMasterToggle = simsMasterToggle;
window.simToggleOne = simToggleOne;
window.AdminSections = window.AdminSections || {};
window.AdminSections.sims = {
init: async () => { if (inited) return; inited = true; await load(); },
reload: load,
};
})();