Files
skyview.astronomiemuseum.de/public/barycenter.php
T
EskimueandClaude Sonnet 4.6 ad0195086f Neue Seite: Sonnensystem-Baryzentrum
Zeigt die Bewegung des gemeinsamen Schwerpunkts von Sonne und Planeten
von 2006 bis 2050 als animierte Canvas-Darstellung. Vergangener Pfad
in Cyan, Prognose gestrichelt in Gold. Planetenbeiträge aller 8 Planeten
(gewichtet nach Masse × Abstand) als Balkendiagramm.

Berechnung via astronomy-engine (Python), neuer API-Action `barycenter`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 10:45:45 +02:00

499 lines
16 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', '12', '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;
}
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'];
$inside = $cur['inside_sun'];
$contributions = $result['data']['contributions'];
?>
<div class="bc-stats-row">
<div class="bc-stat">
<span class="bc-stat-label">Abstand vom Sonnenmittelpunkt</span>
<span class="bc-stat-value"><?= number_format($distSR, 3, ',', '') ?> R☉</span>
<span class="bc-stat-sub"><?= number_format($cur['dist_au'], 5, ',', '') ?> AU</span>
</div>
<div class="bc-stat">
<span class="bc-stat-label">Position</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">Stand: <?= htmlspecialchars($cur['date_utc'], ENT_QUOTES, 'UTF-8') ?></span>
</div>
<div class="bc-stat">
<span class="bc-stat-label">Maximum (2006–2050)</span>
<span class="bc-stat-value"><?= number_format($result['data']['max_dist_solar_radii'], 3, ',', '') ?> R☉</span>
<span class="bc-stat-sub">Maximaler 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 (Vergangenheit)</span>
<span class="bc-legend-item"><span class="bc-legend-dot" style="background:#c9a84c;opacity:0.7"></span> Baryzentrum (Prognose)</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>
</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>
</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_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');
slider.max = SERIES.length - 1;
slider.value = NOW_INDEX;
function formatDate(yearFrac) {
const y = Math.floor(yearFrac);
const m = Math.round((yearFrac - 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);
// Background grid rings
ctx.strokeStyle = 'rgba(255,255,255,0.05)';
ctx.lineWidth = 1;
for (let r = 1; r <= Math.ceil(MAX_DIST_SR * 1.2); r++) {
ctx.beginPath();
ctx.arc(cx, cy, r * scale, 0, Math.PI * 2);
ctx.stroke();
}
// Ring labels
ctx.font = '10px sans-serif';
ctx.fillStyle = 'rgba(255,255,255,0.25)';
ctx.textAlign = 'left';
for (let r = 1; r <= Math.ceil(MAX_DIST_SR * 1.2); r++) {
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();
// Sun surface dashed circle
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 (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 (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(201, 168, 76, 0.45)';
ctx.lineWidth = 1.5;
ctx.setLineDash([5, 4]);
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.d.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]);
}
}
slider.addEventListener('input', () => {
visibleUpTo = parseInt(slider.value, 10);
updateLabel();
draw();
if (animPlaying) stopAnim();
});
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'; ?>