{
const {aprime, eta} = rindler_controls;
const xi0 = 1 / aprime;
const t0 = xi0 * Math.sinh(eta);
const x0 = xi0 * Math.cosh(eta);
const v0 = Math.tanh(eta);
const etaMax = 1.65;
const domain = Math.max(
3.2,
xi0 * Math.cosh(etaMax) + 0.45,
Math.abs(xi0 * Math.sinh(etaMax)) + 0.45
);
const ns = "http://www.w3.org/2000/svg";
const width = 900;
const height = 430;
const plotSize = 390;
const plotLeft = 36;
const plotTop = 18;
const panelLeft = 472;
const sx = (x) => plotLeft + (x + domain) / (2 * domain) * plotSize;
const sy = (t) => plotTop + (domain - t) / (2 * domain) * plotSize;
const f = (x) => Math.abs(x) < 1e-9 ? "0.00" : x.toFixed(2);
const points = (pts) => pts.map(([x, t]) => `${sx(x)},${sy(t)}`).join(" ");
const wrap = document.createElement("div");
wrap.className = "rindler-board";
const style = document.createElement("style");
style.textContent = `
.rindler-controls { display: flex; gap: 1.2rem; align-items: center; flex-wrap: wrap; margin: 0 0 0.35rem; }
.rindler-controls label { display: grid; grid-template-columns: 1.8rem 11rem 3.4rem; align-items: center; gap: 0.55rem; margin: 0; color: #f2f2f2; font-size: 0.48em; }
.rindler-controls input[type="range"] { width: 11rem; accent-color: #f5a524; }
.rindler-controls output { min-width: 3.4rem; padding: 0.08rem 0.25rem; border: 1px solid #334155; border-radius: 6px; background: #101722; color: #f2f2f2; font-variant-numeric: tabular-nums; text-align: right; }
.rindler-board { width: min(100%, 900px); margin: 0 auto; }
.rindler-board svg { width: 100%; height: auto; display: block; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
`;
wrap.append(style);
const svg = document.createElementNS(ns, "svg");
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
svg.setAttribute("role", "img");
svg.setAttribute("aria-label", "Диаграмма координат Риндлера и горизонта");
wrap.append(svg);
function add(name, attrs = {}, parent = svg) {
const node = document.createElementNS(ns, name);
for (const [key, value] of Object.entries(attrs)) {
if (key === "text") {
node.textContent = value;
} else {
node.setAttribute(key, value);
}
}
parent.append(node);
return node;
}
const defs = add("defs");
const clipId = "rindler-plot-clip";
const marker = add("marker", {
id: "rindler-arrow",
markerWidth: "8",
markerHeight: "8",
refX: "6",
refY: "4",
orient: "auto",
markerUnits: "strokeWidth"
}, defs);
add("path", {d: "M 0 0 L 8 4 L 0 8 Z", fill: "#d7dde8"}, marker);
const clip = add("clipPath", {id: clipId}, defs);
add("rect", {x: plotLeft, y: plotTop, width: plotSize, height: plotSize, rx: "6"}, clip);
add("rect", {
x: "0",
y: "0",
width,
height,
rx: "12",
fill: "#070a0f",
stroke: "#243244",
"stroke-width": "1"
});
add("rect", {
x: plotLeft,
y: plotTop,
width: plotSize,
height: plotSize,
fill: "#0b111b",
stroke: "#303b4f",
"stroke-width": "1.2"
});
const plotLayer = add("g", {"clip-path": `url(#${clipId})`});
add("polygon", {
points: points([[0, 0], [domain, domain], [domain, -domain]]),
fill: "#1b4d3e",
opacity: "0.28"
}, plotLayer);
for (const grid of [-2, -1, 1, 2]) {
const value = grid * Math.ceil(domain / 3);
if (Math.abs(value) < domain) {
add("line", {
x1: sx(-domain), y1: sy(value), x2: sx(domain), y2: sy(value),
stroke: "#1e2a3b", "stroke-width": "1"
}, plotLayer);
add("line", {
x1: sx(value), y1: sy(-domain), x2: sx(value), y2: sy(domain),
stroke: "#1e2a3b", "stroke-width": "1"
}, plotLayer);
}
}
add("line", {
x1: sx(-domain), y1: sy(0), x2: sx(domain * 0.98), y2: sy(0),
stroke: "#d7dde8", "stroke-width": "1.6", "marker-end": "url(#rindler-arrow)"
});
add("line", {
x1: sx(0), y1: sy(-domain), x2: sx(0), y2: sy(domain * 0.98),
stroke: "#d7dde8", "stroke-width": "1.6", "marker-end": "url(#rindler-arrow)"
});
add("line", {
x1: sx(-domain), y1: sy(-domain), x2: sx(domain), y2: sy(domain),
stroke: "#f2c94c", "stroke-width": "2.4", "stroke-dasharray": "8 7", opacity: "0.9"
}, plotLayer);
add("line", {
x1: sx(-domain), y1: sy(domain), x2: sx(domain), y2: sy(-domain),
stroke: "#f2c94c", "stroke-width": "2.4", "stroke-dasharray": "8 7", opacity: "0.9"
}, plotLayer);
for (const e of [-1.2, -0.8, -0.4, 0, 0.4, 0.8, 1.2]) {
add("line", {
x1: sx(0), y1: sy(0), x2: sx(domain), y2: sy(domain * Math.tanh(e)),
stroke: "#5cc8ff", "stroke-width": "1.2", opacity: "0.32"
}, plotLayer);
}
add("line", {
x1: sx(0), y1: sy(0), x2: sx(domain), y2: sy(domain * Math.tanh(eta)),
stroke: "#75e6ff", "stroke-width": "3.2", opacity: "0.95"
}, plotLayer);
function hyperbolaPath(xi, steps = 180) {
let d = "";
for (let i = 0; i <= steps; i++) {
const e = -etaMax + 2 * etaMax * i / steps;
const x = xi * Math.cosh(e);
const t = xi * Math.sinh(e);
d += `${i === 0 ? "M" : "L"} ${sx(x)} ${sy(t)} `;
}
return d;
}
for (const xi of [0.5, 0.8, 1.1, 1.5, 2.0, 2.6, 3.4, 4.4]) {
if (xi < domain * 0.92) {
add("path", {
d: hyperbolaPath(xi),
fill: "none",
stroke: "#9fb0c7",
"stroke-width": "1.25",
opacity: "0.45"
}, plotLayer);
}
}
add("path", {
d: hyperbolaPath(xi0),
fill: "none",
stroke: "#f5a524",
"stroke-width": "4.2"
}, plotLayer);
add("circle", {
cx: sx(x0),
cy: sy(t0),
r: "7.5",
fill: "#ff5d73",
stroke: "#fff",
"stroke-width": "2"
}, plotLayer);
add("text", {x: sx(domain * 0.93), y: sy(0) - 10, fill: "#d7dde8", "font-size": "20", text: "x"});
add("text", {x: sx(0) + 10, y: sy(domain * 0.9), fill: "#d7dde8", "font-size": "20", text: "t"});
add("text", {x: sx(domain * 0.58), y: sy(domain * 0.62) - 8, fill: "#f2c94c", "font-size": "17", text: "x = t"});
add("text", {x: sx(domain * 0.56), y: sy(-domain * 0.54) + 24, fill: "#f2c94c", "font-size": "17", text: "x = −t"});
add("text", {x: sx(domain * 0.52), y: sy(domain * 0.08), fill: "#8ae6c1", "font-size": "18", text: "x > |t|"});
add("text", {x: sx(x0) + 11, y: sy(t0) - 10, fill: "#ffb3c0", "font-size": "17", text: "событие"});
add("text", {x: sx(xi0 * Math.cosh(-1.18)) + 8, y: sy(xi0 * Math.sinh(-1.18)) + 18, fill: "#f5a524", "font-size": "18", text: "ξ = 1/a′"});
add("text", {x: sx(domain * 0.72), y: sy(domain * Math.tanh(eta) * 0.72) - 8, fill: "#75e6ff", "font-size": "18", text: "η = const"});
add("rect", {x: panelLeft, y: "38", width: "386", height: "324", rx: "10", fill: "#101722", stroke: "#2b3a50"});
add("text", {x: panelLeft + 22, y: "74", fill: "#ffffff", "font-size": "22", "font-weight": "700", text: "Клин Риндлера"});
add("text", {x: panelLeft + 22, y: "112", fill: "#d7dde8", "font-size": "17", text: `a′ = ${f(aprime)}`});
add("text", {x: panelLeft + 22, y: "140", fill: "#d7dde8", "font-size": "17", text: `ξ₀ = 1/a′ = ${f(xi0)}`});
add("text", {x: panelLeft + 22, y: "168", fill: "#d7dde8", "font-size": "17", text: `η = ${f(eta)}`});
add("text", {x: panelLeft + 22, y: "210", fill: "#f5a524", "font-size": "17", text: "ξ = const: гиперболы"});
add("text", {x: panelLeft + 22, y: "238", fill: "#75e6ff", "font-size": "17", text: "η = const: лучи"});
add("text", {x: panelLeft + 22, y: "280", fill: "#d7dde8", "font-size": "16", text: `t = ξ sinh η = ${f(t0)}`});
add("text", {x: panelLeft + 22, y: "306", fill: "#d7dde8", "font-size": "16", text: `x = ξ cosh η = ${f(x0)}`});
add("text", {x: panelLeft + 22, y: "332", fill: "#d7dde8", "font-size": "16", text: `v = tanh η = ${f(v0)}`});
add("text", {x: plotLeft, y: "422", fill: "#9fb0c7", "font-size": "14", text: "Горизонты x = ±t ограничивают область, доступную равномерно ускоренному наблюдателю."});
return wrap;
}