Files
Test-rep/wbld/orbits.html

312 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Senthara Orbit, Rotation & Day/Night Visualization</title>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.js"></script>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
/* Style for the control panel */
#controlPanel {
position: absolute;
bottom: 10px;
right: 10px;
background-color: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 5px;
font-family: Arial, sans-serif;
color: white;
}
#controlPanel button, #controlPanel select {
margin: 5px;
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
background-color: #eee;
color: black;
cursor: pointer;
}
#controlPanel button:hover {
background-color: #ddd;
}
#controlPanel select:hover {
background-color: #ddd;
}
</style>
</head>
<body>
<div id="controlPanel">
<button id="playPauseButton">Play</button>
<button id="rewindButton">Rewind</button>
<select id="speedSelect">
<option value="1">x1</option>
<option value="2">x2</option>
<option value="5" selected>x5</option>
<option value="10">x10</option>
<option value="100">x100</option>
<option value="1000">x1000</option>
<option value="1000">x1000</option>
<option value="10000">x10000</option>
<option value="100000">x100000</option>
<option value="1000000">x1000000</option>
</select>
</div>
<script>
// Function to calculate the current Senthara date from simulationTime (in days)
function getSentharaDate(simDays) {
const offset = 134;
const X = Math.floor(simDays + offset);
const year = Math.floor(X / 396) + 1;
const dayOfYear = (X % 396) + 1;
const seasonIndex = Math.floor((dayOfYear - 1) / 99);
const dayOfSeason = dayOfYear - seasonIndex * 99;
const seasons = ["Summer", "Autumn", "Winter", "Spring"];
return `Year ${year} - ${seasons[seasonIndex]} ${dayOfSeason}`;
}
// Phaser configuration
const config = {
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: "#000000",
scene: {
preload: preload,
create: create,
update: update
},
scale: {
mode: Phaser.Scale.RESIZE
}
};
const game = new Phaser.Game(config);
let globalGraphics;
let dateText;
let simulationTime = 0;
let simulationSpeed = 5;
let isPlaying = false;
let isRewinding = false;
// Orbital parameters (in days and relative units)
const planetPeriod = 396;
const planetRotationPeriod = 1;
const kerielPeriod = 27;
const arkaenPeriod = 82;
const minianPeriod = 98;
// Orbital radii and eccentricities
let planetOrbitRadius, kerielOrbitRadius, arkaenOrbitRadius, minianOrbitRadius;
const planetEccentricity = 0.2; // Example eccentricity for Senthara
const kerielEccentricity = 0.1;
const arkaenEccentricity = 0.15;
const minianEccentricity = 0.05;
// Angle offsets
let planetAngleOffset;
let kerielAngleOffset;
let arkaenAngleOffset;
let minianAngleOffset;
// Containers
let planetContainer;
let kerielContainer, arkaenContainer, minianContainer;
function preload() {
// No external assets are needed.
}
function create() {
globalGraphics = this.add.graphics();
dateText = this.add.text(10, game.scale.height - 30, "", { font: "20px Arial", fill: "#ffffff" });
recalcOrbitRadii();
setRandomAngleOffsets();
planetContainer = this.add.container(0, 0);
let planetBody = this.add.graphics();
planetBody.fillStyle(0x00FF00, 1);
planetBody.fillCircle(0, 0, 12);
planetBody.lineStyle(2, 0x000000, 1);
planetBody.beginPath();
planetBody.moveTo(0, 0);
planetBody.lineTo(12, 0);
planetBody.strokePath();
planetContainer.add(planetBody);
let nightOverlay = this.add.graphics();
nightOverlay.fillStyle(0x000000, 0.5);
nightOverlay.slice(0, 0, 12, 0, Math.PI, false);
nightOverlay.fillPath();
planetContainer.nightOverlay = nightOverlay;
planetContainer.add(nightOverlay);
kerielContainer = createMoonContainer(this, 6, 0xFF0000);
arkaenContainer = createMoonContainer(this, 5, 0x0000FF);
minianContainer = createMoonContainer(this, 5, 0xFFFFFF);
this.scale.on('resize', (gameSize) => {
recalcOrbitRadii();
dateText.setPosition(10, gameSize.height - 30);
});
const playPauseButton = document.getElementById("playPauseButton");
const rewindButton = document.getElementById("rewindButton");
const speedSelect = document.getElementById("speedSelect");
playPauseButton.addEventListener("click", () => {
isPlaying = !isPlaying;
playPauseButton.textContent = isPlaying ? "Pause" : "Play";
if (isPlaying) isRewinding = false;
});
rewindButton.addEventListener("click", () => {
isRewinding = !isRewinding;
rewindButton.textContent = isRewinding ? "Forward" : "Rewind";
if (isRewinding) isPlaying = false;
playPauseButton.textContent = "Play";
});
speedSelect.addEventListener("change", () => {
simulationSpeed = parseFloat(speedSelect.value);
});
}
function createMoonContainer(scene, radius, color) {
let moonContainer = scene.add.container(0, 0);
let moonBody = scene.add.graphics();
moonBody.fillStyle(color, 1);
moonBody.fillCircle(0, 0, radius);
moonBody.lineStyle(2, 0x000000, 1);
moonBody.beginPath();
moonBody.moveTo(0, 0);
moonBody.lineTo(radius, 0);
moonBody.strokePath();
moonContainer.add(moonBody);
return moonContainer;
}
// Recalculate orbital radii based on the current window size.
function recalcOrbitRadii() {
const minDim = Math.min(window.innerWidth, window.innerHeight);
planetOrbitRadius = minDim * 0.3;
kerielOrbitRadius = planetOrbitRadius * 0.25;
arkaenOrbitRadius = planetOrbitRadius * 0.35;
minianOrbitRadius = planetOrbitRadius * 0.45;
}
// Sets random angle offsets.
function setRandomAngleOffsets() {
planetAngleOffset = Phaser.Math.RND.between(0, 360);
kerielAngleOffset = Phaser.Math.RND.between(0, 360);
arkaenAngleOffset = Phaser.Math.RND.between(0, 360);
minianAngleOffset = Phaser.Math.RND.between(0, 360);
}
// Calculates position on an ellipse.
function calculateEllipsePosition(centerX, centerY, semiMajorAxis, eccentricity, period, time, angleOffset) {
const meanAnomaly = (2 * Math.PI * (time % period)) / period;
// Approximate eccentric anomaly using Newton-Raphson method
let eccentricAnomaly = approximateEccentricAnomaly(meanAnomaly, eccentricity);
// Calculate the true anomaly
const trueAnomaly = 2 * Math.atan2(
Math.sqrt(1 + eccentricity) * Math.sin(eccentricAnomaly / 2),
Math.sqrt(1 - eccentricity) * Math.cos(eccentricAnomaly / 2)
);
// Calculate distance from the center
const distance = semiMajorAxis * (1 - eccentricity * Math.cos(eccentricAnomaly));
// Calculate x and y coordinates, with angle offset
const x = centerX + distance * Math.cos(trueAnomaly + Phaser.Math.DegToRad(angleOffset));
const y = centerY + distance * Math.sin(trueAnomaly + Phaser.Math.DegToRad(angleOffset));
return { x, y };
}
// Approximates the eccentric anomaly using the Newton-Raphson method.
function approximateEccentricAnomaly(meanAnomaly, eccentricity) {
let E = meanAnomaly; // Initial guess
let delta = 1;
// Iterate until the change is small enough (e.g., less than 0.0001)
while (delta > 0.0001) {
let nextE = E - (E - eccentricity * Math.sin(E) - meanAnomaly) / (1 - eccentricity * Math.cos(E));
delta = Math.abs(nextE - E);
E = nextE;
}
return E;
}
function update(time, delta) {
if (isPlaying) {
simulationTime += (delta * simulationSpeed) / 1000;
} else if (isRewinding) {
simulationTime -= (delta * simulationSpeed) / 1000;
}
const centerX = game.scale.width / 2;
const centerY = game.scale.height / 2;
globalGraphics.clear();
globalGraphics.fillStyle(0xFFFF00, 1);
globalGraphics.fillCircle(centerX, centerY, 20);
// Calculate and draw Senthara's elliptical orbit
const planetPos = calculateEllipsePosition(centerX, centerY, planetOrbitRadius, planetEccentricity, planetPeriod, simulationTime, planetAngleOffset);
planetContainer.x = planetPos.x;
planetContainer.y = planetPos.y;
const planetRotationAngle = Phaser.Math.DegToRad((360 * (simulationTime % planetRotationPeriod)) / planetRotationPeriod);
planetContainer.rotation = planetRotationAngle;
const subsolarAngle = Phaser.Math.Angle.Between(planetPos.x, planetPos.y, centerX, centerY);
planetContainer.nightOverlay.rotation = subsolarAngle + Math.PI / 2 - planetContainer.rotation;
// Calculate and update moon positions (relative to Senthara, using elliptical orbits)
const kerielPos = calculateEllipsePosition(planetPos.x, planetPos.y, kerielOrbitRadius, kerielEccentricity, kerielPeriod, simulationTime, kerielAngleOffset);
kerielContainer.x = kerielPos.x;
kerielContainer.y = kerielPos.y;
kerielContainer.rotation = Phaser.Math.Angle.Between(kerielPos.x, kerielPos.y, planetPos.x, planetPos.y);
const arkaenPos = calculateEllipsePosition(planetPos.x, planetPos.y, arkaenOrbitRadius, arkaenEccentricity, arkaenPeriod, simulationTime, arkaenAngleOffset);
arkaenContainer.x = arkaenPos.x;
arkaenContainer.y = arkaenPos.y;
arkaenContainer.rotation = Phaser.Math.Angle.Between(arkaenPos.x, arkaenPos.y, planetPos.x, planetPos.y);
const minianPos = calculateEllipsePosition(planetPos.x, planetPos.y, minianOrbitRadius, minianEccentricity, minianPeriod, simulationTime, minianAngleOffset);
minianContainer.x = minianPos.x;
minianContainer.y = minianPos.y;
minianContainer.rotation = Phaser.Math.Angle.Between(minianPos.x, minianPos.y, planetPos.x, planetPos.y);
// Draw elliptical orbits (optional, for visualization)
drawEllipticalOrbit(globalGraphics, centerX, centerY, planetOrbitRadius, planetEccentricity, planetAngleOffset);
drawEllipticalOrbit(globalGraphics, planetPos.x, planetPos.y, kerielOrbitRadius, kerielEccentricity, kerielAngleOffset);
drawEllipticalOrbit(globalGraphics, planetPos.x, planetPos.y, arkaenOrbitRadius, arkaenEccentricity, arkaenAngleOffset);
drawEllipticalOrbit(globalGraphics, planetPos.x, planetPos.y, minianOrbitRadius, minianEccentricity, minianAngleOffset);
dateText.setText(getSentharaDate(simulationTime));
}
// Helper function to draw an elliptical orbit using Phaser graphics.
function drawEllipticalOrbit(graphics, centerX, centerY, semiMajorAxis, eccentricity, angleOffset) {
const points = [];
const steps = 100; // Number of points to create a smooth curve
for (let i = 0; i <= steps; i++) {
const meanAnomaly = (2 * Math.PI * i) / steps;
const eccentricAnomaly = approximateEccentricAnomaly(meanAnomaly, eccentricity);
const trueAnomaly = 2 * Math.atan2(
Math.sqrt(1 + eccentricity) * Math.sin(eccentricAnomaly / 2),
Math.sqrt(1 - eccentricity) * Math.cos(eccentricAnomaly / 2)
);
const distance = semiMajorAxis * (1 - eccentricity * Math.cos(eccentricAnomaly));
const x = centerX + distance * Math.cos(trueAnomaly + Phaser.Math.DegToRad(angleOffset));
const y = centerY + distance * Math.sin(trueAnomaly + Phaser.Math.DegToRad(angleOffset));
points.push(new Phaser.Math.Vector2(x, y));
}
graphics.lineStyle(1, 0x888888, 1); // Set orbit line style
graphics.strokePoints(points, true); // Draw the ellipse
}
</script>
</body>
</html>