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:
Maxim Dolgolyov
2026-05-08 14:54:54 +03:00
parent d5f77bb648
commit ae31e4c4e8
35 changed files with 3657 additions and 3589 deletions
+121 -1
View File
@@ -1,4 +1,4 @@
'use strict';
'use strict';
/* ═══════════════════════════════════════════════
CollisionSim — 2D elastic/inelastic ball collision
@@ -1008,3 +1008,123 @@ function _roundRect(ctx, x, y, w, h, r) {
ctx.lineTo(x, y + r); ctx.arcTo(x, y, x+r, y, r);
ctx.closePath();
}
/* ─── lab UI init ─────────────────────────────────── */
function _openCollision() {
document.getElementById('sim-topbar-title').textContent = 'Столкновение шаров';
_simShow('sim-coll');
_simShow('ctrl-coll');
_registerSimState('collision', () => cSim?.getParams(), st => cSim?.setParams(st));
if (_embedMode) _startStateEmit('collision');
requestAnimationFrame(() => requestAnimationFrame(() => {
if (!cSim) {
cSim = new CollisionSim(document.getElementById('coll-canvas'));
cSim.onUpdate = _collUpdateUI;
cSim.onPlayPause = collPlayPause;
}
cSim.fit();
cSim.setSpeed(+document.getElementById('sl-speed').value);
collParam();
cSim.draw();
_collUpdateUI(cSim.stats());
}));
}
function collPlayPause() {
if (!cSim) return;
if (cSim.playing) { cSim.pause(); } else { cSim.play(); }
_collSyncBtn();
}
function _collSyncBtn() {
const tb = document.getElementById('coll-play-btn');
const lb = document.getElementById('coll-launch-main');
const lbl = document.getElementById('coll-launch-label');
const lic = document.getElementById('coll-launch-icon');
if (!cSim) return;
const playing = cSim.playing;
if (tb) {
tb.innerHTML = playing
? '<svg viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>'
: '<svg viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>';
tb.title = playing ? 'Пауза' : 'Запустить';
tb.classList.toggle('active', playing);
}
if (lb && lbl && lic) {
lb.classList.toggle('paused', playing);
lb.classList.remove('done');
if (playing) {
lic.innerHTML = '<rect x="5" y="3" width="4" height="18"/><rect x="15" y="3" width="4" height="18"/>';
lbl.textContent = 'Пауза';
} else {
lic.innerHTML = '<polygon points="5 3 19 12 5 21 5 3"/>';
lbl.textContent = 'Запустить';
}
}
}
function collParam() {
const m1 = +document.getElementById('sl-m1').value;
const m2 = +document.getElementById('sl-m2').value;
const v1 = +document.getElementById('sl-cv1').value;
const v2 = +document.getElementById('sl-cv2').value;
const angle = +document.getElementById('sl-cangle').value;
const e = +document.getElementById('sl-e').value;
const spd = +document.getElementById('sl-speed').value;
document.getElementById('c-m1').textContent = m1 + ' кг';
document.getElementById('c-m2').textContent = m2 + ' кг';
document.getElementById('c-v1').textContent = v1 + ' м/с';
document.getElementById('c-v2').textContent = v2 + ' м/с';
document.getElementById('c-angle').textContent = angle + '°';
document.getElementById('c-e').textContent = e.toFixed(2);
document.getElementById('c-speed').textContent = spd.toFixed(2) + '×';
if (cSim) {
/* speed change doesn't require a reset */
const speedChanged = Math.abs(cSim.speed - spd) > 0.001;
if (speedChanged) cSim.setSpeed(spd);
const physChanged = cSim.m1 !== m1 || cSim.m2 !== m2 ||
cSim.v1 !== v1 || cSim.v2 !== v2 ||
cSim.angle !== angle || cSim.e !== e;
if (physChanged) cSim.setParams({ m1, m2, v1, v2, angle, e });
_collSyncBtn();
}
}
function collPreset(m1, m2, v1, v2, angle, e) {
document.getElementById('sl-m1').value = m1;
document.getElementById('sl-m2').value = m2;
document.getElementById('sl-cv1').value = v1;
document.getElementById('sl-cv2').value = v2;
document.getElementById('sl-cangle').value = angle;
document.getElementById('sl-e').value = e;
collParam();
}
function _collUpdateUI(s) {
// before/after are arrays [{m, vx, vy, ke}, ...]
function snapKE(arr) { return arr ? arr.reduce((t, b) => t + b.ke, 0) : null; }
function snapP(arr) {
if (!arr) return null;
return Math.hypot(arr.reduce((t, b) => t + b.m * b.vx, 0),
arr.reduce((t, b) => t + b.m * b.vy, 0));
}
const bKE = snapKE(s.before), bP = snapP(s.before);
const aKE = snapKE(s.after), aP = snapP(s.after);
const f2 = v => v !== null ? v.toFixed(2) : '—';
document.getElementById('cs-pbefore').textContent = bP !== null ? f2(bP) + ' кг·м/с' : '—';
document.getElementById('cs-pafter').textContent = aP !== null ? f2(aP) + ' кг·м/с' : '—';
document.getElementById('cs-kebefore').textContent = bKE !== null ? f2(bKE) + ' Дж' : '—';
document.getElementById('cs-keafter').textContent = aKE !== null ? f2(aKE) + ' Дж' : '—';
document.getElementById('cs-count').textContent = s.colCount;
_collSyncBtn();
}
/* ── magnetic ── */