Files
skyview.astronomiemuseum.de/public/barycenter.php
T

560 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
session_start();
require_once __DIR__ . '/python_api.php';
$result = runPythonApi('barycenter', ['20', '365', '24']);
require __DIR__ . '/header.php';
?>
<style>
.bc-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
@media (max-width: 860px) {
.bc-grid { grid-template-columns: 1fr; }
}
#bc-canvas-wrap {
position: relative;
width: 100%;
aspect-ratio: 1;
background: #04060e;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
#bc-canvas {
display: block;
width: 100%;
height: 100%;
}
.bc-legend {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
margin-top: 0.75rem;
font-size: 0.8rem;
color: var(--text-dim);
}
.bc-legend-item {
display: flex;
align-items: center;
gap: 0.4rem;
}
.bc-legend-dot {
width: 12px;
height: 12px;
border-radius: 50%;
flex-shrink: 0;
}
.bc-stat {
display: flex;
flex-direction: column;
gap: 0.2rem;
padding: 0.75rem 1rem;
background: var(--bg-card2);
border-radius: var(--radius);
border: 1px solid var(--border);
}
.bc-stat-label {
font-size: 0.75rem;
color: var(--text-dim);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.bc-stat-value {
font-size: 1.2rem;
color: var(--gold-pale);
font-weight: 600;
}
.bc-stat-sub {
font-size: 0.8rem;
color: var(--text-dim);
}
.bc-stats-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 0.75rem;
margin-bottom: 1rem;
}
.bc-planet-bar-wrap {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.bc-planet-row {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.85rem;
}
.bc-planet-name {
width: 70px;
flex-shrink: 0;
color: var(--text-dim);
}
.bc-planet-bar-bg {
flex: 1;
height: 8px;
background: rgba(255,255,255,0.06);
border-radius: 4px;
overflow: hidden;
}
.bc-planet-bar {
height: 100%;
border-radius: 4px;
transition: width 0.8s ease;
}
.bc-planet-val {
width: 50px;
text-align: right;
color: var(--text-dim);
font-size: 0.78rem;
}
.bc-anim-controls {
display: flex;
align-items: center;
gap: 0.75rem;
margin-top: 0.75rem;
flex-wrap: wrap;
}
.bc-year-label {
font-size: 0.85rem;
color: var(--gold-pale);
min-width: 60px;
}
.bc-toggle-row {
display: flex;
flex-wrap: wrap;
gap: 0.65rem 1rem;
margin-top: 0.65rem;
}
.bc-toggle {
display: inline-flex;
align-items: center;
gap: 0.45rem;
font-size: 0.82rem;
color: var(--text-dim);
cursor: pointer;
user-select: none;
}
.bc-toggle input {
accent-color: var(--gold);
cursor: pointer;
}
input[type=range].bc-slider {
flex: 1;
min-width: 120px;
accent-color: var(--gold);
cursor: pointer;
}
.bc-inside-badge {
display: inline-block;
padding: 0.15em 0.6em;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.04em;
}
.bc-inside-badge.inside {
background: rgba(79,195,216,0.2);
color: var(--cyan);
border: 1px solid rgba(79,195,216,0.4);
}
.bc-inside-badge.outside {
background: rgba(201,168,76,0.15);
color: var(--gold);
border: 1px solid var(--border);
}
</style>
<?php if (!$result['ok']): ?>
<div class="card">
<p class="msg-error"><li>Fehler beim Berechnen: <?= htmlspecialchars($result['error'] ?? 'Unbekannter Fehler', ENT_QUOTES, 'UTF-8') ?></li></p>
</div>
<?php else:
$cur = $result['data']['current'];
$distSR = $cur['dist_solar_radii'];
$projDistSR = $cur['proj_dist_solar_radii'];
$inside = $cur['inside_sun'];
$contributions = $result['data']['contributions'];
?>
<div class="bc-stats-row">
<div class="bc-stat">
<span class="bc-stat-label">Räumlicher Abstand vom Sonnenmittelpunkt</span>
<span class="bc-stat-value"><?= number_format($distSR, 3, ',', '') ?> R☉</span>
<span class="bc-stat-sub">XY-Projektion: <?= number_format($projDistSR, 3, ',', '') ?> R☉ · <?= number_format($cur['dist_au'], 5, ',', '') ?> AU</span>
</div>
<div class="bc-stat">
<span class="bc-stat-label">Position in der Grafik</span>
<span class="bc-stat-value">
<span class="bc-inside-badge <?= $inside ? 'inside' : 'outside' ?>">
<?= $inside ? 'Innerhalb der Sonne' : 'Außerhalb der Sonne' ?>
</span>
</span>
<span class="bc-stat-sub">Bewertet als XY-Projektion · Stand: <?= htmlspecialchars($cur['date_utc'], ENT_QUOTES, 'UTF-8') ?></span>
</div>
<div class="bc-stat">
<span class="bc-stat-label">Maximum der Grafik (2006–2050)</span>
<span class="bc-stat-value"><?= number_format($result['data']['max_proj_dist_solar_radii'], 3, ',', '') ?> R☉</span>
<span class="bc-stat-sub">Größter projizierter Abstand im Zeitraum</span>
</div>
</div>
<div class="bc-grid">
<div>
<div class="card">
<h2>Bahnkurve des Baryzentrums</h2>
<div id="bc-canvas-wrap">
<canvas id="bc-canvas"></canvas>
</div>
<div class="bc-legend">
<span class="bc-legend-item"><span class="bc-legend-dot" style="background:#f5c842"></span> Sonne</span>
<span class="bc-legend-item"><span class="bc-legend-dot" style="background:rgba(255,255,255,0.25);border:1px dashed #666"></span> Sonnenoberfläche</span>
<span class="bc-legend-item"><span class="bc-legend-dot" style="background:#4fc3d8"></span> Baryzentrum-Bahn</span>
<span class="bc-legend-item"><span class="bc-legend-dot" style="background:#ff6060"></span> Baryzentrum (heute)</span>
</div>
<div class="bc-anim-controls">
<button id="bc-play" class="btn btn-sm btn-secondary">▶ Abspielen</button>
<input type="range" class="bc-slider" id="bc-slider" min="0" max="240" value="240" step="1">
<span class="bc-year-label" id="bc-year-label">heute</span>
</div>
<div class="bc-toggle-row">
<label class="bc-toggle">
<input type="checkbox" id="bc-toggle-path" checked>
<span>Baryzentrum-Linie</span>
</label>
<label class="bc-toggle">
<input type="checkbox" id="bc-toggle-radii" checked>
<span>1R- und 2R-Radius</span>
</label>
</div>
</div>
</div>
<div>
<div class="card">
<h2>Erklärung</h2>
<p style="font-size:0.9rem;color:var(--text-dim);line-height:1.6">
Das <strong style="color:var(--text)">Baryzentrum</strong> ist der gemeinsame Schwerpunkt des gesamten Sonnensystems.
Die Sonne kreist – wie alle Planeten – um diesen Punkt, nicht umgekehrt.
</p>
<p style="font-size:0.9rem;color:var(--text-dim);line-height:1.6;margin-top:0.75rem">
Der Schwerpunkt liegt meist nahe der Sonnenoberfläche oder knapp darüber
(ca. 1&nbsp;Sonnenradius Abstand), selten direkt im Sonnenmittelpunkt.
<strong style="color:var(--gold)">Jupiter</strong> hat den bei weitem größten Einfluss.
</p>
<p style="font-size:0.9rem;color:var(--text-dim);line-height:1.6;margin-top:0.75rem">
Der dargestellte Zeitraum reicht von 2006 bis 2050 (Vergangenheit + Prognose).
Die Bahn folgt keinem einfachen Kreis –
die Überlagerung der Planetenumläufe erzeugt eine komplexe Spirale.
</p>
<p style="font-size:0.9rem;color:var(--text-dim);line-height:1.6;margin-top:0.75rem">
Die Grafik ist eine XY-Projektion. Deshalb wird die Lage relativ zur Sonnenscheibe
hier ebenfalls in der Projektion bewertet, nicht mit dem räumlichen 3D-Abstand.
</p>
</div>
<div class="card" style="margin-top:1.5rem">
<h2>Planetenbeiträge (heliozentrisch)</h2>
<div class="bc-planet-bar-wrap">
<?php
$planetColors = [
'Merkur' => '#b0b0b0',
'Venus' => '#e8c97a',
'Erde' => '#4fc3d8',
'Mars' => '#e05050',
'Jupiter' => '#c9a84c',
'Saturn' => '#d4a96a',
'Uranus' => '#7fd4e8',
'Neptun' => '#6a8fd8',
];
$maxContrib = max(array_column($contributions, 'ssb_contribution_au'));
foreach ($contributions as $p):
$pct = $maxContrib > 0 ? round($p['ssb_contribution_au'] / $maxContrib * 100, 1) : 0;
$color = $planetColors[$p['name']] ?? '#888';
?>
<div class="bc-planet-row">
<span class="bc-planet-name"><?= htmlspecialchars($p['name'], ENT_QUOTES, 'UTF-8') ?></span>
<div class="bc-planet-bar-bg">
<div class="bc-planet-bar" style="width:<?= $pct ?>%;background:<?= $color ?>"></div>
</div>
<span class="bc-planet-val" title="<?= number_format($p['helio_dist_au'], 2, ',', '.') ?> AU Abstand"><?= $p['ssb_contribution_au'] >= 0.0001 ? number_format($p['ssb_contribution_au'], 4, ',', '') : '< 0,0001' ?> AU</span>
</div>
<?php endforeach; ?>
</div>
<p style="font-size:0.78rem;color:var(--text-dim);margin-top:0.9rem">
Balken zeigen den gewichteten Beitrag zum SSB-Offset (Masse × Abstand).
Jupiter dominiert mit ~71 % der gesamten Planetenmasse. Die inneren Planeten
(Merkur bis Mars) tragen zusammen weniger als 0,1 % bei.
</p>
</div>
</div>
</div>
<script>
(function () {
const SERIES = <?= json_encode($result['data']['series'], JSON_UNESCAPED_UNICODE) ?>;
const DATES = <?= json_encode($result['data']['series_dates'], JSON_UNESCAPED_UNICODE) ?>;
const CUR = <?= json_encode($cur, JSON_UNESCAPED_UNICODE) ?>;
const NOW_INDEX = <?= (int)$result['data']['now_index'] ?>;
const SUN_RADIUS_AU = <?= json_encode($result['data']['sun_radius_au']) ?>;
const MAX_DIST_SR = <?= json_encode($result['data']['max_proj_dist_solar_radii']) ?>;
const canvas = document.getElementById('bc-canvas');
const ctx = canvas.getContext('2d');
let visibleUpTo = NOW_INDEX; // startet bei "heute"
let animPlaying = false;
let animFrame = null;
let animIndex = 0;
const slider = document.getElementById('bc-slider');
const playBtn = document.getElementById('bc-play');
const yearLabel = document.getElementById('bc-year-label');
const pathToggle = document.getElementById('bc-toggle-path');
const radiiToggle = document.getElementById('bc-toggle-radii');
slider.max = SERIES.length - 1;
slider.value = NOW_INDEX;
function formatDate(dateValue) {
if (typeof dateValue === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateValue)) {
return dateValue;
}
const y = Math.floor(dateValue);
const m = Math.round((dateValue - y) * 12) + 1;
return y + '-' + String(m).padStart(2, '0');
}
function resize() {
const wrap = document.getElementById('bc-canvas-wrap');
const size = wrap.clientWidth;
canvas.width = size;
canvas.height = size;
draw();
}
function draw() {
const W = canvas.width;
const H = canvas.height;
const cx = W / 2;
const cy = H / 2;
// Scale: fit MAX_DIST_SR * 1.3 solar radii into half-canvas
const scale = (W * 0.42) / (MAX_DIST_SR * 1.15);
const sunRadiusPx = scale; // 1 solar radius → scale px
ctx.clearRect(0, 0, W, H);
if (radiiToggle.checked) {
const guideRadii = [1, 2];
ctx.strokeStyle = 'rgba(255,255,255,0.07)';
ctx.lineWidth = 1;
ctx.font = '10px sans-serif';
ctx.fillStyle = 'rgba(255,255,255,0.25)';
ctx.textAlign = 'left';
for (const r of guideRadii) {
if (r > MAX_DIST_SR * 1.25) {
continue;
}
ctx.beginPath();
ctx.arc(cx, cy, r * scale, 0, Math.PI * 2);
ctx.stroke();
ctx.fillText(r + ' R☉', cx + r * scale + 3, cy - 3);
}
}
// Sun glow
const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, sunRadiusPx * 2.5);
glow.addColorStop(0, 'rgba(255, 220, 80, 0.6)');
glow.addColorStop(0.4, 'rgba(255, 180, 40, 0.25)');
glow.addColorStop(1, 'rgba(255, 140, 0, 0)');
ctx.beginPath();
ctx.arc(cx, cy, sunRadiusPx * 2.5, 0, Math.PI * 2);
ctx.fillStyle = glow;
ctx.fill();
// Sun disk
ctx.beginPath();
ctx.arc(cx, cy, sunRadiusPx, 0, Math.PI * 2);
ctx.fillStyle = '#f5c842';
ctx.fill();
if (radiiToggle.checked) {
ctx.beginPath();
ctx.arc(cx, cy, sunRadiusPx, 0, Math.PI * 2);
ctx.setLineDash([4, 4]);
ctx.strokeStyle = 'rgba(255,255,255,0.2)';
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
}
// Barycenter path up to visibleUpTo
const end = Math.min(visibleUpTo, SERIES.length - 1);
// Vergangener Pfad (bis NOW_INDEX) — cyan
const pastEnd = Math.min(end, NOW_INDEX);
if (pathToggle.checked && pastEnd >= 1) {
ctx.beginPath();
const p0 = SERIES[0];
ctx.moveTo(cx + p0.x / SUN_RADIUS_AU * scale, cy - p0.y / SUN_RADIUS_AU * scale);
for (let i = 1; i <= pastEnd; i++) {
const p = SERIES[i];
ctx.lineTo(cx + p.x / SUN_RADIUS_AU * scale, cy - p.y / SUN_RADIUS_AU * scale);
}
ctx.strokeStyle = 'rgba(79, 195, 216, 0.55)';
ctx.lineWidth = 1.5;
ctx.setLineDash([]);
ctx.stroke();
}
// Zukünftiger Pfad (ab NOW_INDEX) — gold gedimmt, gestrichelt
if (pathToggle.checked && end > NOW_INDEX) {
ctx.beginPath();
const pn = SERIES[NOW_INDEX];
ctx.moveTo(cx + pn.x / SUN_RADIUS_AU * scale, cy - pn.y / SUN_RADIUS_AU * scale);
for (let i = NOW_INDEX + 1; i <= end; i++) {
const p = SERIES[i];
ctx.lineTo(cx + p.x / SUN_RADIUS_AU * scale, cy - p.y / SUN_RADIUS_AU * scale);
}
ctx.strokeStyle = 'rgba(79, 195, 216, 0.55)';
ctx.lineWidth = 1.5;
ctx.setLineDash([]);
ctx.stroke();
ctx.setLineDash([]);
}
// Current SSB dot
const pCur = SERIES[end];
const px = cx + pCur.x / SUN_RADIUS_AU * scale;
const py = cy - pCur.y / SUN_RADIUS_AU * scale;
// Dot glow
const dotGlow = ctx.createRadialGradient(px, py, 0, px, py, 10);
dotGlow.addColorStop(0, 'rgba(255, 80, 80, 0.8)');
dotGlow.addColorStop(1, 'rgba(255, 80, 80, 0)');
ctx.beginPath();
ctx.arc(px, py, 10, 0, Math.PI * 2);
ctx.fillStyle = dotGlow;
ctx.fill();
ctx.beginPath();
ctx.arc(px, py, 4, 0, Math.PI * 2);
ctx.fillStyle = '#ff6060';
ctx.fill();
// Distance label
ctx.font = '11px sans-serif';
ctx.fillStyle = 'rgba(255,255,255,0.65)';
ctx.textAlign = 'left';
ctx.fillText(pCur.dp.toFixed(3) + ' R☉', px + 8, py - 6);
}
function updateLabel() {
const idx = parseInt(slider.value, 10);
if (idx === NOW_INDEX) {
yearLabel.textContent = 'heute';
} else {
yearLabel.textContent = formatDate(DATES[idx]);
}
}
function setVisibleIndex(nextIndex) {
const clamped = Math.max(0, Math.min(SERIES.length - 1, nextIndex));
visibleUpTo = clamped;
slider.value = clamped;
updateLabel();
draw();
}
function handleWheelStep(event) {
event.preventDefault();
if (animPlaying) stopAnim();
const direction = event.deltaY > 0 ? 1 : -1;
setVisibleIndex(parseInt(slider.value, 10) + direction);
}
slider.addEventListener('input', () => {
visibleUpTo = parseInt(slider.value, 10);
updateLabel();
draw();
if (animPlaying) stopAnim();
});
slider.addEventListener('wheel', handleWheelStep, { passive: false });
canvas.addEventListener('wheel', handleWheelStep, { passive: false });
pathToggle.addEventListener('change', draw);
radiiToggle.addEventListener('change', draw);
function stopAnim() {
animPlaying = false;
playBtn.textContent = '▶ Abspielen';
if (animFrame) { cancelAnimationFrame(animFrame); animFrame = null; }
}
function startAnim() {
animPlaying = true;
playBtn.textContent = '⏸ Pause';
// Start from beginning if at or near end
if (visibleUpTo >= SERIES.length - 1) {
visibleUpTo = 0;
animIndex = 0;
} else {
animIndex = visibleUpTo;
}
let last = null;
const msPerStep = 30; // ~33 fps, 1 step per frame
function step(ts) {
if (!animPlaying) return;
if (last === null || ts - last >= msPerStep) {
last = ts;
animIndex++;
if (animIndex >= SERIES.length) {
animIndex = SERIES.length - 1;
visibleUpTo = animIndex;
slider.value = animIndex;
updateLabel();
draw();
stopAnim();
return;
}
visibleUpTo = animIndex;
slider.value = animIndex;
updateLabel();
draw();
}
animFrame = requestAnimationFrame(step);
}
animFrame = requestAnimationFrame(step);
}
playBtn.addEventListener('click', () => {
if (animPlaying) { stopAnim(); } else { startAnim(); }
});
window.addEventListener('resize', resize);
resize();
updateLabel();
})();
</script>
<?php endif; ?>
<?php require __DIR__ . '/footer.php'; ?>