<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>3D-Planeten Mittel</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<style>
body { margin: 0; background: #000; overflow: hidden; }
#container { width: 100vw; height: 100vh; position: relative; }
#controls { position: absolute; top: 10px; left: 10px; z-index: 10; background: rgba(0,0,0,0.7); color: #fff; padding: 10px; border-radius: 8px; }
.planet-label { position: absolute; color: #fff; background: rgba(0,0,0,0.7); padding: 2px 6px; border-radius: 4px; font-size: 12px; pointer-events: none; z-index: 5; white-space: nowrap; }
#info-panel {
position: absolute;
top: 80px;
left: 30px;
background: rgba(0,0,0,0.92);
color: #fff;
padding: 18px 22px;
border-radius: 12px;
z-index: 2000;
min-width: 220px;
font-size: 15px;
box-shadow: 0 2px 12px #0008;
display: none;
}
.tooltip {
position: absolute;
background: rgba(30,30,30,0.98);
color: #ffd700;
padding: 5px 10px;
border-radius: 6px;
font-size: 13px;
pointer-events: none;
z-index: 3000;
white-space: nowrap;
box-shadow: 0 2px 8px #0006;
display: none;
}
</style>
</head>
<body>
<div id="container">
<div id="controls">
<button onclick="toggleLabels()">Beschriftungen ein/aus</button>
<button onclick="window.location.href='planet3d_web_aussen_datum.php'">Zu den äußeren Planeten/Datum</button>
<button onclick="window.location.href='/astro/index.php'">Zurück zum Hauptmenü</button>
</div>
<div id="date-controls" style="position: absolute; top: 20px; right: 20px; background: rgba(0,0,0,0.8); color: #fff; padding: 16px 20px; border-radius: 10px; z-index: 1000;">
<button id="animBtn" type="button" style="margin-bottom:12px; background: linear-gradient(45deg, #00c3ff, #005bea); color: white; padding: 8px 16px; border: none; border-radius: 5px; cursor: pointer; width:100%;" onclick="toggleAnimation()">Animation stoppen</button><br>
<div style="margin-bottom:12px;">
<label for="simSpeed" style="color:#ffd700;">Simulationsgeschwindigkeit:</label><br>
<input type="range" id="simSpeed" min="0.1" max="100" step="0.1" value="1" style="width:120px; vertical-align:middle;" oninput="updateSimSpeed(this.value)">
<input type="number" id="simSpeedNum" min="0.1" max="100" step="0.1" value="1" style="width:60px; margin-left:6px; vertical-align:middle;" oninput="updateSimSpeed(this.value)">
<span style="font-size:12px; color:#ffd700;">Tag/Sek</span>
</div>
<form onsubmit="event.preventDefault(); showPositionsForDate();">
<label for="dateInput" style="color:#ffd700;">Datum wählen:</label><br>
<input type="date" id="dateInput" name="dateInput" style="margin: 8px 0 12px 0; padding: 6px; border-radius: 5px; border: none;">
<button type="submit" style="background: linear-gradient(45deg, #ff6b6b, #ee5a24); color: white; padding: 8px 16px; border: none; border-radius: 5px; cursor: pointer;">Positionen anzeigen</button>
</form>
<div id="date-info" style="margin-top: 8px; font-size: 13px; color: #ffd700;"></div>
<div id="sim-date-info" style="margin-top: 4px; font-size: 13px; color: #ffd700;"></div>
<div id="explain-info" style="margin-top: 10px; font-size: 12px; color: #fff; background: rgba(0,0,0,0.7); border-radius: 8px; padding: 10px 14px; max-width: 380px;">
<b>Begriffe:</b><br>
<b>Aktuelle Umlaufposition:</b> Winkel des Planeten auf seiner Bahn (in Grad, 0° = Frühlingspunkt).<br>
<b>Umlaufzeit:</b> Zeit für einen vollständigen Umlauf um die Sonne (in Tagen).<br>
<b>Abstand zur Sonne (aktuell):</b> Tatsächlicher Abstand des Planeten zur Sonne am gewählten Datum (in Astronomischen Einheiten, 1 AU = mittlerer Abstand Erde-Sonne).<br>
<b>Exzentrizität:</b> Maß für die Abweichung der Bahn von der Kreisform (0 = Kreis, nahe 1 = sehr langgestreckt).<br>
<b>Perihel:</b> Kleinster Abstand des Planeten zur Sonne (in AU).<br>
<b>Aphel:</b> Größter Abstand des Planeten zur Sonne (in AU).
</div>
</div>
<div id="legend" style="position: absolute; left: 20px; bottom: 20px; background: rgba(0,0,0,0.8); color: #fff; padding: 14px 18px; border-radius: 10px; font-size: 13px; z-index: 100; min-width: 180px;">
<b>Legende</b><br>
<span style="color:#ffff00;">●</span> Sonne<br>
<span style="color:#8c7853;">●</span> Merkur<br>
<span style="color:#ffa500;">●</span> Venus<br>
<span style="color:#0066ff;">●</span> Erde<br>
<span style="color:#cccccc;">●</span> Mond (Erde)<br>
<span style="color:#ff4400;">●</span> Mars<br>
<span style="color:#aaaaaa;">●</span> Phobos (Mars)<br>
<span style="color:#bbbbbb;">●</span> Deimos (Mars)<br>
<span style="color:#ffffff;">●</span> Ceres (Asteroid)
</div>
<div id="info-panel"></div>
</div>
<div class="tooltip" id="planet-tooltip"></div>
<script>
let scene, camera, renderer, controls;
let planets = [];
let labels = [];
let labelsVisible = true;
let animationRunning = true;
const planetData = [
{ name: 'Sonne', color: 0xffff00, radius: 2, distance: 0, realAU: 0, ecc: 0, peri: 0, aphe: 0, speed: 0 },
{ name: 'Merkur', color: 0x8c7853, radius: 0.3, distance: 5, realAU: 0.387, ecc: 0.2056, peri: 0.307, aphe: 0.467, speed: 0 },
{ name: 'Venus', color: 0xffa500, radius: 0.5, distance: 7.5, realAU: 0.723, ecc: 0.0067, peri: 0.718, aphe: 0.728, speed: 0 },
{ name: 'Erde', color: 0x0066ff, radius: 0.7, distance: 10, realAU: 1.0, ecc: 0.0167, peri: 0.983, aphe: 1.017, speed: 0 },
{ name: 'Mars', color: 0xff4400, radius: 0.5, distance: 12.5, realAU: 1.524, ecc: 0.0934, peri: 1.381, aphe: 1.666, speed: 0 },
{ name: 'Ceres', color: 0xffffff, radius: 0.13, distance: 16, realAU: 2.77, ecc: 0.079, peri: 2.557, aphe: 2.983, speed: 0 },
{ name: 'Vesta', color: 0xdddddd, radius: 0.11, distance: 16.7, realAU: 2.36, ecc: 0.089, peri: 2.151, aphe: 2.572, speed: 0 },
{ name: 'Pallas', color: 0x99ccff, radius: 0.10, distance: 17.3, realAU: 2.77, ecc: 0.231, peri: 2.131, aphe: 3.412, speed: 0 },
{ name: 'Hygiea', color: 0xffeebb, radius: 0.09, distance: 18, realAU: 3.14, ecc: 0.117, peri: 2.776, aphe: 3.504, speed: 0 }
];
let moon = null;
let moonAngle = Math.random() * Math.PI * 2;
const moonDistance = 1.2;
let marsMoons = [];
let marsMoonAngles = [Math.random() * Math.PI * 2, Math.random() * Math.PI * 2];
const marsMoonData = [
{ name: 'Phobos', color: 0xaaaaaa, radius: 0.08, distance: 0.7, speed: 0.09 },
{ name: 'Deimos', color: 0xbbbbbb, radius: 0.06, distance: 1.2, speed: 0.05 }
];
// Umlaufzeiten in Tagen (NASA/JPL)
const orbitalPeriods = {
'Merkur': 87.969,
'Venus': 224.701,
'Erde': 365.256,
'Mars': 686.980,
'Ceres': 1680.0,
'Vesta': 1325.0,
'Pallas': 1687.0,
'Hygiea': 2034.0,
'Mond': 27.321661,
'Phobos': 0.31891,
'Deimos': 1.263,
};
// Simulationsfaktor: wie viele Tage pro Sekunde?
let simDaysPerSecond = 1;
let lastFrameTime = Date.now();
// Add global variable to store last planet positions from backend
let lastPlanetPositions = null;
let angleTargets = [];
let requestInProgress = false;
let backendError = false;
// Add global variables for simulation date
let simDate = null;
let simDateFrac = 0; // Fractional day
let lastSimDateString = null;
// Add global cache for seasons per year
let seasonsCache = {};
let seasonsLoading = {};
// Helper: fetch seasons for a year from backend
function fetchSeasonsForYear(year, cb) {
if (seasonsCache[year]) {
cb(seasonsCache[year]);
return;
}
if (seasonsLoading[year]) {
// Already loading, wait and retry
setTimeout(() => fetchSeasonsForYear(year, cb), 200);
return;
}
seasonsLoading[year] = true;
fetch('get_seasons.php?year=' + year)
.then(r => r.json())
.then(data => {
seasonsCache[year] = data;
cb(data);
})
.catch(() => {
cb(null);
});
}
// Helper: determine season from ISO dates
function getSeasonFromAstronomical(dateObj, seasons) {
// dateObj: JS Date (UTC), seasons: {spring_equinox, summer_solstice, autumn_equinox, winter_solstice}
if (!seasons || !seasons.spring_equinox) return null;
const d = dateObj;
const spring = new Date(seasons.spring_equinox);
const summer = new Date(seasons.summer_solstice);
const autumn = new Date(seasons.autumn_equinox);
const winter = new Date(seasons.winter_solstice);
if (d >= spring && d < summer) return 'Frühling';
if (d >= summer && d < autumn) return 'Sommer';
if (d >= autumn && d < winter) return 'Herbst';
// Winter: von Winteranfang bis Frühlingsanfang (Jahreswechsel beachten)
return 'Winter';
}
function getAngularSpeed(period) {
return 2 * Math.PI / period; // rad/Tag
}
// Ergänze Bahnparameter für jeden Planeten (Werte: NASA/JPL, Einheiten: a in AU, i/Omega/omega in Grad)
const orbitalElements = {
'Merkur': { a: 0.387, e: 0.2056, i: 7.005, Omega: 48.331, omega: 29.124 },
'Venus': { a: 0.723, e: 0.0067, i: 3.394, Omega: 76.680, omega: 54.884 },
'Erde': { a: 1.000, e: 0.0167, i: 0.000, Omega: -11.260, omega: 114.207 },
'Mars': { a: 1.524, e: 0.0934, i: 1.850, Omega: 49.558, omega: 286.502 },
'Ceres': { a: 2.77, e: 0.079, i: 10.593, Omega: 80.305, omega: 73.597 },
'Vesta': { a: 2.36, e: 0.089, i: 7.140, Omega: 103.851, omega: 150.647 },
'Pallas': { a: 2.77, e: 0.231, i: 34.837, Omega: 173.087, omega: 310.048 },
'Hygiea': { a: 3.14, e: 0.117, i: 3.842, Omega: 283.646, omega: 312.346 }
};
// Hilfsfunktion: Grad zu Radiant
function deg2rad(deg) { return deg * Math.PI / 180; }
// Hilfsfunktion: Kepler-Gleichung lösen (numerisch, für Exzentrische Anomalie E)
function solveKepler(M, e, tol=1e-6) {
let E = M;
let dE;
do {
dE = (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E));
E -= dE;
} while (Math.abs(dE) > tol);
return E;
}
// Hilfsfunktion: 3D-Position aus Bahnparametern und wahrer Anomalie
function orbitalToEcliptic(a, e, i, Omega, omega, nu) {
// a: große Halbachse (AU), e: Exzentrizität, i/Omega/omega/nu: Radiant
// 1. Abstand r
let r = a * (1 - e * e) / (1 + e * Math.cos(nu));
// 2. Position in Bahnebene
let x_orb = r * Math.cos(nu);
let y_orb = r * Math.sin(nu);
let z_orb = 0;
// 3. Rotationen: erst um omega (z), dann i (x), dann Omega (z)
// Rotationsmatrix (z-omega, x-i, z-Omega)
let x1 = x_orb * Math.cos(omega) - y_orb * Math.sin(omega);
let y1 = x_orb * Math.sin(omega) + y_orb * Math.cos(omega);
let z1 = z_orb;
let x2 = x1;
let y2 = y1 * Math.cos(i) - z1 * Math.sin(i);
let z2 = y1 * Math.sin(i) + z1 * Math.cos(i);
let x3 = x2 * Math.cos(Omega) - y2 * Math.sin(Omega);
let y3 = x2 * Math.sin(Omega) + y2 * Math.cos(Omega);
let z3 = z2;
return { x: x3, y: z3, z: y3 }; // y3 als z, z3 als y (Three.js: y=up)
}
// Funktion: Ellipsenbahn als Linie zeichnen
function createOrbitEllipse(planetName, color=0xbbbbbb) {
const el = orbitalElements[planetName];
if (!el) return null;
const points = [];
for (let f = 0; f <= 360; f += 2) {
let nu = deg2rad(f);
let pos = orbitalToEcliptic(el.a * 10, el.e, deg2rad(el.i), deg2rad(el.Omega), deg2rad(el.omega), nu); // *10 für Skalierung
points.push(new THREE.Vector3(pos.x, pos.y, pos.z));
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: color, opacity: 0.5, transparent: true });
return new THREE.Line(geometry, material);
}
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000011);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 30);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Licht
scene.add(new THREE.AmbientLight(0xffffff, 0.7));
let sunLight = new THREE.PointLight(0xffffff, 2, 100);
sunLight.position.set(0,0,0);
scene.add(sunLight);
// Planeten
planetData.forEach((data, idx) => {
let geometry = new THREE.SphereGeometry(data.radius, 32, 32);
let material = new THREE.MeshPhongMaterial({ color: data.color });
let mesh = new THREE.Mesh(geometry, material);
mesh.position.x = data.distance; // wird später überschrieben
scene.add(mesh);
planets.push({ mesh, data, angle: Math.random() * Math.PI * 2 });
// Orbit als Ellipse
if (data.name !== 'Sonne' && orbitalElements[data.name]) {
let orbit = createOrbitEllipse(data.name);
if (orbit) scene.add(orbit);
}
});
// Mond zur Erde
let moonGeometry = new THREE.SphereGeometry(0.15, 16, 16);
let moonMaterial = new THREE.MeshPhongMaterial({ color: 0xcccccc });
moon = new THREE.Mesh(moonGeometry, moonMaterial);
scene.add(moon);
// Asteroidengürtel als Punktwolke
const asteroidCount = 200;
const astMin = 15;
const astMax = 17;
const asteroidGeometry = new THREE.BufferGeometry();
const asteroidPositions = [];
for (let i = 0; i < asteroidCount; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = astMin + Math.random() * (astMax - astMin);
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
const y = (Math.random() - 0.5) * 0.2;
asteroidPositions.push(x, y, z);
}
asteroidGeometry.setAttribute('position', new THREE.Float32BufferAttribute(asteroidPositions, 3));
const asteroidMaterial = new THREE.PointsMaterial({ color: 0xcccccc, size: 0.12 });
const asteroids = new THREE.Points(asteroidGeometry, asteroidMaterial);
scene.add(asteroids);
// Marsmonde
let mars = planets[4];
marsMoons = marsMoonData.map((moon, i) => {
let geo = new THREE.SphereGeometry(moon.radius, 12, 12);
let mat = new THREE.MeshPhongMaterial({ color: moon.color });
let mesh = new THREE.Mesh(geo, mat);
scene.add(mesh);
return { mesh, data: moon };
});
createLabels();
animate();
window.addEventListener('resize', onWindowResize);
}
function createLabels() {
const container = document.getElementById('container');
// Planeten-Labels
Object.keys(planets).forEach((planetName, i) => {
const planet = planets[planetName];
let label = document.createElement('div');
label.className = 'planet-label';
label.textContent = planet.data.name;
label.style.display = labelsVisible ? 'block' : 'none';
container.appendChild(label);
labels[i] = label;
});
// Mond-Label Erde
let moonLabel = document.createElement('div');
moonLabel.className = 'planet-label';
moonLabel.textContent = 'Mond';
moonLabel.style.display = labelsVisible ? 'block' : 'none';
container.appendChild(moonLabel);
labels.push(moonLabel);
// Marsmonde-Labels
marsMoons.forEach((m, i) => {
let moonLabel = document.createElement('div');
moonLabel.className = 'planet-label';
moonLabel.textContent = marsMoonData[i].name;
moonLabel.style.display = labelsVisible ? 'block' : 'none';
container.appendChild(moonLabel);
labels.push(moonLabel);
});
}
function updateLabels() {
if (!labelsVisible) {
labels.forEach(label => label.style.display = 'none');
return;
}
// Planeten
Object.keys(planets).forEach((planetName, i) => {
const planet = planets[planetName];
let label = labels[i];
let vector = planet.mesh.position.clone();
vector.project(camera);
const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
const y = (-(vector.y * 0.5) + 0.5) * window.innerHeight;
if (vector.z < 1) {
label.style.display = 'block';
label.style.left = x + 'px';
label.style.top = y + 'px';
label.style.transform = 'translate(-50%, -50%)';
} else {
label.style.display = 'none';
}
});
// Mond Erde
let moonLabel = labels[Object.keys(planets).length];
let moonVector = moon.position.clone();
moonVector.project(camera);
const mx = (moonVector.x * 0.5 + 0.5) * window.innerWidth;
const my = (-(moonVector.y * 0.5) + 0.5) * window.innerHeight;
if (moonVector.z < 1) {
moonLabel.style.display = 'block';
moonLabel.style.left = mx + 'px';
moonLabel.style.top = my + 'px';
moonLabel.style.transform = 'translate(-50%, -50%)';
} else {
moonLabel.style.display = 'none';
}
// Marsmonde
marsMoons.forEach((m, i) => {
let moonLabel = labels[Object.keys(planets).length + 1 + i];
let moonVector = m.mesh.position.clone();
moonVector.project(camera);
const mx = (moonVector.x * 0.5 + 0.5) * window.innerWidth;
const my = (-(moonVector.y * 0.5) + 0.5) * window.innerHeight;
if (moonVector.z < 1) {
moonLabel.style.display = 'block';
moonLabel.style.left = mx + 'px';
moonLabel.style.top = my + 'px';
moonLabel.style.transform = 'translate(-50%, -50%)';
} else {
moonLabel.style.display = 'none';
}
});
}
function toggleLabels() {
labelsVisible = !labelsVisible;
updateLabels();
}
function toggleAnimation() {
animationRunning = !animationRunning;
document.getElementById('animBtn').textContent = animationRunning ? 'Animation stoppen' : 'Animation starten';
}
function animate() {
requestAnimationFrame(animate);
let now = Date.now();
let deltaSec = (now - lastFrameTime) / 1000;
lastFrameTime = now;
if (animationRunning && simDate) {
// Advance simulation date
let daysToAdd = deltaSec * simDaysPerSecond;
simDateFrac += daysToAdd;
if (simDateFrac >= 1 || simDateFrac <= -1) {
let wholeDays = Math.trunc(simDateFrac);
simDate.setUTCDate(simDate.getUTCDate() + wholeDays);
simDateFrac -= wholeDays;
}
updateSimDateDisplay();
// Query backend if date changed by at least 1 day
let d = new Date(simDate.getTime() + simDateFrac * 86400000);
let yyyy = d.getUTCFullYear();
let mm = String(d.getUTCMonth() + 1).padStart(2, '0');
let dd = String(d.getUTCDate()).padStart(2, '0');
let dateStr = `${yyyy}-${mm}-${dd}`;
if (dateStr !== lastSimDateString && !requestInProgress && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
requestInProgress = true;
fetch('/astro/astronomy/planets/get_planet_positions.php?date=' + encodeURIComponent(dateStr))
.then(r => r.json())
.then(data => {
if (data.error) {
backendError = true;
requestInProgress = false;
return;
}
lastPlanetPositions = data.planets;
// Backend-Daten werden in der Hauptschleife verwendet, nicht hier direkt
backendError = false;
document.getElementById('date-info').textContent = '';
lastSimDateString = dateStr;
requestInProgress = false;
})
.catch(err => {
backendError = true;
document.getElementById('date-info').textContent = 'Fehler beim Laden der Planetenpositionen.';
requestInProgress = false;
});
}
planets.forEach((p, i) => {
if (p.data.name === 'Sonne') return;
const el = orbitalElements[p.data.name];
if (!el) return;
// Hole aktuellen Winkel (Backend: ekliptische Länge lambda, in Grad)
let lambda = null;
if (lastPlanetPositions) {
const backendMap = { 'Merkur': 'mercury', 'Venus': 'venus', 'Erde': 'earth', 'Mars': 'mars', 'Ceres': 'ceres', 'Vesta': 'vesta', 'Pallas': 'pallas', 'Hygiea': 'hygiea' };
let key = backendMap[p.data.name];
if (key && lastPlanetPositions[key] && typeof lastPlanetPositions[key].angle === 'number') {
lambda = deg2rad(-lastPlanetPositions[key].angle); // MINUS für korrekte Richtung!
}
}
// Wenn kein Backend: lokale Simulation (KORRIGIERT: gegen Uhrzeigersinn)
if (lambda === null) {
// Aktualisiere Winkel basierend auf Umlaufzeit (gegen Uhrzeigersinn)
let period = orbitalPeriods[p.data.name];
if (period) {
let omega = getAngularSpeed(period);
let deltaAngle = omega * deltaSec * simDaysPerSecond;
p.angle -= deltaAngle; // MINUS für gegen Uhrzeigersinn!
}
lambda = p.angle;
}
// Berechne wahre Anomalie nu aus lambda, Omega, omega
// lambda = Omega + omega + nu => nu = lambda - Omega - omega
let nu = lambda - deg2rad(el.Omega) - deg2rad(el.omega);
// 3D-Position
let pos = orbitalToEcliptic(el.a * 10, el.e, deg2rad(el.i), deg2rad(el.Omega), deg2rad(el.omega), nu); // *10 für Skalierung
p.mesh.position.set(pos.x, pos.y, pos.z);
});
// Mond umkreist Erde (realistisch)
let earth = planets[3];
if (earth) {
let moonPeriod = orbitalPeriods['Mond'];
let moonOmega = getAngularSpeed(moonPeriod);
let moonDelta = moonOmega * deltaSec * simDaysPerSecond;
moonAngle -= moonDelta;
moon.position.x = earth.mesh.position.x + Math.cos(moonAngle) * moonDistance;
moon.position.z = earth.mesh.position.z + Math.sin(moonAngle) * moonDistance;
moon.position.y = earth.mesh.position.y;
}
// Marsmonde umkreisen Mars (realistisch)
let mars = planets[4];
if (mars) {
// Phobos
let phobosPeriod = orbitalPeriods['Phobos'];
let phobosOmega = getAngularSpeed(phobosPeriod);
let phobosDelta = phobosOmega * deltaSec * simDaysPerSecond;
marsMoonAngles[0] -= phobosDelta;
marsMoons[0].mesh.position.x = mars.mesh.position.x + Math.cos(marsMoonAngles[0]) * marsMoonData[0].distance;
marsMoons[0].mesh.position.z = mars.mesh.position.z + Math.sin(marsMoonAngles[0]) * marsMoonData[0].distance;
marsMoons[0].mesh.position.y = mars.mesh.position.y;
// Deimos (Korrektur: gleiche Richtung wie Phobos)
let deimosPeriod = orbitalPeriods['Deimos'];
let deimosOmega = getAngularSpeed(deimosPeriod);
let deimosDelta = deimosOmega * deltaSec * simDaysPerSecond;
marsMoonAngles[1] -= deimosDelta;
marsMoons[1].mesh.position.x = mars.mesh.position.x + Math.cos(marsMoonAngles[1]) * marsMoonData[1].distance;
marsMoons[1].mesh.position.z = mars.mesh.position.z + Math.sin(marsMoonAngles[1]) * marsMoonData[1].distance;
marsMoons[1].mesh.position.y = mars.mesh.position.y;
}
} else {
updateSimDateDisplay();
}
updateLabels();
controls.update();
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function showPositionsForDate() {
const date = document.getElementById('dateInput').value;
document.getElementById('date-info').textContent = date ? `Gewähltes Datum: ${date}` : '';
document.getElementById('sim-date-info').textContent = '';
if (!date) return;
// Set simulation date to selected date
simDate = new Date(date + 'T00:00:00Z');
simDateFrac = 0;
lastSimDateString = date; // Setze auf das neue Datum, damit der Animations-Loop nicht sofort einen weiteren Request absetzt
updateSimDateDisplay();
fetch('/astro/astronomy/planets/get_planet_positions.php?date=' + encodeURIComponent(date))
.then(r => r.json())
.then(data => {
if (data.error) {
document.getElementById('date-info').textContent = 'Fehler: ' + data.error;
return;
}
lastPlanetPositions = data.planets;
// Mapping: planetData[1]=Merkur, [2]=Venus, [3]=Erde, [4]=Mars
const planetOrder = ['mercury','venus','earth','mars'];
planetOrder.forEach((name, i) => {
if (data.planets && data.planets[name] && typeof data.planets[name].angle === 'number') {
// Umrechnung Grad -> Bogenmaß
const angleRad = -data.planets[name].angle * Math.PI / 180;
angleTargets[i+1] = angleRad; // Setze Zielwinkel
planets[i+1].angle = angleRad; // Sofort setzen
planets[i+1].mesh.position.x = Math.cos(angleRad) * planets[i+1].data.distance;
planets[i+1].mesh.position.z = Math.sin(angleRad) * planets[i+1].data.distance;
}
});
// Info anzeigen
document.getElementById('date-info').textContent = `Gewähltes Datum: ${data.date}`;
updateSimDateDisplay();
// Visueller Hinweis
let info = document.createElement('div');
info.id = 'position-update-info';
info.style.position = 'absolute';
info.style.top = '60px';
info.style.right = '40px';
info.style.background = 'rgba(0,180,0,0.85)';
info.style.color = '#fff';
info.style.padding = '10px 18px';
info.style.borderRadius = '8px';
info.style.fontSize = '16px';
info.style.zIndex = 3000;
info.textContent = 'Positionen aktualisiert!';
document.body.appendChild(info);
setTimeout(() => {
if (info.parentNode) info.parentNode.removeChild(info);
}, 2000);
})
.catch(err => {
document.getElementById('date-info').textContent = 'Fehler beim Laden der Planetenpositionen.';
});
}
function updateSimSpeed(val) {
simDaysPerSecond = parseFloat(val);
document.getElementById('simSpeed').value = simDaysPerSecond;
document.getElementById('simSpeedNum').value = simDaysPerSecond;
}
document.addEventListener('DOMContentLoaded', function() {
updateSimSpeed(1);
});
init();
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let hoveredObj = null;
let selectedObj = null;
const infoPanel = document.getElementById('info-panel');
const tooltip = document.getElementById('planet-tooltip');
function getSeasonForDate(dateObj) {
// dateObj: JS Date (UTC)
const year = dateObj.getUTCFullYear();
// Astronomical seasons (northern hemisphere, UTC)
// Approximate dates (can be refined with ephemeris if needed)
const spring = new Date(Date.UTC(year, 2, 20)); // 20. März
const summer = new Date(Date.UTC(year, 5, 21)); // 21. Juni
const autumn = new Date(Date.UTC(year, 8, 23)); // 23. Sept
const winter = new Date(Date.UTC(year, 11, 21)); // 21. Dez
if (dateObj >= spring && dateObj < summer) return 'Frühling';
if (dateObj >= summer && dateObj < autumn) return 'Sommer';
if (dateObj >= autumn && dateObj < winter) return 'Herbst';
// Winter: von Winteranfang bis Frühlingsanfang (Jahreswechsel beachten)
return 'Winter';
}
function getObjectInfo(obj) {
let name = obj.data?.name || obj.data?.label || 'Objekt';
let period = orbitalPeriods[name] || null;
let angleDeg = obj.angle !== undefined ? (obj.angle * 180 / Math.PI) % 360 : null;
if (angleDeg < 0) angleDeg += 360;
let realAU = obj.data?.realAU;
let ecc = obj.data?.ecc;
let peri = obj.data?.peri;
let aphe = obj.data?.aphe;
// Lookup true distance from lastPlanetPositions if available
let backendDist = null;
if (lastPlanetPositions && name !== 'Sonne') {
// Map JS name to backend key
const backendMap = { 'Merkur': 'mercury', 'Venus': 'venus', 'Erde': 'earth', 'Mars': 'mars', 'Ceres': 'ceres', 'Vesta': 'vesta', 'Pallas': 'pallas', 'Hygiea': 'hygiea' };
let key = backendMap[name];
if (key && lastPlanetPositions[key] && typeof lastPlanetPositions[key].distance_au === 'number') {
backendDist = lastPlanetPositions[key].distance_au;
}
}
let info = `<b>${name}</b><br>`;
if (angleDeg !== null) info += `Aktuelle Umlaufposition: ${angleDeg.toFixed(2)}°<br>`;
if (period) info += `Umlaufzeit: ${period} Tage<br>`;
if (backendDist) info += `Abstand zur Sonne (aktuell): ${backendDist} AU<br>`;
else if (realAU && name !== 'Sonne') info += `Abstand zur Sonne: ${realAU} AU<br>`;
if (ecc && name !== 'Sonne') info += `Exzentrizität: ${ecc}<br>`;
if (peri && name !== 'Sonne') info += `Perihel: ${peri} AU<br>`;
if (aphe && name !== 'Sonne') info += `Aphel: ${aphe} AU<br>`;
// Jahreszeit für Erde anzeigen (exakt)
if (name === 'Erde' && simDate) {
let d = new Date(simDate.getTime() + simDateFrac * 86400000);
let year = d.getUTCFullYear();
let fallbackSeason = getSeasonForDate(d); // fallback
let seasonText = '';
fetchSeasonsForYear(year, function(seasons) {
let season = getSeasonFromAstronomical(d, seasons);
let show = season || fallbackSeason;
infoPanel.innerHTML = info + `<b>Jahreszeit (Nordhalbkugel):</b> ${show}<br>`;
});
// Platzhalter, wird nachgeladen
info += `<b>Jahreszeit (Nordhalbkugel):</b> ...<br>`;
}
if (name === 'Mond') info += `Abstand zur Erde: ${moonDistance} AU (skaliert)`;
if (name === 'Phobos' || name === 'Deimos') info += `Abstand zu Mars: ${marsMoonData[name === 'Phobos' ? 0 : 1].distance} AU (skaliert)`;
return info;
}
function showTooltip(obj, x, y) {
tooltip.innerHTML = obj.data?.name || obj.data?.label || 'Objekt';
tooltip.style.left = (x + 12) + 'px';
tooltip.style.top = (y - 8) + 'px';
tooltip.style.display = 'block';
}
function hideTooltip() {
tooltip.style.display = 'none';
}
function showInfoPanel(obj) {
infoPanel.innerHTML = getObjectInfo(obj);
infoPanel.style.display = 'block';
}
function hideInfoPanel() {
infoPanel.style.display = 'none';
}
// Mouse move: Tooltip
renderer.domElement.addEventListener('mousemove', function(event) {
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
let objects = planets.map(p => p.mesh).concat([moon], marsMoons.map(m => m.mesh));
let intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
let mesh = intersects[0].object;
let obj = planets.find(p => p.mesh === mesh) ||
(moon === mesh ? {mesh: moon, data: {name: 'Mond'}, angle: moonAngle} : null) ||
marsMoons.find((m, i) => m.mesh === mesh ? {mesh: m.mesh, data: {name: marsMoonData[i].name}, angle: marsMoonAngles[i]} : null);
hoveredObj = obj;
showTooltip(obj, event.clientX, event.clientY);
} else {
hoveredObj = null;
hideTooltip();
}
});
// Mouse out: Tooltip
renderer.domElement.addEventListener('mouseleave', function() {
hoveredObj = null;
hideTooltip();
});
// Mouse click: Info-Panel
renderer.domElement.addEventListener('click', function(event) {
if (hoveredObj) {
selectedObj = hoveredObj;
showInfoPanel(selectedObj);
} else {
selectedObj = null;
hideInfoPanel();
}
});
// Info-Panel beim Start ausblenden
hideInfoPanel();
function updateSimDateDisplay() {
if (!simDate) {
document.getElementById('sim-date-info').textContent = '';
return;
}
// Format YYYY-MM-DD
let d = new Date(simDate.getTime() + simDateFrac * 86400000);
let yyyy = d.getUTCFullYear();
let mm = String(d.getUTCMonth() + 1).padStart(2, '0');
let dd = String(d.getUTCDate()).padStart(2, '0');
let dateStr = `${yyyy}-${mm}-${dd}`;
document.getElementById('sim-date-info').textContent = `Simuliertes Datum: ${dateStr}`;
}
</script>
</body>
</html>