Verbessere Baryzentrum-Darstellung und Tagesauflösung
This commit is contained in:
+102
-41
@@ -4,7 +4,7 @@ session_start();
|
||||
|
||||
require_once __DIR__ . '/python_api.php';
|
||||
|
||||
$result = runPythonApi('barycenter', ['20', '12', '24']);
|
||||
$result = runPythonApi('barycenter', ['20', '365', '24']);
|
||||
|
||||
|
||||
require __DIR__ . '/header.php';
|
||||
@@ -135,6 +135,25 @@ require __DIR__ . '/header.php';
|
||||
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;
|
||||
@@ -169,29 +188,30 @@ input[type=range].bc-slider {
|
||||
<?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">Abstand vom Sonnenmittelpunkt</span>
|
||||
<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"><?= number_format($cur['dist_au'], 5, ',', '') ?> AU</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</span>
|
||||
<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">Stand: <?= htmlspecialchars($cur['date_utc'], ENT_QUOTES, 'UTF-8') ?></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 (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>
|
||||
<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>
|
||||
|
||||
@@ -205,8 +225,7 @@ input[type=range].bc-slider {
|
||||
<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:#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">
|
||||
@@ -214,6 +233,16 @@ input[type=range].bc-slider {
|
||||
<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>
|
||||
@@ -233,6 +262,10 @@ input[type=range].bc-slider {
|
||||
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">
|
||||
@@ -279,7 +312,7 @@ input[type=range].bc-slider {
|
||||
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 MAX_DIST_SR = <?= json_encode($result['data']['max_proj_dist_solar_radii']) ?>;
|
||||
|
||||
const canvas = document.getElementById('bc-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
@@ -292,13 +325,19 @@ input[type=range].bc-slider {
|
||||
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(yearFrac) {
|
||||
const y = Math.floor(yearFrac);
|
||||
const m = Math.round((yearFrac - y) * 12) + 1;
|
||||
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');
|
||||
}
|
||||
|
||||
@@ -322,20 +361,22 @@ input[type=range].bc-slider {
|
||||
|
||||
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);
|
||||
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
|
||||
@@ -354,21 +395,22 @@ input[type=range].bc-slider {
|
||||
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([]);
|
||||
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 (pastEnd >= 1) {
|
||||
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);
|
||||
@@ -383,7 +425,7 @@ input[type=range].bc-slider {
|
||||
}
|
||||
|
||||
// Zukünftiger Pfad (ab NOW_INDEX) — gold gedimmt, gestrichelt
|
||||
if (end > NOW_INDEX) {
|
||||
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);
|
||||
@@ -391,9 +433,9 @@ input[type=range].bc-slider {
|
||||
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.strokeStyle = 'rgba(79, 195, 216, 0.55)';
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.setLineDash([5, 4]);
|
||||
ctx.setLineDash([]);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
@@ -421,7 +463,7 @@ input[type=range].bc-slider {
|
||||
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);
|
||||
ctx.fillText(pCur.dp.toFixed(3) + ' R☉', px + 8, py - 6);
|
||||
}
|
||||
|
||||
function updateLabel() {
|
||||
@@ -433,12 +475,31 @@ input[type=range].bc-slider {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
+20
-7
@@ -5780,7 +5780,7 @@ def action_barycenter(args: list[str]) -> dict:
|
||||
steps_per_year = int(args[1]) if len(args) > 1 and args[1].isdigit() else 12
|
||||
years_forward = int(args[2]) if len(args) > 2 and args[2].isdigit() else 0
|
||||
years_back = max(1, min(years_back, 100))
|
||||
steps_per_year = max(4, min(steps_per_year, 52))
|
||||
steps_per_year = max(4, min(steps_per_year, 366))
|
||||
years_forward = max(0, min(years_forward, 100))
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -5793,6 +5793,8 @@ def action_barycenter(args: list[str]) -> dict:
|
||||
cur_y = -sun_state.y
|
||||
cur_dist_au = math.hypot(cur_x, cur_y, -sun_state.z)
|
||||
cur_dist_sr = cur_dist_au / _SUN_RADIUS_AU
|
||||
cur_proj_dist_au = math.hypot(cur_x, cur_y)
|
||||
cur_proj_dist_sr = cur_proj_dist_au / _SUN_RADIUS_AU
|
||||
|
||||
# -- Zeitreihe --
|
||||
total_steps = (years_back + years_forward) * steps_per_year
|
||||
@@ -5800,24 +5802,32 @@ def action_barycenter(args: list[str]) -> dict:
|
||||
start_jd = now_time.tt - years_back * 365.25
|
||||
series = []
|
||||
max_dist_au = 0.0
|
||||
max_proj_dist_au = 0.0
|
||||
for i in range(total_steps + 1):
|
||||
t = astronomy.Time(start_jd + i * days_per_step)
|
||||
s = astronomy.BaryState(astronomy.Body.Sun, t)
|
||||
tx, ty = -s.x, -s.y
|
||||
d = math.hypot(tx, ty, -s.z)
|
||||
dp = math.hypot(tx, ty)
|
||||
if d > max_dist_au:
|
||||
max_dist_au = d
|
||||
if dp > max_proj_dist_au:
|
||||
max_proj_dist_au = dp
|
||||
dt_utc = datetime(2000, 1, 1, 12, tzinfo=timezone.utc) + timedelta(days=t.tt - 0.0)
|
||||
# Epoche J2000.0 = 2000-01-01 12:00 TT ≈ UTC (Differenz <1 min hier vernachlässigt)
|
||||
series.append({"x": round(tx, 6), "y": round(ty, 6), "d": round(d / _SUN_RADIUS_AU, 3)})
|
||||
series.append({
|
||||
"x": round(tx, 6),
|
||||
"y": round(ty, 6),
|
||||
"d": round(d / _SUN_RADIUS_AU, 3),
|
||||
"dp": round(dp / _SUN_RADIUS_AU, 3),
|
||||
})
|
||||
|
||||
# Zeitstempel für Animationslabel (nur Jahr.Monat reicht)
|
||||
# Zeitstempel für Animationslabel als echte UTC-Kalenderdaten
|
||||
series_dates = []
|
||||
for i in range(total_steps + 1):
|
||||
t_days = start_jd + i * days_per_step
|
||||
# Grobe Umrechnung J2000-TT → Jahr
|
||||
year_frac = 2000.0 + (t_days / 365.25)
|
||||
series_dates.append(round(year_frac, 3))
|
||||
dt_utc = datetime(2000, 1, 1, 12, tzinfo=timezone.utc) + timedelta(days=t_days)
|
||||
series_dates.append(dt_utc.strftime("%Y-%m-%d"))
|
||||
|
||||
# -- Planetenmassen (GM relativ zur Sonne = 1) für gewichteten Beitrag --
|
||||
# Quellen: IAU 2012 / astronomy-engine Konstanten
|
||||
@@ -5872,13 +5882,16 @@ def action_barycenter(args: list[str]) -> dict:
|
||||
"y": round(cur_y, 6),
|
||||
"dist_au": round(cur_dist_au, 6),
|
||||
"dist_solar_radii": round(cur_dist_sr, 4),
|
||||
"inside_sun": cur_dist_sr <= 1.0,
|
||||
"proj_dist_au": round(cur_proj_dist_au, 6),
|
||||
"proj_dist_solar_radii": round(cur_proj_dist_sr, 4),
|
||||
"inside_sun": cur_proj_dist_sr <= 1.0,
|
||||
"date_utc": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
},
|
||||
"series": series,
|
||||
"series_dates": series_dates,
|
||||
"now_index": now_index,
|
||||
"max_dist_solar_radii": round(max_dist_au / _SUN_RADIUS_AU, 3),
|
||||
"max_proj_dist_solar_radii": round(max_proj_dist_au / _SUN_RADIUS_AU, 3),
|
||||
"sun_radius_au": _SUN_RADIUS_AU,
|
||||
"contributions": contributions,
|
||||
"years_back": years_back,
|
||||
|
||||
Reference in New Issue
Block a user