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:
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
ProjectileSim v2 — physics simulation
|
||||
@@ -1061,3 +1061,190 @@ function _projArrow(ctx, x1, y1, x2, y2, color, lw) {
|
||||
ctx.closePath(); ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/* ─── lab UI init ─────────────────────────────────── */
|
||||
function _openProjectile() {
|
||||
document.getElementById('sim-topbar-title').textContent = 'Бросок тела';
|
||||
_simShow('sim-proj');
|
||||
_simShow('ctrl-proj');
|
||||
_registerSimState('projectile', () => pSim?.getParams(), st => pSim?.setParams(st));
|
||||
if (_embedMode) _startStateEmit('projectile');
|
||||
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
if (!pSim) {
|
||||
pSim = new ProjectileSim(document.getElementById('proj-canvas'));
|
||||
pSim.onUpdate = _projUpdateUI;
|
||||
pSim.onPlayPause = projPlayPause;
|
||||
}
|
||||
pSim.fit();
|
||||
projParam(); // sync sliders <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> sim
|
||||
pSim.draw();
|
||||
_projUpdateUI(pSim.stats());
|
||||
}));
|
||||
}
|
||||
|
||||
function projPlayPause() {
|
||||
if (!pSim) return;
|
||||
if (pSim.playing) {
|
||||
pSim.pause();
|
||||
} else {
|
||||
pSim.play();
|
||||
}
|
||||
_projSyncPlayBtn();
|
||||
}
|
||||
|
||||
function _projSyncPlayBtn() {
|
||||
/* small topbar button */
|
||||
const tb = document.getElementById('proj-play-btn');
|
||||
/* big launch button */
|
||||
const lb = document.getElementById('proj-launch-main');
|
||||
const lbl = document.getElementById('proj-launch-label');
|
||||
const lic = document.getElementById('proj-launch-icon');
|
||||
if (!pSim) return;
|
||||
|
||||
const tf = pSim._curTFlight();
|
||||
const done = !pSim.playing && pSim.t >= tf && pSim.t > 0;
|
||||
const playing = pSim.playing;
|
||||
|
||||
/* topbar */
|
||||
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);
|
||||
}
|
||||
|
||||
/* big button */
|
||||
if (lb && lbl && lic) {
|
||||
lb.classList.toggle('paused', playing);
|
||||
lb.classList.toggle('done', done && !playing);
|
||||
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 if (done) {
|
||||
lic.innerHTML = '<polygon points="5 3 19 12 5 21 5 3"/>';
|
||||
lbl.textContent = 'Повторить';
|
||||
} else {
|
||||
lic.innerHTML = '<polygon points="5 3 19 12 5 21 5 3"/>';
|
||||
lbl.textContent = 'Запустить';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function projParam() {
|
||||
const v0 = +document.getElementById('sl-v0').value;
|
||||
const angle = +document.getElementById('sl-angle').value;
|
||||
const h0 = +document.getElementById('sl-h0').value;
|
||||
const g = +document.getElementById('sl-g').value;
|
||||
|
||||
document.getElementById('p-v0').textContent = v0 + ' м/с';
|
||||
document.getElementById('p-angle').textContent = angle + '°';
|
||||
document.getElementById('p-h0').textContent = h0 + ' м';
|
||||
document.getElementById('p-g').textContent = g.toFixed(2) + ' м/с²';
|
||||
|
||||
if (pSim) { pSim.setParams({ v0, angle, h0, g }); _projSyncPlayBtn(); }
|
||||
}
|
||||
|
||||
function projPreset(v0, angle, h0, g) {
|
||||
document.getElementById('sl-v0').value = v0;
|
||||
document.getElementById('sl-angle').value = angle;
|
||||
document.getElementById('sl-h0').value = h0;
|
||||
document.getElementById('sl-g').value = g;
|
||||
projParam();
|
||||
}
|
||||
|
||||
function projToggleDrag(rowEl) {
|
||||
if (!pSim) return;
|
||||
pSim.drag = !pSim.drag;
|
||||
const on = pSim.drag;
|
||||
rowEl.classList.toggle('active', on);
|
||||
const tog = document.getElementById('drag-toggle');
|
||||
tog.style.background = on ? 'var(--violet)' : 'rgba(255,255,255,0.12)';
|
||||
tog.querySelector('span').style.marginLeft = on ? '14px' : '2px';
|
||||
document.getElementById('drag-params').style.display = on ? '' : 'none';
|
||||
document.getElementById('ps-loss-wrap').style.display = on ? '' : 'none';
|
||||
if (on) {
|
||||
const cd = +document.getElementById('sl-cd').value / 100;
|
||||
const mass = +document.getElementById('sl-mass').value;
|
||||
pSim.setParams({ drag: true, Cd: cd, mass });
|
||||
} else {
|
||||
pSim.setParams({ drag: false });
|
||||
}
|
||||
}
|
||||
|
||||
function projCdChange() {
|
||||
const cd = +document.getElementById('sl-cd').value / 100;
|
||||
document.getElementById('p-cd').textContent = cd.toFixed(2);
|
||||
if (pSim) pSim.setParams({ Cd: cd });
|
||||
}
|
||||
|
||||
function projMassChange() {
|
||||
const mass = +document.getElementById('sl-mass').value;
|
||||
document.getElementById('p-mass').textContent = mass + ' кг';
|
||||
if (pSim) pSim.setParams({ mass });
|
||||
}
|
||||
|
||||
function projWindChange() {
|
||||
const wind = +document.getElementById('sl-wind').value;
|
||||
const label = wind === 0 ? '0 м/с' : (wind > 0 ? '<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> +' : '<svg class="ic" viewBox="0 0 24 24"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg> ') + Math.abs(wind) + ' м/с';
|
||||
document.getElementById('p-wind').textContent = label;
|
||||
document.getElementById('ps-loss-wrap').style.display = wind !== 0 ? '' : (pSim && pSim.drag ? '' : 'none');
|
||||
if (pSim) { pSim.setParams({ wind }); _projSyncPlayBtn(); }
|
||||
}
|
||||
|
||||
function projToggleBounce(rowEl) {
|
||||
if (!pSim) return;
|
||||
pSim.bounce = !pSim.bounce;
|
||||
const on = pSim.bounce;
|
||||
rowEl.classList.toggle('active', on);
|
||||
const tog = document.getElementById('bounce-toggle');
|
||||
tog.style.background = on ? 'rgba(123,245,164,0.8)' : 'rgba(255,255,255,0.12)';
|
||||
tog.querySelector('span').style.marginLeft = on ? '14px' : '2px';
|
||||
document.getElementById('bounce-params').style.display = on ? '' : 'none';
|
||||
const e = +document.getElementById('sl-restitution').value / 100;
|
||||
pSim.setParams({ bounce: on, restitution: e });
|
||||
}
|
||||
|
||||
function projRestitutionChange() {
|
||||
const e = +document.getElementById('sl-restitution').value / 100;
|
||||
document.getElementById('p-restitution').textContent = e.toFixed(2);
|
||||
if (pSim) pSim.setParams({ restitution: e });
|
||||
}
|
||||
|
||||
function projSetSpeed(s, el) {
|
||||
if (pSim) pSim.setSpeed(s);
|
||||
document.querySelectorAll('.proj-speed').forEach(b => b.classList.remove('active'));
|
||||
if (el) el.classList.add('active');
|
||||
}
|
||||
|
||||
function projSaveGhost() {
|
||||
if (pSim) pSim.saveGhost();
|
||||
}
|
||||
|
||||
function projClearGhosts() {
|
||||
if (pSim) pSim.clearGhosts();
|
||||
}
|
||||
|
||||
function _projUpdateUI(s) {
|
||||
const fmt = (n, unit) => n < 10000 ? n.toFixed(2) + ' ' + unit : (n/1000).toFixed(2) + ' к' + unit;
|
||||
document.getElementById('ps-range').textContent = fmt(s.range, 'м');
|
||||
document.getElementById('ps-hmax').textContent = fmt(s.hMax, 'м');
|
||||
document.getElementById('ps-tf').textContent = s.tf.toFixed(2) + ' с';
|
||||
document.getElementById('ps-vland').textContent = fmt(s.vLand, 'м/с');
|
||||
document.getElementById('ps-t').textContent = s.t.toFixed(2) + ' с';
|
||||
const laEl = document.getElementById('ps-land-angle');
|
||||
if (laEl) laEl.textContent = s.landAngle > 0.5 ? s.landAngle.toFixed(1) + '°' : '—';
|
||||
if (s.hasMod) {
|
||||
const lossEl = document.getElementById('ps-loss');
|
||||
if (lossEl) {
|
||||
const sign = s.rangeLoss > 0 ? '+' : '';
|
||||
lossEl.textContent = s.rangeLoss !== 0 ? sign + s.rangeLoss + '%' : '0%';
|
||||
lossEl.style.color = s.rangeLoss < 0 ? '#EF476F' : '#7BF5A4';
|
||||
}
|
||||
}
|
||||
_projSyncPlayBtn();
|
||||
}
|
||||
|
||||
/* ── collision ── */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user