deadhead-space-freight-tycoon/packages/sim/src/positions.ts

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);
}