diff --git a/public/barycenter.php b/public/barycenter.php index 888ce6b..05afcbc 100644 --- a/public/barycenter.php +++ b/public/barycenter.php @@ -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 {
- Abstand vom Sonnenmittelpunkt + Räumlicher Abstand vom Sonnenmittelpunkt R☉ - AU + XY-Projektion: R☉ · AU
- Position + Position in der Grafik - Stand: + Bewertet als XY-Projektion · Stand:
- Maximum (2006–2050) - R☉ - Maximaler Abstand im Zeitraum + Maximum der Grafik (2006–2050) + R☉ + Größter projizierter Abstand im Zeitraum
@@ -205,8 +225,7 @@ input[type=range].bc-slider {
Sonne Sonnenoberfläche - Baryzentrum (Vergangenheit) - Baryzentrum (Prognose) + Baryzentrum-Bahn Baryzentrum (heute)
@@ -214,6 +233,16 @@ input[type=range].bc-slider { heute
+
+ + +
@@ -233,6 +262,10 @@ input[type=range].bc-slider { Die Bahn folgt keinem einfachen Kreis – die Überlagerung der Planetenumläufe erzeugt eine komplexe Spirale.

+

+ 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. +

@@ -279,7 +312,7 @@ input[type=range].bc-slider { const CUR = ; const NOW_INDEX = ; const SUN_RADIUS_AU = ; - const MAX_DIST_SR = ; + const MAX_DIST_SR = ; 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; diff --git a/public/py/api.py b/public/py/api.py index c2ad5e4..f555004 100644 --- a/public/py/api.py +++ b/public/py/api.py @@ -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,