import type { Location } from "@deadhead/content"; import { bodyPosition, type GameTime, type Position } from "@deadhead/sim"; /** * Map coordinates: the Sun at (0, 0), the edge of the map at radius 1, y down. * Real distances are compressed so the inner planets and Jupiter fit together. */ export interface MapPoint { x: number; y: number; } /** Heliocentric radius (million km) shown at the edge of the map. */ export const MAP_EDGE_MILLION_KM = 1000; /** Moons are drawn this far from their planet (map units), whatever the real distance. */ export const MOON_MAP_OFFSET = 0.04; /** * Compressed radial scale: real distance from the Sun (million km) to map * radius. Square root keeps the order (farther stays farther) while bringing * Jupiter (~780M km) within a few times Earth's (~150M km) distance. */ export function radialScale(millionKm: number): number { return Math.sqrt(Math.max(0, millionKm) / MAP_EDGE_MILLION_KM); } /** A heliocentric position on the map: same angle, compressed radius. */ export function toMapPoint(position: Position): MapPoint { const r = Math.hypot(position.x, position.y); if (r === 0) return { x: 0, y: 0 }; const k = radialScale(r) / r; // Screens grow downwards; flip y so orbits run counter-clockwise as seen from above. return { x: position.x * k, y: -position.y * k }; } /** * Where a location is drawn. The Moon sits a fixed small distance from Earth * in its true direction, since at map scale it would overlap Earth. */ export function locationMapPoint(location: Location, gameTime: GameTime): MapPoint { if (location.body !== "moon") return toMapPoint(bodyPosition(location.body, gameTime)); const earth = bodyPosition("earth", gameTime); const moon = bodyPosition("moon", gameTime); const dx = moon.x - earth.x; const dy = -(moon.y - earth.y); const length = Math.hypot(dx, dy) || 1; const base = toMapPoint(earth); return { x: base.x + (dx / length) * MOON_MAP_OFFSET, y: base.y + (dy / length) * MOON_MAP_OFFSET, }; }