Files
2026-04-23 22:01:15 +02:00

808 lines
30 KiB
PHP
Raw Permalink 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);
ini_set('display_errors', '1');
error_reporting(E_ALL);
session_start();
$loggedIn = isset($_SESSION['user_id']);
?>
<?php require __DIR__ . '/header.php'; ?>
<style>
.at-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
@media (max-width: 720px) {
.at-layout { grid-template-columns: 1fr; }
}
/* Parameter-Grid */
.at-param-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem 1rem;
}
@media (max-width: 480px) {
.at-param-grid { grid-template-columns: 1fr; }
}
.at-param {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.at-param label {
font-size: 0.78rem;
color: var(--text-dim);
letter-spacing: 0.04em;
}
.at-param input[type=range] {
width: 100%;
accent-color: var(--gold);
cursor: pointer;
}
.at-param .at-num {
display: flex;
align-items: center;
gap: 0.5rem;
}
.at-param .at-num input[type=number] {
flex: 1;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
padding: 0.35rem 0.6rem;
font-size: 0.92rem;
min-width: 0;
}
.at-param .at-num input[type=number]:focus {
outline: none;
border-color: var(--gold-dim);
}
.at-param .at-unit {
font-size: 0.78rem;
color: var(--text-dim);
white-space: nowrap;
}
.at-hint {
font-size: 0.75rem;
color: var(--text-dim);
line-height: 1.45;
}
/* Albedo-Modus Toggle */
.at-mode-toggle {
display: flex;
gap: 0;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
width: fit-content;
}
.at-mode-toggle input[type=radio] { display: none; }
.at-mode-toggle label {
padding: 0.3rem 0.85rem;
font-size: 0.78rem;
color: var(--text-dim);
cursor: pointer;
transition: background 0.15s, color 0.15s;
letter-spacing: 0.03em;
border: none;
}
.at-mode-toggle input[type=radio]:checked + label {
background: var(--gold-dim);
color: var(--bg);
font-weight: 600;
}
/* Abgeleitete Bond-Albedo Anzeige */
.at-derived {
display: flex;
align-items: center;
gap: 0.5rem;
background: rgba(201,168,76,0.07);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.35rem 0.7rem;
font-size: 0.85rem;
}
.at-derived-label { color: var(--text-dim); }
.at-derived-val { color: var(--gold); font-weight: 600; }
/* Albedo-Vorschau */
.at-albedo-wrap {
display: flex;
align-items: center;
gap: 0.75rem;
}
.at-albedo-swatch {
width: 2rem;
height: 2rem;
border-radius: 50%;
border: 1px solid var(--border);
flex-shrink: 0;
transition: background 0.2s;
}
.at-albedo-caption { font-size: 0.75rem; color: var(--text-dim); }
/* Preset-Buttons */
.at-presets {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
/* Ergebnisse */
.at-results { display: flex; flex-direction: column; gap: 1rem; }
.at-result-row {
display: flex;
align-items: baseline;
gap: 0.6rem;
flex-wrap: wrap;
}
.at-result-label { font-size: 0.78rem; color: var(--text-dim); min-width: 5rem; }
.at-result-sym {
font-family: 'Georgia', serif;
font-style: italic;
color: var(--gold-pale);
font-size: 1rem;
min-width: 3rem;
}
.at-result-val {
font-size: 1.45rem;
font-weight: 600;
color: var(--gold);
letter-spacing: 0.01em;
}
.at-result-c { font-size: 0.9rem; color: var(--text-dim); }
/* Durchmesser */
.at-diam-row {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.at-diam-val {
font-size: 1.6rem;
font-weight: 700;
color: var(--cyan);
}
.at-diam-unit { font-size: 0.9rem; color: var(--text-dim); }
.at-diam-note { font-size: 0.75rem; color: var(--text-dim); }
/* Temperaturprofil Canvas */
.at-theta-wrap {
position: relative;
width: 100%;
}
#at-theta-canvas {
display: block;
width: 100%;
height: 170px;
border-radius: var(--radius);
}
/* Größenschätzung hidden state */
.at-size-section { display: none; }
.at-size-section.is-visible { display: block; }
/* Trennlinie */
.at-divider {
border: none;
border-top: 1px solid var(--border);
margin: 0.8rem 0;
}
/* Tooltip */
.at-tip {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.1em;
height: 1.1em;
border-radius: 50%;
border: 1px solid var(--gold-dim);
color: var(--gold-dim);
font-size: 0.68rem;
font-style: normal;
cursor: help;
vertical-align: middle;
margin-left: 0.3em;
flex-shrink: 0;
user-select: none;
line-height: 1;
}
.at-tip::after {
content: attr(data-tip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: #111827;
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
font-size: 0.78rem;
line-height: 1.5;
padding: 0.5rem 0.75rem;
width: 220px;
white-space: normal;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s;
z-index: 100;
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
}
.at-tip:hover::after,
.at-tip:focus::after {
opacity: 1;
}
</style>
<div class="at-layout">
<!-- Linke Spalte: Eingaben -->
<div class="card">
<h2>Parameter</h2>
<div class="at-param-grid">
<div class="at-param">
<label for="at-r">Sonnenabstand <em>r</em> <span class="at-tip" tabindex="0" data-tip="Wie weit ist der Asteroid von der Sonne entfernt? Je weiter weg, desto weniger Wärme kommt an. 1 AE = Abstand Erde–Sonne (≈ 150 Mio. km).">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-r" min="0.05" max="50" step="0.01" value="1.00">
<span class="at-unit">AE</span>
</div>
<input type="range" id="at-r-slider" min="0.05" max="10" step="0.01" value="1.00">
<p class="at-hint">Abstand zur Sonne. Einstrahlung skaliert mit 1/r².</p>
</div>
<div class="at-param">
<label for="at-eps">Emissivität <em>ε</em> <span class="at-tip" tabindex="0" data-tip="Wie gut gibt die Oberfläche Wärme als Strahlung ab. Gestein strahlt fast perfekt (≈ 0,90). Der Maximalwert 1 wäre ein idealer Wärmestrahler.">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-eps" min="0.01" max="1" step="0.01" value="0.90">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-eps-slider" min="0.01" max="1" step="0.01" value="0.90">
<p class="at-hint">Wärmeabstrahlung. Typisch 0,90–0,95.</p>
</div>
<div class="at-param" style="grid-column:1/-1">
<label for="at-eta">Beaming-Faktor <em>η</em> <span class="at-tip" tabindex="0" data-tip="Korrektur für Oberflächenrauigkeit und Wärmespeicherung. Eine glatte, schnell rotierende Kugel hat η = 1. Rauere oder langsamere Asteroiden weichen davon ab (typisch 0,7–1,4).">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-eta" min="0.1" max="3" step="0.01" value="1.00">
<span class="at-unit">> 0</span>
</div>
<input type="range" id="at-eta-slider" min="0.1" max="3" step="0.01" value="1.00">
<p class="at-hint">Rauigkeits-/Anisotropie-Korrektur (NEATM). Typisch 0,7–1,4.</p>
</div>
</div>
<hr class="at-divider">
<!-- Albedo-Modus -->
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:0.65rem;flex-wrap:wrap;">
<span class="at-hint" style="min-height:0;">Albedo-Modus:</span>
<div class="at-mode-toggle">
<input type="radio" name="at-mode" id="at-mode-bond" value="bond" checked>
<label for="at-mode-bond">Bond-Albedo</label>
<input type="radio" name="at-mode" id="at-mode-geo" value="geo">
<label for="at-mode-geo">Geometrische Albedo</label>
</div>
</div>
<!-- Bond-Modus -->
<div id="at-bond-section">
<div class="at-param">
<label for="at-a">Bond-Albedo <em>A</em> <span class="at-tip" tabindex="0" data-tip="Wie viel des einfallenden Sonnenlichts wird insgesamt zurückgeworfen? 0 = pechschwarz, 1 = perfekter Spiegel. Kohle-artige Asteroiden: ≈ 0,03; heller Gesteinsasteroid: ≈ 0,25.">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-a" min="0" max="0.999" step="0.001" value="0.050">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-a-slider" min="0" max="0.999" step="0.001" value="0.050">
<p class="at-hint">Reflektierter Anteil der gesamten Strahlungsenergie. Dunkel ≈ 0,02–0,10.</p>
</div>
</div>
<!-- Geo-Modus -->
<div id="at-geo-section" style="display:none;">
<div class="at-param-grid">
<div class="at-param">
<label for="at-p">Geometrische Albedo <em>p</em> <span class="at-tip" tabindex="0" data-tip="Wie hell wirkt die Oberfläche, wenn die Sonne genau hinter dem Beobachter steht? Dieser Wert steht in Asteroidenkatalogen (MPC/JPL). Dunkel ≈ 0,04–0,10, hell ≈ 0,20–0,45.">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-p" min="0.001" max="0.999" step="0.001" value="0.100">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-p-slider" min="0.001" max="0.999" step="0.001" value="0.100">
<p class="at-hint">Katalogwert (MPC/JPL). C-Typ ≈ 0,04–0,10.</p>
</div>
<div class="at-param">
<label for="at-G">Phasenkoeffizient <em>G</em> <span class="at-tip" tabindex="0" data-tip="Beschreibt, wie stark der Asteroid aufleuchtet, wenn die Sonne fast direkt hinter dem Beobachter steht (Opposition). Standardwert 0,15 gilt für die meisten Asteroiden.">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-G" min="0" max="0.9" step="0.01" value="0.15">
<span class="at-unit">IAU</span>
</div>
<input type="range" id="at-G-slider" min="0" max="0.9" step="0.01" value="0.15">
<p class="at-hint">IAU H-G Standard. Typisch 0,15; dunkel ≈ 0,02.</p>
</div>
</div>
<div style="margin-top:0.6rem;">
<div class="at-derived">
<span class="at-derived-label">→ Bond-Albedo <em>A</em> =</span>
<span class="at-derived-val" id="at-derived-A">–</span>
<span class="at-derived-label" style="margin-left:0.25rem;font-size:0.7rem;">(= p · q, &nbsp;q = 0,290 + 0,684·G)</span>
</div>
</div>
<hr class="at-divider">
<div class="at-param">
<label for="at-H">Absolute Helligkeit <em>H</em> <span style="color:var(--text-dim);font-style:normal;">(optional, für Größe)</span> <span class="at-tip" tabindex="0" data-tip="Die Helligkeit des Asteroiden aus einer genormten Entfernung (1 AE zur Sonne und zum Beobachter). Steht in Katalogen. Je kleiner die Zahl, desto heller — und desto größer der Asteroid.">ⓘ</span></label>
<div class="at-num">
<input type="number" id="at-H" min="-5" max="35" step="0.1" value="" placeholder="z. B. 20,5">
<span class="at-unit">mag</span>
</div>
<p class="at-hint">H-Magnitude aus MPC/JPL → berechnet Durchmesser D.</p>
</div>
</div>
<hr class="at-divider">
<div class="at-albedo-wrap" style="margin-bottom:0.6rem;">
<div class="at-albedo-swatch" id="at-albedo-swatch"></div>
<span class="at-albedo-caption">Albedo-Vorschau (Helligkeit der Oberfläche)</span>
</div>
<hr class="at-divider">
<p class="at-hint" style="margin-bottom:0.4rem;">Bekannte Asteroiden</p>
<div class="at-presets">
<button class="btn btn-sm btn-secondary" data-preset="c">C-Typ</button>
<button class="btn btn-sm btn-secondary" data-preset="s">S-Typ</button>
<button class="btn btn-sm btn-secondary" data-preset="vesta">Vesta</button>
<button class="btn btn-sm btn-secondary" data-preset="eros">Eros</button>
<button class="btn btn-sm btn-secondary" data-preset="bennu">Bennu</button>
<button class="btn btn-sm btn-secondary" data-preset="ryugu">Ryugu</button>
<button class="btn btn-sm btn-secondary" data-preset="neufang">(2183) Neufang</button>
<button class="btn btn-sm btn-danger" id="at-reset">Zurücksetzen</button>
</div>
</div>
<!-- Rechte Spalte -->
<div style="display:flex;flex-direction:column;gap:1.25rem;">
<!-- Temperaturen -->
<div class="card">
<h2>Temperaturen</h2>
<div class="at-results">
<div>
<div class="at-result-row">
<span class="at-result-label">Gleichgewicht</span>
<span class="at-result-sym">T<sub>eq</sub></span>
<span class="at-result-val" id="at-teq-k">–</span>
<span class="at-result-c" id="at-teq-c"></span>
</div>
<p class="at-hint" style="margin-top:0.2rem;">Globaler Mittelwert (gesamte Oberfläche, schnelle Rotation).</p>
</div>
<hr class="at-divider">
<div>
<div class="at-result-row">
<span class="at-result-label">Subsolar</span>
<span class="at-result-sym">T<sub>ss</sub></span>
<span class="at-result-val" id="at-tss-k">–</span>
<span class="at-result-c" id="at-tss-c"></span>
</div>
<p class="at-hint" style="margin-top:0.2rem;">Heißester Punkt direkt unter der Sonne (Zenitwinkel θ = 0°).</p>
</div>
</div>
</div>
<!-- Temperaturprofil -->
<div class="card">
<div style="display:flex;align-items:center;justify-content:space-between;gap:0.5rem;margin-bottom:0.5rem;">
<h2 style="margin:0;">Temperaturprofil Tagseite</h2>
<div class="at-mode-toggle">
<input type="radio" name="at-unit" id="at-unit-k" value="K" checked>
<label for="at-unit-k">K</label>
<input type="radio" name="at-unit" id="at-unit-c" value="C">
<label for="at-unit-c">°C</label>
</div>
</div>
<div class="at-theta-wrap">
<canvas id="at-theta-canvas"></canvas>
</div>
<p class="at-hint" style="margin-top:0.5rem;">
T(θ) = T<sub>ss</sub> · cos(θ)<sup>¼</sup> — Temperatur als Funktion des Sonnenzenitwinkels.
Gestrichelt: T<sub>eq</sub>.
</p>
</div>
<!-- Größenschätzung (nur geo-Modus + H-Eingabe) -->
<div class="card at-size-section" id="at-size-card">
<h2>Größenabschätzung</h2>
<div class="at-diam-row">
<span class="at-diam-val" id="at-diam-val">–</span>
<span class="at-diam-unit">km</span>
</div>
<p class="at-hint" style="margin-top:0.4rem;" id="at-diam-note"></p>
<p class="at-diam-note" style="margin-top:0.3rem;">
D = (1329 km / √p) · 10<sup>−H/5</sup>
</p>
</div>
<!-- Formeln -->
<div class="card">
<h2>Formeln</h2>
<table class="data-table">
<tr><td>T<sub>eq</sub></td><td>⁴√[ (1−A)·S / (4·ε·σ·η) ]</td></tr>
<tr><td>T<sub>ss</sub></td><td>⁴√[ (1−A)·S / (ε·σ·η) ]</td></tr>
<tr><td>T(θ)</td><td>T<sub>ss</sub> · cos(θ)<sup>¼</sup></td></tr>
<tr><td>S</td><td>S₀ / r² &nbsp;&nbsp;(S₀ = 1361 W/m²)</td></tr>
<tr><td>A</td><td>p · q &nbsp;&nbsp;(q = 0,290 + 0,684·G)</td></tr>
<tr><td>D</td><td>(1329 km / √p) · 10<sup>−H/5</sup></td></tr>
<tr><td>σ</td><td>5,670 × 10⁻⁸ W m⁻² K⁻⁴</td></tr>
</table>
<p class="hint" style="margin-top:0.75rem;">
Modell: NEATM · Albedo: IAU H-G-System (Bowell et al. 1989)
</p>
</div>
</div>
</div>
<script>
(function () {
const SIGMA = 5.670374419e-8;
const S0 = 1361.0;
// Phasenintegral (Bowell et al. 1989, IAU H-G)
function phaseIntegral(G) { return 0.290 + 0.684 * G; }
function bondAlbedo(p, G) { return p * phaseIntegral(G); }
function asteroidDiam(H, p) { return (1329.0 / Math.sqrt(p)) * Math.pow(10, -H / 5.0); }
// Presets: geometrische Albedo p + G (Bond A wird daraus abgeleitet)
// Quellen: Dawn, NEAR-Shoemaker, OSIRIS-REx, Hayabusa2, JPL/MPC
const PRESETS = {
// r p G eps eta
default: { r:1.00, p:0.100, G:0.15, eps:0.90, eta:1.00 },
c: { r:2.50, p:0.057, G:0.15, eps:0.90, eta:1.00 }, // C-Typ typisch
s: { r:2.00, p:0.202, G:0.24, eps:0.90, eta:1.00 }, // S-Typ typisch
vesta: { r:2.36, p:0.423, G:0.32, eps:0.90, eta:1.00 }, // Dawn-Mission
eros: { r:1.46, p:0.250, G:0.22, eps:0.90, eta:1.00 }, // NEAR-Shoemaker
bennu: { r:1.13, p:0.044, G:0.02, eps:0.90, eta:1.00 }, // OSIRIS-REx
ryugu: { r:1.19, p:0.045, G:0.09, eps:0.90, eta:1.00 }, // Hayabusa2
neufang: { r:3.01, p:0.115, G:0.15, eps:0.90, eta:1.00 }, // Schätzwert
};
// Eingaben
const numR = document.getElementById('at-r');
const numA = document.getElementById('at-a');
const numP = document.getElementById('at-p');
const numG = document.getElementById('at-G');
const numH = document.getElementById('at-H');
const numEps = document.getElementById('at-eps');
const numEta = document.getElementById('at-eta');
const sldR = document.getElementById('at-r-slider');
const sldA = document.getElementById('at-a-slider');
const sldP = document.getElementById('at-p-slider');
const sldG = document.getElementById('at-G-slider');
const sldEps = document.getElementById('at-eps-slider');
const sldEta = document.getElementById('at-eta-slider');
const modeRadios = document.querySelectorAll('input[name="at-mode"]');
const bondSection = document.getElementById('at-bond-section');
const geoSection = document.getElementById('at-geo-section');
const derivedA = document.getElementById('at-derived-A');
// Ausgaben
const outTeqK = document.getElementById('at-teq-k');
const outTeqC = document.getElementById('at-teq-c');
const outTssK = document.getElementById('at-tss-k');
const outTssC = document.getElementById('at-tss-c');
const swatch = document.getElementById('at-albedo-swatch');
const sizeCard = document.getElementById('at-size-card');
const diamVal = document.getElementById('at-diam-val');
const diamNote = document.getElementById('at-diam-note');
function fmt(val, d) { return val.toFixed(d).replace('.', ','); }
function fmtSI(val) {
if (val >= 1e6) return fmt(val / 1e6, 2) + ' ×10⁶';
if (val >= 1000) return fmt(val, 0);
return fmt(val, 1);
}
function getMode() {
for (const r of modeRadios) { if (r.checked) return r.value; }
return 'bond';
}
function getBondAlbedo() {
if (getMode() === 'geo') {
const p = parseFloat(numP.value);
const G = parseFloat(numG.value);
if (!isFinite(p) || !isFinite(G)) return NaN;
return bondAlbedo(p, G);
}
return parseFloat(numA.value);
}
function calculate() {
const r = parseFloat(numR.value);
const A = getBondAlbedo();
const eps = parseFloat(numEps.value);
const eta = parseFloat(numEta.value);
const mode = getMode();
// Bond-Albedo Anzeige im Geo-Modus
if (mode === 'geo') {
derivedA.textContent = isFinite(A) ? fmt(A, 4) : '–';
}
// Albedo-Swatch (immer mit A)
if (isFinite(A)) {
const v = Math.round(Math.min(1, Math.max(0, A)) * 255);
swatch.style.background = `rgb(${v},${v},${v})`;
}
if (!isFinite(r) || r <= 0 || !isFinite(A) || A < 0 || A >= 1 ||
!isFinite(eps) || eps <= 0 || !isFinite(eta) || eta <= 0) {
outTeqK.textContent = '–'; outTeqC.textContent = '';
outTssK.textContent = '–'; outTssC.textContent = '';
drawThetaProfile(null, null);
updateSize(mode, NaN, NaN);
return;
}
const S = S0 / (r * r);
const Teq = Math.pow((1 - A) * S / (4.0 * eps * SIGMA * eta), 0.25);
const Tss = Math.pow((1 - A) * S / ( eps * SIGMA * eta), 0.25);
outTeqK.textContent = fmt(Teq, 2) + ' K';
outTeqC.textContent = '(' + fmt(Teq - 273.15, 2) + ' °C)';
outTssK.textContent = fmt(Tss, 2) + ' K';
outTssC.textContent = '(' + fmt(Tss - 273.15, 2) + ' °C)';
drawThetaProfile(Tss, Teq);
const p = mode === 'geo' ? parseFloat(numP.value) : NaN;
updateSize(mode, p, NaN);
}
function updateSize(mode, p, _unused) {
if (mode !== 'geo') { sizeCard.classList.remove('is-visible'); return; }
const H = parseFloat(numH.value);
if (!isFinite(H) || !isFinite(p) || p <= 0) {
sizeCard.classList.remove('is-visible');
return;
}
const D = asteroidDiam(H, p);
sizeCard.classList.add('is-visible');
diamVal.textContent = fmtSI(D) + ' km';
diamNote.textContent = 'H = ' + fmt(H, 1) + ' mag, p = ' + fmt(p, 3);
}
function getTempUnit() {
return document.getElementById('at-unit-c').checked ? 'C' : 'K';
}
function toUnit(K) { return getTempUnit() === 'C' ? K - 273.15 : K; }
function unitLabel() { return getTempUnit() === 'C' ? '°C' : 'K'; }
document.querySelectorAll('input[name="at-unit"]').forEach(r =>
r.addEventListener('change', () => {
const Tss = parseFloat(outTssK.textContent);
const Teq = parseFloat(outTeqK.textContent);
if (isFinite(Tss) && isFinite(Teq)) drawThetaProfile(Tss, Teq);
})
);
// T(θ) Profil Canvas
function drawThetaProfile(Tss, Teq) {
const canvas = document.getElementById('at-theta-canvas');
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const cssW = canvas.offsetWidth || 400;
const cssH = 170;
canvas.style.height = cssH + 'px';
canvas.width = Math.round(cssW * dpr);
canvas.height = Math.round(cssH * dpr);
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.clearRect(0, 0, cssW, cssH);
const PAD = { top: 20, right: 14, bottom: 34, left: 52 };
const cW = cssW - PAD.left - PAD.right;
const cH = cssH - PAD.top - PAD.bottom;
// Hintergrund
ctx.fillStyle = '#06080f';
ctx.fillRect(0, 0, cssW, cssH);
if (!Tss || !Teq) {
ctx.fillStyle = '#8899bb';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Bitte Parameter eingeben', cssW / 2, cssH / 2);
return;
}
const TssU = toUnit(Tss);
const TeqU = toUnit(Teq);
const unit = unitLabel();
// Y-Achse: bei Celsius kann Minimum negativ sein → Bereich anpassen
const Tmin_u = getTempUnit() === 'C' ? Math.min(0, toUnit(0)) : 0;
const Tmax_u = TssU * (getTempUnit() === 'C' ? 1.08 : 1.08);
const Trange = Tmax_u - Tmin_u || 1;
const xOf = t => PAD.left + (t / 90) * cW;
const yOf = T => PAD.top + cH - ((T - Tmin_u) / Trange) * cH;
// Grid
ctx.strokeStyle = 'rgba(136,153,187,0.12)';
ctx.lineWidth = 1;
for (const theta of [0, 30, 60, 90]) {
ctx.beginPath(); ctx.moveTo(xOf(theta), PAD.top); ctx.lineTo(xOf(theta), PAD.top + cH); ctx.stroke();
}
const nY = 4;
for (let i = 0; i <= nY; i++) {
const T = Tmin_u + (i / nY) * Trange;
ctx.beginPath(); ctx.moveTo(PAD.left, yOf(T)); ctx.lineTo(PAD.left + cW, yOf(T)); ctx.stroke();
}
// T_eq gestrichelt (cyan)
ctx.strokeStyle = '#4fc3d8';
ctx.lineWidth = 1.5;
ctx.setLineDash([5, 4]);
ctx.beginPath(); ctx.moveTo(PAD.left, yOf(TeqU)); ctx.lineTo(PAD.left + cW, yOf(TeqU)); ctx.stroke();
ctx.setLineDash([]);
// T(θ) Kurve (gold, Füllung darunter)
const tCurve = px => toUnit(Tss * Math.pow(Math.cos((px / cW) * 90 * Math.PI / 180), 0.25));
ctx.beginPath();
for (let px = 0; px <= cW; px++) {
px === 0 ? ctx.moveTo(xOf(px / cW * 90), yOf(tCurve(px))) : ctx.lineTo(xOf(px / cW * 90), yOf(tCurve(px)));
}
ctx.lineTo(PAD.left + cW, PAD.top + cH);
ctx.lineTo(PAD.left, PAD.top + cH);
ctx.closePath();
ctx.fillStyle = 'rgba(201,168,76,0.08)';
ctx.fill();
ctx.strokeStyle = '#c9a84c';
ctx.lineWidth = 2;
ctx.beginPath();
for (let px = 0; px <= cW; px++) {
px === 0 ? ctx.moveTo(xOf(px / cW * 90), yOf(tCurve(px))) : ctx.lineTo(xOf(px / cW * 90), yOf(tCurve(px)));
}
ctx.stroke();
// Achsen
ctx.strokeStyle = 'rgba(136,153,187,0.35)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(PAD.left, PAD.top);
ctx.lineTo(PAD.left, PAD.top + cH);
ctx.lineTo(PAD.left + cW, PAD.top + cH);
ctx.stroke();
// X-Beschriftung
ctx.fillStyle = '#8899bb';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
for (const theta of [0, 30, 60, 90]) {
ctx.fillText(theta + '°', xOf(theta), PAD.top + cH + 5);
}
ctx.fillText('Zenitwinkel θ', PAD.left + cW / 2, PAD.top + cH + 19);
// Y-Beschriftung
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
for (let i = 0; i <= nY; i++) {
const T = Tmin_u + (i / nY) * Trange;
ctx.fillText(Math.round(T) + ' ' + unit, PAD.left - 5, yOf(T));
}
// Labels an den Kurven
ctx.font = 'bold 10px sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#c9a84c';
ctx.fillText('T_ss = ' + TssU.toFixed(0) + ' ' + unit, xOf(0) + 4, yOf(TssU) - 2);
ctx.fillStyle = '#4fc3d8';
ctx.font = '10px sans-serif';
const labelX = Math.min(xOf(45), PAD.left + cW - 90);
ctx.fillText('T_eq = ' + TeqU.toFixed(0) + ' ' + unit, labelX, yOf(TeqU) - 3);
}
// Slider ↔ Zahl synchronisieren
function sync(num, sld) {
num.addEventListener('input', () => { sld.value = num.value; calculate(); });
sld.addEventListener('input', () => { num.value = sld.value; calculate(); });
}
sync(numR, sldR);
sync(numA, sldA);
sync(numP, sldP);
sync(numG, sldG);
sync(numEps, sldEps);
sync(numEta, sldEta);
numH.addEventListener('input', calculate);
// Modus-Toggle
modeRadios.forEach(r => r.addEventListener('change', () => {
const geo = getMode() === 'geo';
bondSection.style.display = geo ? 'none' : '';
geoSection.style.display = geo ? '' : 'none';
if (geo) {
// Bond→Geo: A in p umrechnen (näherungsweise mit aktuellem G)
const A = parseFloat(numA.value);
const G = parseFloat(numG.value) || 0.15;
const q = phaseIntegral(G);
if (isFinite(A) && q > 0) {
const p = Math.min(0.999, Math.max(0.001, A / q));
numP.value = sldP.value = p.toFixed(3);
}
} else {
// Geo→Bond: berechnetes A übernehmen
const A = getBondAlbedo();
if (isFinite(A)) numA.value = sldA.value = A.toFixed(3);
}
calculate();
}));
// Presets
function applyPreset(pr) {
numR.value = sldR.value = pr.r;
numEps.value = sldEps.value = pr.eps;
numEta.value = sldEta.value = pr.eta;
// Preset immer im Geo-Modus anwenden
document.getElementById('at-mode-geo').checked = true;
bondSection.style.display = 'none';
geoSection.style.display = '';
numP.value = sldP.value = pr.p;
numG.value = sldG.value = pr.G;
const A = bondAlbedo(pr.p, pr.G);
numA.value = sldA.value = A.toFixed(3);
calculate();
}
document.querySelectorAll('[data-preset]').forEach(btn => {
btn.addEventListener('click', () => {
const k = btn.dataset.preset;
if (PRESETS[k]) applyPreset(PRESETS[k]);
});
});
document.getElementById('at-reset').addEventListener('click', () => {
document.getElementById('at-mode-bond').checked = true;
bondSection.style.display = '';
geoSection.style.display = 'none';
numR.value = sldR.value = 1.00;
numA.value = sldA.value = 0.050;
numP.value = sldP.value = 0.100;
numG.value = sldG.value = 0.15;
numH.value = '';
numEps.value = sldEps.value = 0.90;
numEta.value = sldEta.value = 1.00;
calculate();
});
window.addEventListener('resize', () => {
const Tss = parseFloat(outTssK.textContent);
const Teq = parseFloat(outTeqK.textContent);
if (isFinite(Tss) && isFinite(Teq)) drawThetaProfile(Tss, Teq);
});
calculate();
})();
</script>
<?php require __DIR__ . '/footer.php'; ?>