feat(phys11 ch4): Wave 8 — Глава 4 «Основы СТО» (§24-§26 + Финал)
- phys-fx.js: PHYS.GammaPlot (график γ(β)), PHYS.TimeDilation (двое часов), PHYS.LengthContraction (стержень в покое и в движении) - ch4 — синяя тема (--pri:#2563eb), watermark c/γ/E/★ - §24: принцип отн. Галилея, кризис эфира, опыт Майкельсона – Морли - §25: 2 постулата Эйнштейна, замедление времени Δτ=γΔτ₀, сокращение L=L₀/γ, релятив. сложение скоростей - §26: релятив. импульс p=γmv, E=γmc², E₀=mc², E²=(pc)²+(mc²)², аннигиляция, ядерная энергия - 6 квизов + 3 босса (b1-b3) - Финал: 3 интегральных босса (fb1-fb3), +150 XP бонус, ачивка ch4_master
This commit is contained in:
@@ -1540,4 +1540,196 @@ class TwoLensSystem {
|
||||
}
|
||||
P.TwoLensSystem = TwoLensSystem;
|
||||
|
||||
/* ============================================================ */
|
||||
/* GammaPlot — график γ(β) и τ/τ₀, L/L₀ */
|
||||
/* ============================================================ */
|
||||
|
||||
class GammaPlot {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 580;
|
||||
this.H = opts.height || 280;
|
||||
this.beta = opts.beta !== undefined ? opts.beta : 0.5; /* v/c */
|
||||
this.color = opts.color || '#2563eb';
|
||||
this.paused = true;
|
||||
this.render();
|
||||
}
|
||||
setBeta(v){ this.beta = Math.max(0, Math.min(0.999, v)); this.render(); }
|
||||
update(){}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const pad = 40;
|
||||
const left = pad, right = W - pad - 100, top = 30, bot = H - 40;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
/* Сетка */
|
||||
svg += '<g stroke="#e2e8f0" stroke-width="0.8">';
|
||||
for (let i = 0; i <= 10; i++){
|
||||
const x = left + i * (right - left) / 10;
|
||||
svg += '<line x1="' + x + '" y1="' + top + '" x2="' + x + '" y2="' + bot + '"/>';
|
||||
}
|
||||
for (let i = 0; i <= 6; i++){
|
||||
const y = top + i * (bot - top) / 6;
|
||||
svg += '<line x1="' + left + '" y1="' + y + '" x2="' + right + '" y2="' + y + '"/>';
|
||||
}
|
||||
svg += '</g>';
|
||||
/* Оси */
|
||||
svg += '<line x1="' + left + '" y1="' + bot + '" x2="' + right + '" y2="' + bot + '" stroke="#0f172a" stroke-width="1.4"/>';
|
||||
svg += '<line x1="' + left + '" y1="' + top + '" x2="' + left + '" y2="' + bot + '" stroke="#0f172a" stroke-width="1.4"/>';
|
||||
/* Метки осей */
|
||||
svg += '<text x="' + ((left + right) / 2) + '" y="' + (bot + 26) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">β = v/c</text>';
|
||||
svg += '<text x="' + (left - 24) + '" y="' + ((top + bot) / 2) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" transform="rotate(-90 ' + (left - 24) + ' ' + ((top + bot) / 2) + ')">γ</text>';
|
||||
/* γ — растёт; ограничим до 6 */
|
||||
let pts = '';
|
||||
for (let i = 0; i <= 100; i++){
|
||||
const b = i / 100 * 0.99;
|
||||
const g = 1 / Math.sqrt(1 - b * b);
|
||||
const x = left + b * (right - left);
|
||||
const y = bot - Math.min(6, g) * (bot - top) / 6;
|
||||
pts += x.toFixed(1) + ',' + y.toFixed(1) + ' ';
|
||||
}
|
||||
svg += '<polyline points="' + pts + '" fill="none" stroke="' + this.color + '" stroke-width="2.4"/>';
|
||||
/* γ = 1 базовая линия */
|
||||
svg += '<line x1="' + left + '" y1="' + (bot - (bot - top) / 6) + '" x2="' + right + '" y2="' + (bot - (bot - top) / 6) + '" stroke="#94a3b8" stroke-width="0.8" stroke-dasharray="3 3"/>';
|
||||
svg += '<text x="' + (left - 6) + '" y="' + (bot - (bot - top) / 6 + 4) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#94a3b8">1</text>';
|
||||
/* Текущая точка */
|
||||
const g = 1 / Math.sqrt(1 - this.beta * this.beta);
|
||||
const cx = left + this.beta * (right - left);
|
||||
const cy = bot - Math.min(6, g) * (bot - top) / 6;
|
||||
svg += '<line x1="' + cx + '" y1="' + bot + '" x2="' + cx + '" y2="' + cy.toFixed(1) + '" stroke="#dc2626" stroke-width="1" stroke-dasharray="3 3"/>';
|
||||
svg += '<circle cx="' + cx + '" cy="' + cy.toFixed(1) + '" r="5" fill="#dc2626"/>';
|
||||
/* Подпись значения */
|
||||
const panelX = right + 12;
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 14) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">β = ' + this.beta.toFixed(3) + '</text>';
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 32) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#2563eb" font-weight="700">γ = ' + g.toFixed(3) + '</text>';
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 50) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">τ = γτ₀</text>';
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 64) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">L = L₀/γ</text>';
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 82) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">τ/τ₀ = ' + g.toFixed(2) + '</text>';
|
||||
svg += '<text x="' + panelX + '" y="' + (top + 96) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">L/L₀ = ' + (1 / g).toFixed(2) + '</text>';
|
||||
svg += '<text x="' + (W/2) + '" y="20" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">γ = 1/√(1 - β²)</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.GammaPlot = GammaPlot;
|
||||
|
||||
/* ============================================================ */
|
||||
/* TimeDilation — двое часов: «покоящиеся» и «движущиеся» */
|
||||
/* ============================================================ */
|
||||
|
||||
class TimeDilation {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 600;
|
||||
this.H = opts.height || 240;
|
||||
this.beta = opts.beta !== undefined ? opts.beta : 0.5;
|
||||
this.t = 0;
|
||||
this.paused = false;
|
||||
this._render();
|
||||
util.subscribe(this);
|
||||
util.observe(this);
|
||||
}
|
||||
setBeta(v){ this.beta = Math.max(0, Math.min(0.99, v)); }
|
||||
update(dt){ this.t += dt; }
|
||||
drawClock(svg, cx, cy, r, time, label, color){
|
||||
let s = svg;
|
||||
s += '<circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="#fff" stroke="' + color + '" stroke-width="2.4"/>';
|
||||
/* Метки часов 12, 3, 6, 9 */
|
||||
for (let i = 0; i < 12; i++){
|
||||
const a = i * Math.PI / 6;
|
||||
const x1 = cx + (r - 6) * Math.sin(a), y1 = cy - (r - 6) * Math.cos(a);
|
||||
const x2 = cx + (r - 2) * Math.sin(a), y2 = cy - (r - 2) * Math.cos(a);
|
||||
s += '<line x1="' + x1.toFixed(1) + '" y1="' + y1.toFixed(1) + '" x2="' + x2.toFixed(1) + '" y2="' + y2.toFixed(1) + '" stroke="#475569" stroke-width="' + (i % 3 === 0 ? 2 : 1) + '"/>';
|
||||
}
|
||||
/* Стрелка секундная (один оборот = 6 «времени») */
|
||||
const a = (time / 6) * 2 * Math.PI;
|
||||
const hx = cx + (r - 12) * Math.sin(a), hy = cy - (r - 12) * Math.cos(a);
|
||||
s += '<line x1="' + cx + '" y1="' + cy + '" x2="' + hx.toFixed(1) + '" y2="' + hy.toFixed(1) + '" stroke="' + color + '" stroke-width="2.6" stroke-linecap="round"/>';
|
||||
s += '<circle cx="' + cx + '" cy="' + cy + '" r="3" fill="' + color + '"/>';
|
||||
s += '<text x="' + cx + '" y="' + (cy + r + 22) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="' + color + '" font-weight="700">' + label + '</text>';
|
||||
s += '<text x="' + cx + '" y="' + (cy + r + 38) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">t = ' + time.toFixed(2) + ' с</text>';
|
||||
return s;
|
||||
}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const g = 1 / Math.sqrt(1 - this.beta * this.beta);
|
||||
/* Часы покоя — реальное время */
|
||||
const t0 = this.t;
|
||||
/* Движущиеся — идут медленнее в γ раз для наблюдателя */
|
||||
const tmov = this.t / g;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
svg = this.drawClock(svg, 140, 100, 55, t0, 'часы наблюдателя', '#0f172a');
|
||||
svg = this.drawClock(svg, W - 140, 100, 55, tmov, 'часы в движ. системе', '#dc2626');
|
||||
/* Стрелка движения */
|
||||
svg += '<line x1="220" y1="' + (H - 30) + '" x2="' + (W - 220) + '" y2="' + (H - 30) + '" stroke="#94a3b8" stroke-width="1.4"/>';
|
||||
svg += '<polygon points="' + (W - 220) + ',' + (H - 30) + ' ' + (W - 232) + ',' + (H - 36) + ' ' + (W - 232) + ',' + (H - 24) + '" fill="#94a3b8"/>';
|
||||
svg += '<text x="' + (W/2) + '" y="' + (H - 36) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">v → · β = ' + this.beta.toFixed(2) + ' · γ = ' + g.toFixed(2) + '</text>';
|
||||
svg += '<text x="' + (W/2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">Δτ = γ · Δτ₀ (движущиеся часы идут медленнее)</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
_render(){ this.render(); }
|
||||
}
|
||||
P.TimeDilation = TimeDilation;
|
||||
|
||||
/* ============================================================ */
|
||||
/* LengthContraction — стержень в покое и в движении */
|
||||
/* ============================================================ */
|
||||
|
||||
class LengthContraction {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 600;
|
||||
this.H = opts.height || 220;
|
||||
this.beta = opts.beta !== undefined ? opts.beta : 0.5;
|
||||
this.L0 = opts.L0 !== undefined ? opts.L0 : 320;
|
||||
this.paused = true;
|
||||
this.render();
|
||||
}
|
||||
setBeta(v){ this.beta = Math.max(0, Math.min(0.99, v)); this.render(); }
|
||||
update(){}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const g = 1 / Math.sqrt(1 - this.beta * this.beta);
|
||||
const L = this.L0 / g;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
/* Подпись */
|
||||
svg += '<text x="' + (W/2) + '" y="20" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">L = L₀ · √(1 - β²) = L₀/γ</text>';
|
||||
/* Стержень в покое */
|
||||
const cx = W / 2;
|
||||
const y1 = 60;
|
||||
svg += '<text x="' + (cx - this.L0/2 - 10) + '" y="' + (y1 + 5) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">покой:</text>';
|
||||
svg += '<rect x="' + (cx - this.L0/2) + '" y="' + (y1 - 12) + '" width="' + this.L0 + '" height="24" fill="#dbeafe" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
/* Деления */
|
||||
for (let i = 0; i <= 10; i++){
|
||||
const x = cx - this.L0/2 + i * this.L0 / 10;
|
||||
svg += '<line x1="' + x + '" y1="' + (y1 - 12) + '" x2="' + x + '" y2="' + (y1 + 12) + '" stroke="#0f172a" stroke-width="0.8"/>';
|
||||
}
|
||||
svg += '<text x="' + cx + '" y="' + (y1 + 32) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a">L₀ = ' + this.L0 + ' (собственная длина)</text>';
|
||||
/* Стержень в движении */
|
||||
const y2 = 140;
|
||||
svg += '<text x="' + (cx - L/2 - 10) + '" y="' + (y2 + 5) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="#dc2626" font-weight="700">движется:</text>';
|
||||
svg += '<rect x="' + (cx - L/2) + '" y="' + (y2 - 12) + '" width="' + L.toFixed(1) + '" height="24" fill="#fee2e2" stroke="#dc2626" stroke-width="1.6"/>';
|
||||
for (let i = 0; i <= 10; i++){
|
||||
const x = cx - L/2 + i * L / 10;
|
||||
svg += '<line x1="' + x.toFixed(1) + '" y1="' + (y2 - 12) + '" x2="' + x.toFixed(1) + '" y2="' + (y2 + 12) + '" stroke="#dc2626" stroke-width="0.8"/>';
|
||||
}
|
||||
/* Стрелка скорости */
|
||||
svg += '<line x1="' + (cx + L/2 + 10) + '" y1="' + y2 + '" x2="' + (cx + L/2 + 60) + '" y2="' + y2 + '" stroke="#dc2626" stroke-width="2"/>';
|
||||
svg += '<polygon points="' + (cx + L/2 + 60) + ',' + y2 + ' ' + (cx + L/2 + 50) + ',' + (y2 - 6) + ' ' + (cx + L/2 + 50) + ',' + (y2 + 6) + '" fill="#dc2626"/>';
|
||||
svg += '<text x="' + (cx + L/2 + 30) + '" y="' + (y2 - 10) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#dc2626" font-weight="700">v</text>';
|
||||
svg += '<text x="' + cx + '" y="' + (y2 + 32) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#dc2626">L = ' + L.toFixed(1) + ' · L/L₀ = ' + (1/g).toFixed(3) + '</text>';
|
||||
/* Параметры */
|
||||
svg += '<text x="' + (W - 12) + '" y="' + (H - 10) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">β = ' + this.beta.toFixed(2) + ' · γ = ' + g.toFixed(2) + '</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.LengthContraction = LengthContraction;
|
||||
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user