feat(phys11 W1): Глава 1 §1-§3 + расширение phys-fx.js (EnergyView)
phys-fx.js (+EnergyView): - PHYS.EnergyView — график 3 кривых: W_к (красный), W_п (зелёный), W_мех=const (фиолетовый пунктир) - Использует кинетическую/потенциальную энергию для гарм. колеб.: cos², sin², сумма = 1 - Легенда в правом верхнем углу physics_11_ch1.html (~63 КБ): Архитектура geom_10_r1 (geom11-стиль): - 2-кол layout с col-side (XP card + cheat sheet + tip) - Hero cyan-градиент + кнопка 'Начать §1' - psel-grid: 6 параграфов + Финал; §1-§3 активны, §4-§6 и Финал locked - sec секции с watermark (∿, маятник, E, ☰, ∿, муз. нота, ★) - card теории + wg workshops + opt-btn кнопки §1 Колебательное движение. Гарм. колебания: - 3 теор. карточки (определение, T/ν/ω, гарм. колеб. x=Acos(ωt+φ₀)) - Инт. 1: Oscillogram с ползунками A, ω, φ (live-анимация) - Инт. 2: Расчёт T,ν,ω (5 задач input) - Инт. 3: Свойства колеб. (5 MC) - Босс §1: 5 этапов, +65 XP §2 Маятники: - 2 теор. карточки (пружинный T=2π√(m/k), матем. T=2π√(l/g)) - Инт. 1: SpringMass + Pendulum side-by-side с 4 ползунками (m,k,l,g) - Инт. 2: Расчёт T (5 input) - Инт. 3: Как изменится T (5 MC) - Босс §2: 5 этапов, +70 XP §3 Превращения энергии: - 2 теор. карточки (формулы W_к, W_п; закон сохранения W_мех=kA²/2) - Инт. 1: EnergyView с ползунками A, ω (3 кривые в реал. времени) - Инт. 2: Расчёт энергии (5 input) - Инт. 3: Превращения энергии (5 MC) - Босс §3: 5 этапов, +65 XP §4-§6 и Финал — stub-карточки 'в разработке (W2)'. LocalStorage: physics11_ch1_*, physics11_xp (общий со всем курсом) Server sync: /api/textbooks/physics-11-ch1/progress
This commit is contained in:
@@ -321,4 +321,72 @@ class Pendulum {
|
||||
}
|
||||
P.Pendulum = Pendulum;
|
||||
|
||||
/* ============================================================ */
|
||||
/* EnergyView — превращения энергии при гарм. колебаниях */
|
||||
/* Показывает W_к, W_п, W_мех=const на одном графике */
|
||||
/* ============================================================ */
|
||||
|
||||
class EnergyView {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 560;
|
||||
this.H = opts.height || 240;
|
||||
this.pad = opts.pad || 36;
|
||||
this.A = opts.A !== undefined ? opts.A : 1.0;
|
||||
this.omega = opts.omega !== undefined ? opts.omega : 2 * Math.PI;
|
||||
this.tWindow = opts.tWindow || 4;
|
||||
this.paused = false;
|
||||
this.t = 0;
|
||||
this.history = []; // [t, Wk, Wp]
|
||||
util.subscribe(this);
|
||||
util.observe(this);
|
||||
this.render();
|
||||
}
|
||||
setA(v){ this.A = v; this.history = []; }
|
||||
setOmega(v){ this.omega = v; this.history = []; }
|
||||
update(dt){
|
||||
this.t += dt;
|
||||
/* Для x = A cos(ωt): v = -Aω sin(ωt)
|
||||
W_к = m v² / 2 = (1/2) m A² ω² sin²(ωt)
|
||||
W_п = k x² / 2 = (1/2) m ω² · A² cos²(ωt) (k = m ω²)
|
||||
В безразмерных: положим (1/2)mω²A² = 1 — тогда обе варьируются 0..1, сумма = 1 */
|
||||
const c = Math.cos(this.omega * this.t);
|
||||
const s = Math.sin(this.omega * this.t);
|
||||
const Wp = c * c;
|
||||
const Wk = s * s;
|
||||
this.history.push([this.t, Wk, Wp]);
|
||||
while (this.history.length && this.history[0][0] < this.t - this.tWindow) this.history.shift();
|
||||
}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H, pad = this.pad;
|
||||
const tMin = Math.max(0, this.t - this.tWindow);
|
||||
const yRange = [0, 1.1];
|
||||
const ax = util.axes(W, H, pad, this.tWindow, yRange);
|
||||
function path(idx, color, label){
|
||||
if (this.history.length < 2) return '';
|
||||
const pts = this.history.map(p => (ax.left + (p[0] - tMin) * (ax.right - ax.left) / this.tWindow).toFixed(1) + ',' + ax.toY(p[idx]).toFixed(1));
|
||||
return '<polyline points="' + pts.join(' ') + '" fill="none" stroke="' + color + '" stroke-width="2.2" stroke-linejoin="round"/>';
|
||||
}
|
||||
const pK = path.call(this, 1, '#dc2626');
|
||||
const pP = path.call(this, 2, '#16a34a');
|
||||
/* W_мех = const = 1 (горизонтальная линия) */
|
||||
const pM = '<line x1="' + ax.left + '" y1="' + ax.toY(1) + '" x2="' + ax.right + '" y2="' + ax.toY(1) + '" stroke="#7c3aed" stroke-width="2.4" stroke-dasharray="6 4"/>';
|
||||
/* Легенда */
|
||||
const legend = '<g font-family="JetBrains Mono,monospace" font-size="11" font-weight="700">'
|
||||
+ '<rect x="' + (W - 130) + '" y="' + (pad - 28) + '" width="118" height="74" fill="#fff" stroke="#e2e8f0" rx="6"/>'
|
||||
+ '<line x1="' + (W - 122) + '" y1="' + (pad - 14) + '" x2="' + (W - 102) + '" y2="' + (pad - 14) + '" stroke="#dc2626" stroke-width="2.4"/>'
|
||||
+ '<text x="' + (W - 96) + '" y="' + (pad - 10) + '" fill="#dc2626">W кинет.</text>'
|
||||
+ '<line x1="' + (W - 122) + '" y1="' + (pad + 6) + '" x2="' + (W - 102) + '" y2="' + (pad + 6) + '" stroke="#16a34a" stroke-width="2.4"/>'
|
||||
+ '<text x="' + (W - 96) + '" y="' + (pad + 10) + '" fill="#16a34a">W потенц.</text>'
|
||||
+ '<line x1="' + (W - 122) + '" y1="' + (pad + 26) + '" x2="' + (W - 102) + '" y2="' + (pad + 26) + '" stroke="#7c3aed" stroke-width="2.4" stroke-dasharray="4 3"/>'
|
||||
+ '<text x="' + (W - 96) + '" y="' + (pad + 30) + '" fill="#7c3aed">W мех = const</text>'
|
||||
+ '</g>';
|
||||
const svg = util.svgFrame(W, H) + ax.svg + pM + pK + pP + legend + '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.EnergyView = EnergyView;
|
||||
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user