diff --git a/wbld/orbits.html b/wbld/orbits.html
index 543d7d6..15f6cc3 100644
--- a/wbld/orbits.html
+++ b/wbld/orbits.html
@@ -46,6 +46,7 @@
+
@@ -65,7 +66,7 @@
return `Year ${year} - ${seasons[seasonIndex]} ${dayOfSeason}`;
}
- // Phaser configuration (responsive)
+ // Phaser configuration
const config = {
type: Phaser.AUTO,
width: window.innerWidth,
@@ -85,48 +86,49 @@
let globalGraphics;
let dateText;
- let simulationTime = 0; // in days (simulation time)
- let simulationSpeed = 5; // speed multiplier
- let isPlaying = false; // Start paused
- let isRewinding = false; // Control forward/backward
+ let simulationTime = 0;
+ let simulationSpeed = 5;
+ let isPlaying = false;
+ let isRewinding = false;
-
- // Orbital periods (in days)
- const planetPeriod = 396; // Senthara's orbital year
- const planetRotationPeriod = 1; // Planet rotates fully in 1 day
+ // Orbital parameters (in days and relative units)
+ const planetPeriod = 396;
+ const planetRotationPeriod = 1;
const kerielPeriod = 27;
const arkaenPeriod = 82;
const minianPeriod = 98;
- // Orbital radii (computed relative to window size)
+ // 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;
- // Containers for the planet and the moons
+ // 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"
- });
+ dateText = this.add.text(10, game.scale.height - 30, "", { font: "20px Arial", fill: "#ffffff" });
recalcOrbitRadii();
+ setRandomAngleOffsets();
- // Create a container for Senthara (the planet)
planetContainer = this.add.container(0, 0);
-
- // Draw the planet body (green circle) with a marker for rotation.
let planetBody = this.add.graphics();
planetBody.fillStyle(0x00FF00, 1);
planetBody.fillCircle(0, 0, 12);
- // Marker line (points to the right)
planetBody.lineStyle(2, 0x000000, 1);
planetBody.beginPath();
planetBody.moveTo(0, 0);
@@ -134,29 +136,22 @@
planetBody.strokePath();
planetContainer.add(planetBody);
- // Create a night overlay (semi-transparent dark half-circle)
let nightOverlay = this.add.graphics();
nightOverlay.fillStyle(0x000000, 0.5);
- // Draw a half-circle (180° arc) with radius 12
nightOverlay.slice(0, 0, 12, 0, Math.PI, false);
nightOverlay.fillPath();
- // Save as a property for updating its rotation later.
planetContainer.nightOverlay = nightOverlay;
planetContainer.add(nightOverlay);
- // Create containers for each moon.
- kerielContainer = createMoonContainer(this, 6, 0xFF0000); // Keriel: red
- arkaenContainer = createMoonContainer(this, 5, 0x0000FF); // Arkaen: blue
- minianContainer = createMoonContainer(this, 5, 0xFFFFFF); // Minian: white
+ kerielContainer = createMoonContainer(this, 6, 0xFF0000);
+ arkaenContainer = createMoonContainer(this, 5, 0x0000FF);
+ minianContainer = createMoonContainer(this, 5, 0xFFFFFF);
- // Listen for window resize events.
this.scale.on('resize', (gameSize) => {
recalcOrbitRadii();
dateText.setPosition(10, gameSize.height - 30);
});
-
- // --- UI Control Event Handlers ---
const playPauseButton = document.getElementById("playPauseButton");
const rewindButton = document.getElementById("rewindButton");
const speedSelect = document.getElementById("speedSelect");
@@ -164,29 +159,25 @@
playPauseButton.addEventListener("click", () => {
isPlaying = !isPlaying;
playPauseButton.textContent = isPlaying ? "Pause" : "Play";
- if (isPlaying) isRewinding = false; // Stop rewinding when playing
+ if (isPlaying) isRewinding = false;
});
-
rewindButton.addEventListener("click", () => {
isRewinding = !isRewinding;
rewindButton.textContent = isRewinding ? "Forward" : "Rewind";
- if(isRewinding) isPlaying = false; // Pause when rewinding
- playPauseButton.textContent = "Play"; // Reset play/pause button
+ if (isRewinding) isPlaying = false;
+ playPauseButton.textContent = "Play";
});
speedSelect.addEventListener("change", () => {
simulationSpeed = parseFloat(speedSelect.value);
});
}
-
- // Helper function to create a moon container with a circular body and a marker.
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);
- // Marker line indicating the "front" of the moon.
moonBody.lineStyle(2, 0x000000, 1);
moonBody.beginPath();
moonBody.moveTo(0, 0);
@@ -199,87 +190,122 @@
// Recalculate orbital radii based on the current window size.
function recalcOrbitRadii() {
const minDim = Math.min(window.innerWidth, window.innerHeight);
- planetOrbitRadius = minDim * 0.3; // 30% of the smaller dimension
+ 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) {
- // Advance or rewind simulation time based on state.
if (isPlaying) {
simulationTime += (delta * simulationSpeed) / 1000;
} else if (isRewinding) {
simulationTime -= (delta * simulationSpeed) / 1000;
}
-
- // Get center of the screen (position of the star).
const centerX = game.scale.width / 2;
const centerY = game.scale.height / 2;
- // Clear global graphics.
globalGraphics.clear();
-
- // Draw the central star.
globalGraphics.fillStyle(0xFFFF00, 1);
globalGraphics.fillCircle(centerX, centerY, 20);
- // Draw Senthara's orbital path (around the star).
- globalGraphics.lineStyle(1, 0x555555, 1);
- globalGraphics.strokeCircle(centerX, centerY, planetOrbitRadius);
+ // 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;
- // Calculate Senthara's (the planet's) position along its orbit.
- const planetOrbitAngle = Phaser.Math.DegToRad((360 * (simulationTime % planetPeriod)) / planetPeriod - 90);
- const planetX = centerX + planetOrbitRadius * Math.cos(planetOrbitAngle);
- const planetY = centerY + planetOrbitRadius * Math.sin(planetOrbitAngle);
-
- // Update the planet container's position.
- planetContainer.x = planetX;
- planetContainer.y = planetY;
- // Set the planet's self-rotation (day/night cycle) with a 1-day period.
const planetRotationAngle = Phaser.Math.DegToRad((360 * (simulationTime % planetRotationPeriod)) / planetRotationPeriod);
planetContainer.rotation = planetRotationAngle;
- // Update the night overlay so that the dark half covers the side away from the star.
- const subsolarAngle = Phaser.Math.Angle.Between(planetX, planetY, centerX, centerY);
- // The night overlay's rotation is adjusted relative to the planet's rotation.
+ 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).
- // Keriel:
- const kerielOrbitAngle = Phaser.Math.DegToRad((360 * (simulationTime % kerielPeriod)) / kerielPeriod - 90);
- const kerielX = planetX + kerielOrbitRadius * Math.cos(kerielOrbitAngle);
- const kerielY = planetY + kerielOrbitRadius * Math.sin(kerielOrbitAngle);
- kerielContainer.x = kerielX;
- kerielContainer.y = kerielY;
- // Ensure tidal locking: set Keriel's rotation so its marker points toward Senthara.
- kerielContainer.rotation = Phaser.Math.Angle.Between(kerielX, kerielY, planetX, planetY);
+ // 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);
- // Arkaen:
- const arkaenOrbitAngle = Phaser.Math.DegToRad((360 * (simulationTime % arkaenPeriod)) / arkaenPeriod - 90);
- const arkaenX = planetX + arkaenOrbitRadius * Math.cos(arkaenOrbitAngle);
- const arkaenY = planetY + arkaenOrbitRadius * Math.sin(arkaenOrbitAngle);
- arkaenContainer.x = arkaenX;
- arkaenContainer.y = arkaenY;
- arkaenContainer.rotation = Phaser.Math.Angle.Between(arkaenX, arkaenY, planetX, planetY);
+ 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);
- // Minian:
- const minianOrbitAngle = Phaser.Math.DegToRad((360 * (simulationTime % minianPeriod)) / minianPeriod - 90);
- const minianX = planetX + minianOrbitRadius * Math.cos(minianOrbitAngle);
- const minianY = planetY + minianOrbitRadius * Math.sin(minianOrbitAngle);
- minianContainer.x = minianX;
- minianContainer.y = minianY;
- minianContainer.rotation = Phaser.Math.Angle.Between(minianX, minianY, planetX, planetY);
+ 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);
- // Optionally, redraw the moons' orbital paths (around Senthara).
- globalGraphics.lineStyle(1, 0x888888, 1);
- globalGraphics.strokeCircle(planetX, planetY, kerielOrbitRadius);
- globalGraphics.strokeCircle(planetX, planetY, arkaenOrbitRadius);
- globalGraphics.strokeCircle(planetX, planetY, minianOrbitRadius);
+ // 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);
- // Update the Senthara calendar date text.
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
}