refactor: distribute lab-init.js into 34 engine files
lab-init.js: 4098 -> 543 lines (infrastructure + THEORY only) Each sim's _open*() + UI helpers moved to its engine file: graph.js, projectile.js, collision.js, magnetic.js, triangle.js, geometry.js, trigcircle.js, gas.js (molphys), coulomb.js, circuit.js, reactions.js (chemistry), newton.js (dynamics), chemsandbox.js, celldivision.js, photosynthesis.js, angrybirds.js, quadratic.js, normaldist.js, graphtransform.js, pendulum.js, equilibrium.js, thinlens.js, mirror.js, isoprocess.js, titration.js, refraction.js, probability.js, bohratom.js, electrolysis.js, waves.js, crystal.js, orbitals.js, stereo.js, hydrostatics.js All 34 engine files syntax-checked OK.
This commit is contained in:
+265
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* GasSim v2 — Ideal Gas simulation (PV=nRT, Maxwell-Boltzmann distribution)
|
||||
* v2: hover inspector, velocity vectors, movable piston, v_mp/v_rms markers.
|
||||
*/
|
||||
@@ -460,3 +460,267 @@ class GasSim {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── lab UI init ─────────────────────────────────── */
|
||||
function _openMolPhys(mode) {
|
||||
document.getElementById('sim-topbar-title').textContent = 'Молекулярная физика';
|
||||
_simShow('sim-molphys');
|
||||
_simShow('ctrl-molphys');
|
||||
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
// lazy-init all sims
|
||||
if (!gasSim) { gasSim = new GasSim(document.getElementById('gas-canvas')); gasSim.onUpdate = _gasUpdateUI; }
|
||||
if (!brownSim) { brownSim = new BrownianSim(document.getElementById('brownian-canvas')); brownSim.onUpdate = _brownUpdateUI; }
|
||||
if (!statesSim) { statesSim = new StatesSim(document.getElementById('states-canvas')); statesSim.onUpdate = _statesUpdateUI; }
|
||||
if (!diffSim) { diffSim = new DiffusionSim(document.getElementById('diffusion-canvas')); diffSim.onUpdate = _diffUpdateUI; }
|
||||
|
||||
molMode(mode || 'gas');
|
||||
}));
|
||||
}
|
||||
|
||||
function molMode(mode, btn) {
|
||||
_molMode = mode;
|
||||
// stop all
|
||||
if (gasSim) gasSim.stop();
|
||||
if (brownSim) brownSim.stop();
|
||||
if (statesSim) statesSim.stop();
|
||||
if (diffSim) diffSim.stop();
|
||||
|
||||
// toggle mode buttons
|
||||
document.querySelectorAll('.mol-mode').forEach(b => b.classList.remove('active'));
|
||||
if (btn) btn.classList.add('active');
|
||||
else { const mb = document.getElementById('mol-mode-' + mode); if (mb) mb.classList.add('active'); }
|
||||
|
||||
// toggle panels
|
||||
const panels = ['gas', 'brownian', 'states', 'diffusion'];
|
||||
panels.forEach(p => {
|
||||
document.getElementById('mol-panel-' + p).style.display = p === mode ? '' : 'none';
|
||||
});
|
||||
|
||||
// toggle canvases
|
||||
document.getElementById('gas-canvas').style.display = mode === 'gas' ? 'block' : 'none';
|
||||
document.getElementById('brownian-canvas').style.display = mode === 'brownian' ? 'block' : 'none';
|
||||
document.getElementById('states-canvas').style.display = mode === 'states' ? 'block' : 'none';
|
||||
document.getElementById('diffusion-canvas').style.display = mode === 'diffusion' ? 'block' : 'none';
|
||||
|
||||
// toggle topbar diffusion partition button
|
||||
document.getElementById('ctrl-mol-diff').style.display = mode === 'diffusion' ? 'contents' : 'none';
|
||||
|
||||
// start active sim
|
||||
const titles = { gas: 'Молекулярная физика — Газ', brownian: 'Молекулярная физика — Броуновское', states: 'Молекулярная физика — Фазы', diffusion: 'Молекулярная физика — Диффузия' };
|
||||
document.getElementById('sim-topbar-title').textContent = titles[mode] || 'Молекулярная физика';
|
||||
|
||||
if (mode === 'gas') { gasSim.fit(); gasSim.start(); }
|
||||
if (mode === 'brownian') { brownSim.fit(); brownSim.start(); }
|
||||
if (mode === 'states') { statesSim.fit(); statesSim.start(); }
|
||||
if (mode === 'diffusion') { diffSim.fit(); diffSim.start(); }
|
||||
}
|
||||
|
||||
function molReset() {
|
||||
if (_molMode === 'gas' && gasSim) {
|
||||
gasSim.reset();
|
||||
document.getElementById('sl-gPiston').value = 100;
|
||||
document.getElementById('g-piston').textContent = '100%';
|
||||
}
|
||||
if (_molMode === 'brownian' && brownSim) brownSim.reset();
|
||||
if (_molMode === 'states' && statesSim) {
|
||||
statesSim.reset();
|
||||
document.getElementById('sl-stN').value = 64;
|
||||
document.getElementById('st-N').textContent = '64';
|
||||
const vBtn = document.getElementById('states-vec-btn');
|
||||
if (vBtn) { vBtn.textContent = 'Векторы скоростей: Выкл'; vBtn.style.color = ''; }
|
||||
}
|
||||
if (_molMode === 'diffusion' && diffSim) {
|
||||
diffSim.reset();
|
||||
document.getElementById('diffusion-part-btn').textContent = '‖ Раздел';
|
||||
document.getElementById('df-part-row').classList.add('active');
|
||||
document.getElementById('df-pore-row').classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function gasNChange() {
|
||||
const n = +document.getElementById('sl-gN').value;
|
||||
document.getElementById('g-N').textContent = n;
|
||||
if (gasSim) { gasSim.setN(n); }
|
||||
}
|
||||
|
||||
function gasTChange() {
|
||||
const raw = +document.getElementById('sl-gT').value;
|
||||
const t = raw / 10;
|
||||
document.getElementById('g-T').textContent = t.toFixed(1) + ' у.е.';
|
||||
if (gasSim) gasSim.setT(t);
|
||||
}
|
||||
|
||||
function gasPistonChange() {
|
||||
const v = +document.getElementById('sl-gPiston').value;
|
||||
document.getElementById('g-piston').textContent = v + '%';
|
||||
if (gasSim) gasSim.setPiston(v / 100);
|
||||
}
|
||||
|
||||
function gasToggleVectors(btn) {
|
||||
if (!gasSim) return;
|
||||
gasSim.toggleVectors();
|
||||
btn.textContent = 'Векторы скоростей: ' + (gasSim._showVectors ? 'Вкл' : 'Выкл');
|
||||
btn.style.color = gasSim._showVectors ? '#7BF5A4' : '';
|
||||
}
|
||||
|
||||
function _gasUpdateUI(info) {
|
||||
document.getElementById('gstat-P').textContent = info.P;
|
||||
document.getElementById('gstat-V').textContent = info.V;
|
||||
document.getElementById('gstat-PV').textContent = info.PV;
|
||||
document.getElementById('gstat-v').textContent = info.avgSpeed + ' у.е.';
|
||||
document.getElementById('mpbar-l1').textContent = 'N';
|
||||
document.getElementById('mpbar-v1').textContent = info.N;
|
||||
document.getElementById('mpbar-l2').textContent = 'T';
|
||||
document.getElementById('mpbar-v2').textContent = info.T.toFixed(1);
|
||||
document.getElementById('mpbar-l3').textContent = 'P';
|
||||
document.getElementById('mpbar-v3').textContent = info.P;
|
||||
document.getElementById('mpbar-l4').textContent = 'V';
|
||||
document.getElementById('mpbar-v4').textContent = info.V;
|
||||
document.getElementById('mpbar-l5').textContent = 'PV';
|
||||
document.getElementById('mpbar-v5').textContent = info.PV;
|
||||
}
|
||||
|
||||
function brownNChange() {
|
||||
const n = +document.getElementById('sl-brN').value;
|
||||
document.getElementById('br-N').textContent = n;
|
||||
if (brownSim) brownSim.setN(n);
|
||||
}
|
||||
|
||||
function brownTChange() {
|
||||
const t = +document.getElementById('sl-brT').value / 10;
|
||||
document.getElementById('br-T').textContent = t.toFixed(1) + ' у.е.';
|
||||
if (brownSim) brownSim.setT(t);
|
||||
}
|
||||
|
||||
function _brownUpdateUI(info) {
|
||||
document.getElementById('brstat-dr').textContent = info.displacement + ' px';
|
||||
document.getElementById('brstat-msd').textContent = info.msd + ' px²';
|
||||
document.getElementById('brstat-v').textContent = info.speed;
|
||||
document.getElementById('brstat-steps').textContent = info.steps;
|
||||
document.getElementById('mpbar-l1').textContent = 'Шагов';
|
||||
document.getElementById('mpbar-v1').textContent = info.steps;
|
||||
document.getElementById('mpbar-l2').textContent = '|Δr|';
|
||||
document.getElementById('mpbar-v2').textContent = info.displacement + ' px';
|
||||
document.getElementById('mpbar-l3').textContent = 'MSD';
|
||||
document.getElementById('mpbar-v3').textContent = info.msd + ' px²';
|
||||
document.getElementById('mpbar-l4').textContent = 'v';
|
||||
document.getElementById('mpbar-v4').textContent = info.speed;
|
||||
document.getElementById('mpbar-l5').textContent = 'N';
|
||||
document.getElementById('mpbar-v5').textContent = info.N;
|
||||
}
|
||||
|
||||
function statesTChange() {
|
||||
const raw = +document.getElementById('sl-stT').value;
|
||||
const t = raw / 100;
|
||||
document.getElementById('st-T').textContent = t.toFixed(2);
|
||||
if (statesSim) statesSim.setT(t);
|
||||
}
|
||||
|
||||
function statesPreset(t) {
|
||||
document.getElementById('sl-stT').value = Math.round(t * 100);
|
||||
document.getElementById('st-T').textContent = t.toFixed(2);
|
||||
if (statesSim) statesSim.setT(t);
|
||||
}
|
||||
|
||||
function statesNChange() {
|
||||
const n = +document.getElementById('sl-stN').value;
|
||||
document.getElementById('st-N').textContent = n;
|
||||
if (statesSim) statesSim.setN(n);
|
||||
}
|
||||
|
||||
function statesToggleVectors(btn) {
|
||||
if (!statesSim) return;
|
||||
statesSim.toggleVectors();
|
||||
btn.textContent = 'Векторы скоростей: ' + (statesSim._showVectors ? 'Вкл' : 'Выкл');
|
||||
btn.style.color = statesSim._showVectors ? '#7BF5A4' : '';
|
||||
}
|
||||
|
||||
function _statesUpdateUI(info) {
|
||||
const phaseColors = { solid: '#4CC9F0', liquid: '#7BF5A4', gas: '#EF476F' };
|
||||
const phaseLabels = { solid: 'Твёрдое', liquid: 'Жидкость', gas: 'Газ' };
|
||||
const c = phaseColors[info.phase] || '#fff';
|
||||
document.getElementById('ststat-phase').textContent = phaseLabels[info.phase] || info.phase;
|
||||
document.getElementById('ststat-phase').style.color = c;
|
||||
document.getElementById('ststat-KE').textContent = info.avgKE;
|
||||
document.getElementById('ststat-PE').textContent = info.avgPE;
|
||||
const pEl = document.getElementById('ststat-P');
|
||||
if (pEl) pEl.textContent = info.P !== undefined ? info.P : '—';
|
||||
document.getElementById('mpbar-l1').textContent = 'Фаза';
|
||||
document.getElementById('mpbar-v1').textContent = phaseLabels[info.phase] || info.phase;
|
||||
document.getElementById('mpbar-v1').style.color = c;
|
||||
document.getElementById('mpbar-l2').textContent = 'T';
|
||||
document.getElementById('mpbar-v2').textContent = info.T.toFixed(2);
|
||||
document.getElementById('mpbar-l3').textContent = 'KE';
|
||||
document.getElementById('mpbar-v3').textContent = info.avgKE;
|
||||
document.getElementById('mpbar-l4').textContent = 'PE';
|
||||
document.getElementById('mpbar-v4').textContent = info.avgPE;
|
||||
document.getElementById('mpbar-l5').textContent = 'P';
|
||||
document.getElementById('mpbar-v5').textContent = info.P !== undefined ? info.P : '—';
|
||||
}
|
||||
|
||||
function diffNChange() {
|
||||
const n = +document.getElementById('sl-dfN').value;
|
||||
document.getElementById('df-N').textContent = n;
|
||||
if (diffSim) diffSim.setN(n);
|
||||
}
|
||||
|
||||
function diffTChange() {
|
||||
const t = +document.getElementById('sl-dfT').value / 10;
|
||||
document.getElementById('df-T').textContent = t.toFixed(1) + ' у.е.';
|
||||
if (diffSim) diffSim.setT(t);
|
||||
}
|
||||
|
||||
function diffPartitionToggle(rowEl) {
|
||||
if (!diffSim) return;
|
||||
diffSim.togglePartition();
|
||||
const on = diffSim.partitionOn;
|
||||
rowEl.classList.toggle('active', on);
|
||||
document.getElementById('diffusion-part-btn').innerHTML = on ? '‖ Раздел' : '<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/></svg> Раздел снят';
|
||||
}
|
||||
|
||||
function diffPartitionBtn() {
|
||||
if (!diffSim) return;
|
||||
const on = diffSim.partitionOn;
|
||||
document.getElementById('diffusion-part-btn').innerHTML = on ? '‖ Раздел' : '<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/></svg> Раздел снят';
|
||||
document.getElementById('df-part-row').classList.toggle('active', on);
|
||||
}
|
||||
|
||||
function diffPoreToggle(rowEl) {
|
||||
if (!diffSim) return;
|
||||
diffSim.togglePore();
|
||||
const pore = diffSim._poreMode;
|
||||
const on = diffSim.partitionOn;
|
||||
rowEl.classList.toggle('active', pore);
|
||||
const tog = document.getElementById('df-pore-toggle');
|
||||
if (tog) tog.style.background = pore ? '#FFB347' : 'rgba(255,255,255,0.15)';
|
||||
const span = tog && tog.querySelector('span');
|
||||
if (span) span.style.marginLeft = pore ? '14px' : '2px';
|
||||
// Also sync partition row
|
||||
document.getElementById('df-part-row').classList.toggle('active', on);
|
||||
}
|
||||
|
||||
function _diffUpdateUI(info) {
|
||||
document.getElementById('dfstat-LA').textContent = info.leftA;
|
||||
document.getElementById('dfstat-LB').textContent = info.leftB;
|
||||
document.getElementById('dfstat-RA').textContent = info.rightA;
|
||||
document.getElementById('dfstat-RB').textContent = info.rightB;
|
||||
document.getElementById('dfstat-mix').textContent = info.mixed + '%';
|
||||
document.getElementById('mpbar-l1').textContent = 'Смешивание';
|
||||
document.getElementById('mpbar-v1').textContent = info.mixed + '%';
|
||||
document.getElementById('mpbar-l2').textContent = 'Лево A/B';
|
||||
document.getElementById('mpbar-v2').textContent = info.leftA + '/' + info.leftB;
|
||||
document.getElementById('mpbar-l3').textContent = 'Право A/B';
|
||||
document.getElementById('mpbar-v3').textContent = info.rightA + '/' + info.rightB;
|
||||
document.getElementById('mpbar-l4').textContent = 'Раздел';
|
||||
const partLabel = !info.partitionOn ? 'снят' : info.poreMode ? 'пора' : 'вкл';
|
||||
document.getElementById('mpbar-v4').textContent = partLabel;
|
||||
document.getElementById('mpbar-v4').style.color = !info.partitionOn ? '#34d399' : info.poreMode ? '#FFB347' : '#fff';
|
||||
document.getElementById('mpbar-l5').textContent = 'Шагов';
|
||||
document.getElementById('mpbar-v5').textContent = info.steps;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════
|
||||
ЗАКОН КУЛОНА
|
||||
════════════════════════════════ */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user