61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import type { AstronomyBody, Location } from "@deadhead/content";
|
|
import { Body, HelioVector, KM_PER_AU, RotateVector, Rotation_EQJ_ECL } from "astronomy-engine";
|
|
import type { GameTime } from "./clock";
|
|
|
|
/** A heliocentric point in the ecliptic plane, in million km. */
|
|
export interface Position {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
const MILLION_KM_PER_AU = KM_PER_AU / 1e6;
|
|
const MS_PER_DAY = 86_400_000;
|
|
const J2000 = Date.UTC(2000, 0, 1, 12);
|
|
|
|
/** Fixed Earth→Moon offset (mean lunar distance); the Moon's orbit is ignored. */
|
|
const MOON_OFFSET_MILLION_KM = 0.384;
|
|
|
|
/**
|
|
* Ceres is not in astronomy-engine, so it follows a circular orbit with its
|
|
* real mean radius and period, starting from its approximate J2000 mean longitude.
|
|
*/
|
|
const CERES_RADIUS_AU = 2.7675;
|
|
const CERES_PERIOD_DAYS = 1680.5;
|
|
const CERES_J2000_LONGITUDE_DEG = 160;
|
|
|
|
const eqjToEcliptic = Rotation_EQJ_ECL();
|
|
|
|
function planetPosition(body: Body, gameTime: GameTime): Position {
|
|
const ecliptic = RotateVector(eqjToEcliptic, HelioVector(body, new Date(gameTime)));
|
|
return { x: ecliptic.x * MILLION_KM_PER_AU, y: ecliptic.y * MILLION_KM_PER_AU };
|
|
}
|
|
|
|
function ceresPosition(gameTime: GameTime): Position {
|
|
const days = (gameTime - J2000) / MS_PER_DAY;
|
|
const angle = ((CERES_J2000_LONGITUDE_DEG + (360 * days) / CERES_PERIOD_DAYS) * Math.PI) / 180;
|
|
const radius = CERES_RADIUS_AU * MILLION_KM_PER_AU;
|
|
return { x: radius * Math.cos(angle), y: radius * Math.sin(angle) };
|
|
}
|
|
|
|
export function bodyPosition(body: AstronomyBody, gameTime: GameTime): Position {
|
|
switch (body) {
|
|
case "earth":
|
|
return planetPosition(Body.Earth, gameTime);
|
|
case "moon": {
|
|
const earth = planetPosition(Body.Earth, gameTime);
|
|
return { x: earth.x + MOON_OFFSET_MILLION_KM, y: earth.y };
|
|
}
|
|
case "mars":
|
|
return planetPosition(Body.Mars, gameTime);
|
|
case "ceres":
|
|
return ceresPosition(gameTime);
|
|
case "jupiter":
|
|
return planetPosition(Body.Jupiter, gameTime);
|
|
}
|
|
}
|
|
|
|
/** A location's 2D position at a game time, in million km from the Sun. */
|
|
export function locationPosition(location: Location, gameTime: GameTime): Position {
|
|
return bodyPosition(location.body, gameTime);
|
|
}
|