import type * as THREE from "three"; import type { AssetCache, Item3D } from "@johnmorrisdotca/kazoku-ui/renderer"; import type { GeoPoint, GlobeView } from "../types.ts"; import type { SatelliteRef } from "../satellites/types.ts"; import { lookAt } from "../view.ts"; import { type LightFade } from "./light-fade.ts"; import type { PathHit } from "../space/paths.ts"; import { type SatelliteScreen, type SatelliteState } from "../space/satellite-layer.ts"; import type { Layer } from "./tiers.ts"; import { type ThreeParts } from "./textures.ts"; import { type TileSet, type TileSources } from "./tile-set.ts"; import type { SkyFrame } from "../sky/frame.ts"; import { type BodyLabelState } from "./body-label.ts"; import { type PlaceSpriteData } from "../markers/place-sprites.ts"; import { type MeteorState } from "../meteors/layer.ts"; import { type BorderState } from "../borders/layer.ts"; import { type GridState } from "../grid/layer.ts"; import { type RouteState } from "../route/layer.ts"; export type { ThreeParts }; type Three = ThreeParts; /** * The glow reaches this many radii from the centre; the globe at 1x is drawn to fit inside it. * * @example * ```ts * import { ATMOSPHERE_OUTER } from "@johnmorrisdotca/chikyu-earth/realistic"; * * console.log(ATMOSPHERE_OUTER); * ``` */ export declare const ATMOSPHERE_OUTER = 1.08; /** What the scene reads at each draw; the element changes it and asks for a draw. */ /** * How the Earth is lit: `sun` (the true light, with the night side dark and the city lights on it), `lit` (always lit, as if by daylight from the camera's side, the day picture and no city lights: the night side is never a black frame) * or `boost` (the true terminator, with the night side raised so the land and coasts read). * * @example * ```ts * import type { LightMode } from "@johnmorrisdotca/chikyu-earth/realistic"; * * const modes: LightMode[] = ["sun", "lit", "boost"]; * console.log(modes.join(", ")); * ``` */ export type LightMode = "sun" | "lit" | "boost"; /** * How the camera is held: `ground` (the default) over a place on the ground, so the Earth keeps still and the clock moves the light across it; `space` held in the stars, so the Earth turns beneath it as the clock runs and the day and night line stays * where the Sun really puts it; `sun` held on the Sun's side, so the day and night line keeps its place on the screen while the Earth turns beneath. * * @example * ```ts * import type { CameraMode } from "@johnmorrisdotca/chikyu-earth/realistic"; * * const modes: CameraMode[] = ["ground", "space", "sun"]; * console.log(modes.join(", ")); * ``` */ export type CameraMode = "ground" | "space" | "sun"; export interface EarthModel { /** The home centre; the shared controls' yaw and tilt are added to it as in the flat globe. */ centre: GeoPoint; view: GlobeView; /** The moment shown, in milliseconds since the epoch: it fixes where the Sun is and how far the clouds have drifted. */ time: number; clouds: boolean; lights: boolean; /** How the Earth is lit (see `LightMode`). */ light: LightMode; /** The cross-fade between the true light and `lit`, stepped at each draw (src/realistic/light-fade.ts). */ lightFade: LightFade; /** How the camera is held (see `CameraMode`). */ camera: CameraMode; /** How many times its real thickness the air is drawn, 1 to 5 (`AIR_THICKNESS_DEFAULT` until the element says otherwise); read at every draw. */ airThickness: number; /** How fast the cloud picture is made to move eastward against the ground, degrees an hour: 0 (the default) keeps the clouds where the picture has them. The picture is one snapshot, not a forecast; a drift is only for life. */ cloudDrift: number; /** For the `space` camera: the Earth's turn against the stars (degrees) when the camera was set; null until the next draw sets it. */ cameraSpin: number | null; /** Game seconds a second (the clock's speed): the satellites are placed from it. */ speed: number; /** The satellites shown, the one chosen, and what the last draw did with them. */ satellites: SatelliteState; /** Where the camera looked at the last draw (the element decides from it whether a new draw is worth it). */ drawn: { view: GlobeView; time: number; radius: number; centre: GeoPoint; } | null; /** The map tiles laid over the pictures while the scene is built: what they are doing, for a page or a test. */ tiles: TileSet | null; /** The page's path under a point of the view (CSS pixels), within a radius, once a draw has been made and one is there. */ pathAt: ((x: number, y: number, radius: number) => Omit | null) | null; /** The satellite at a point of the view (CSS pixels), within a radius, once a draw has been made and one is there. */ pick: ((x: number, y: number, radius: number) => SatelliteRef | null) | null; /** Where a satellite of a shown group is on the screen as of the last draw, or `null`. */ placeSatellite: ((ref: SatelliteRef) => SatelliteScreen | null) | null; /** The pixel ratio the textures are chosen for. */ ratio(): number; /** Meteors seen from space (src/meteors/layer.ts): off unless the element switches them on. */ meteors: MeteorState; /** The name written on the Earth (src/realistic/body-label.ts): off unless the element sets it. */ bodyLabel: BodyLabelState; /** The borders of the countries and of their regions (src/borders/layer.ts): off unless the element switches them on. */ borders: BorderState; /** The meridians and parallels (src/grid/layer.ts): off unless the element switches them on. */ grid: GridState; /** A route between two places (src/route/layer.ts): off unless the element sets it. */ route: RouteState; } export interface EarthDeps { assets: AssetCache; /** Ends with a slash. */ textureBase: () => string; /** The widest texture, 4096 at most. */ cap: () => number; /** The map source of each layer now (the data door's: a source a page added, else the `tiles` folder), or none: the Earth then keeps the package's own pictures. */ sources?: () => TileSources; /** Told once when the tile set has been read: the width in pixels of the finest level of each of its layers. */ onTiles?: (layers: Record) => void; invalidate: () => void; onError?: (error: unknown) => void; onTier?: (layer: Layer, width: number) => void; /** How much the air costs: `full` (12 samples along the ray and 4 toward the Sun) or `light` (6 and 2, for a small or slow device). Read once, when the scene is built. */ air?: () => "full" | "light"; /** The satellites could not be drawn (the three.js parts handed over lack `Points`). */ onSatellitesError?: (error: unknown) => void; /** Where the sky falls on the screen at this draw, for the names of the stars and the constellation lines laid over it: the directions seen along the picture's own axes. */ onSky?: (frame: SkyFrame) => void; /** The name written on the Earth could not be made (the three.js handed over lacks a part, or the label could not be loaded). */ onLabelError?: (error: unknown) => void; /** Told after the tiles were put right for a draw: what the draw shows from each source is known now. */ onTiled?: () => void; /** Told at the start of every draw, before anything is read for it. */ onDraw?: () => void; /** Told at every draw where the disc is, for what is laid over it (markers). */ onPose?: (pose: { lon: number; lat: number; radius: number; cx: number; cy: number; width: number; height: number; }) => void; /** Where the satellite under the pointer is, or null. Called at each draw while one is under the pointer (and once when it is let go). */ onHover?: (place: { x: number; y: number; visible: boolean; } | null) => void; /** The rings the places' layer wants drawn in one draw call, read right after `onPose`. */ placeSprites?: () => PlaceSpriteData | null; /** Told once, when the rings of the places that are not markers of their own were first wanted: whether the picture can draw them (false when the three.js parts handed over lack `Points`). */ onPlaceRings?: (drawn: boolean) => void; /** The painted satellite trail, when the element has one. Read at every draw; nothing is uploaded while `step` returns false. */ paint?: { data(): Uint8Array; width: number; height: number; step(ms: number): boolean; span(): number; on(): boolean; }; } export declare const newModel: () => EarthModel; /** * The disc's radius in CSS pixels in a box of `width` x `height` at `zoom`: it fits the glow in the box at 1x. * * @param width - The box's width in CSS pixels. * @param height - The box's height in CSS pixels. * @param zoom - The view's zoom, 1 at Fit. * @returns The radius in CSS pixels, at least 1. * * @example * ```ts * import { discRadius } from "@johnmorrisdotca/chikyu-earth/realistic"; * * console.log(discRadius(400, 400, 1).toFixed(1)); * ``` */ export declare const discRadius: (width: number, height: number, zoom: number) => number; export { lookAt }; /** * The place the camera looks at for a moment: the home centre as it is (`ground`), the home centre as it was when the camera was set, carried west by the Earth's turn since (`space`), or the home centre read from the Sun's own place (`sun`: `0,0` is straight at noon). * It sets `cameraSpin` when the `space` camera has none yet. Pure apart from that. * * @param model - The model: its centre, camera, camera spin and the moment shown. * @returns The longitude and latitude the view is turned from (yaw and tilt are added to it by `lookAt`). * * @example * ```ts * import { cameraCentre } from "@johnmorrisdotca/chikyu-earth/realistic"; * * const start = Date.UTC(2026, 10, 2, 12); * const model = { centre: { lon: 10, lat: 30 }, camera: "space" as const, cameraSpin: null, time: start }; * cameraCentre(model); * model.time = start + 6 * 3_600_000; * console.log(cameraCentre(model).lon.toFixed(1)); * ``` */ export declare function cameraCentre(model: Pick): GeoPoint; /** An equirectangular sphere: +z is longitude 0 on the equator, +x east, +y north; v is 0 at the north pole (the top row of the picture). */ export declare function sphere(three: Three, columns: number, rows: number): THREE.BufferGeometry; export declare function createEarthItem(model: EarthModel, deps: EarthDeps): Item3D;