import type { Camera, Viewport } from "./camera"; import { layoutScene } from "./layout"; import type { MapPoint } from "./projection"; import type { Scene } from "./scene"; export type MapSelection = { kind: "location"; id: string } | { kind: "flight"; dispatchId: string }; /** * What a tap at `tap` (screen pixels) selects: the nearest ship or location * whose tap target (at least `minTapPx`, Step 10C.6) contains it, as laid out * on screen. Ships win ties, since they are drawn on top. */ export function hitTest( scene: Scene, tap: MapPoint, viewport: Viewport, camera: Camera, ): MapSelection | null { const layout = layoutScene(scene, viewport, camera); let best: { selection: MapSelection; distance: number } | null = null; const consider = (center: MapPoint, size: number, selection: MapSelection, bias: number) => { const distance = Math.hypot(center.x - tap.x, center.y - tap.y); if (distance <= size / 2 && (!best || distance - bias < best.distance)) { best = { selection, distance: distance - bias }; } }; for (const location of layout.locations) { consider(location.center, location.tap, { kind: "location", id: location.id }, 0); } for (const flight of layout.flights) { consider(flight.center, flight.tap, { kind: "flight", dispatchId: flight.dispatchId }, 4); } return (best as { selection: MapSelection } | null)?.selection ?? null; }