# API

## createTopography(element, settings?, options?)

Mounts a canvas inside `element` (it fills the element) and returns an instance.

- `settings`: partial settings, see [params.md](params.md). Fields: `{ look, shape, location, seed, lines, terrain, motion, layout, theme, mode, elevation, extent, detail, quality, camera }`. `mode` is `'2d' | '3d'` and defaults to `'2d'`. `quality` is `'auto' | 'low' | 'medium' | 'high'` and defaults to `'auto'`. `auto` starts a phone at low and a desktop at high, then steps down if presented frames miss 60 Hz, or 120 Hz once a frame under 12 ms has been seen. It does not step back up.
- `options`: `{ maxPixelRatio = 2, maxPixels = 4147200, forceWebGL1 = false, benchmark = false }`. `benchmark` keeps a 3D view drawing while it is visible so `stats` can be read.

Instance:

- `update(partialSettings)`: merges, clamps and applies live. Only changed uniforms reach the GPU.
- `snapshot({ frame?, width?, height?, region?, motion?, patch?, field? }) => Promise<Blob>`: renders one PNG off-screen. `frame` is the animation time in ms. Give one side to keep the aspect ratio. `region` (`{ x, y, width, height }`, fractions from the top-left) keeps only that slice of the view, drawn at full detail. `motion` draws the 3D view along that camera path at its pose for `frame`, like that moment of `record`. `patch` and `field` draw this image from temporary settings and a temporary height grid, then put the live view back, so the page does not flash and a second WebGL context is not required. The live canvas is not resized. Rejects without WebGL. In 3D it includes the current camera.
- `record({ seconds?, fps?, width?, height?, frame?, format?, motion? }) => Promise<Blob>`: renders a 3D clip with a moving camera. `format` is `'mp4'` (default) or `'webm'`. `motion` is `orbit`, `push`, `flyover`, `drone` or `still`. The live canvas is not resized. Rejects without WebGL or when the browser cannot encode that format.
- `setHeightfield({ samples, size, aspect } | null)`: draws your own height grid until the `location` setting changes. `null` brings back the generated terrain.
- `setInsets({ top, right, bottom, left } | null)`: the space page UI covers on each side of the view, in CSS px. The 3D picture slides (a lens shift, not a camera move) so the terrain centres in the part left free. 2D, `snapshot` and `record` ignore it and centre in their own frame. `insets` reads it back.
- `clampPan(panX, panZ) => [x, z]`: the pan kept over the terrain as it is drawn now.
- `pose`: `{ azimuth, elevation, radius, panX, panZ }`, where the 3D camera is now along its path.
- `stats`: `{ fps, frameMs, mesh, quality }` while a 3D view is drawing. `frameMs` is the time spent drawing one frame. `mesh` is the segments along one side, or 0 in 2D. `quality` is the tier actually drawing: `low`, `medium` or `high`.
- `destroy()`: stops the loop, removes observers and the canvas, and releases the WebGL context.
- `settings`: the current resolved settings.
- `status`: `'webgl2' | 'webgl1' | 'fallback' | 'lost' | 'destroyed'`.
- `canvas`: the canvas element.

Behaviour:

- Uses WebGL2, then WebGL1. Without either, or while the context is lost, the element shows the paper colour. A restored context resumes. If the browser never restores it, the canvas is replaced and the view comes back on its own. Drawing does not call `gl.finish`, and the backing store is left alone when a resize reports the same size.
- `prefers-reduced-motion: reduce` renders a single still frame.
- Animation pauses when the element is off-screen or the tab is hidden.
- Pixel budget: `quality` caps the device pixel ratio, the pixel count and the mesh. `maxPixelRatio` and `maxPixels` are the ceiling above that.

## <topographic-view>

`import 'topographic/element'` registers the element. Attributes: `look`, `shape`, `location`, `seed`, `theme`, `ink`, `paper`, `mode`, `elevation`, `extent`, `detail`, `quality`, `azimuth`, `camera-elevation`, `radius`, `camera-motion`, `camera-speed`, `pan-x`, `pan-z`, plus one per numeric setting (see [params.md](params.md)). Attribute changes apply live, and a removed attribute returns to its default. `element.topographic` is the instance, and `element.snapshot(opts)` is a shortcut.

## React: <Topography> and useTopography

`<Topography look shape location lines terrain motion layout theme seed mode elevation extent detail quality camera options onReady {...divProps}>` renders a div with the background, and children on top. It is SSR-safe: the server renders an empty div.

`useTopography(settings, options)` returns a ref to put on any element. `ref.topographic` is the instance while mounted.

## topographic/locations

`import { LOCATIONS, loadLocation, fetchLocation } from 'topographic/locations'`.

- `LOCATIONS`: the built-in places, each `{ id, name, bbox, aspect, defaults }`. `defaults` is `{ camera: { azimuth, elevation, radius }, elevation, extent, lines, layout }`.
  - `istanbul`: Istanbul, west 28.45, south 40.78, east 29.65, north 41.36
  - `cyprus`: Cyprus, west 32.07126, south 34.41845, east 34.93823, north 35.92088
  - `matterhorn`: Matterhorn & Zermatt, west 7.441, south 45.883, east 7.959, north 46.117
  - `geirangerfjord`: Geirangerfjord, west 6.474, south 61.925, east 7.626, north 62.275
  - `grand-canyon`: Grand Canyon, west -112.434, south 35.945, east -111.766, north 36.295
  - `yosemite`: Yosemite Valley, west -119.755, south 37.647, east -119.415, north 37.823
  - `torres-del-paine`: Torres del Paine, west -73.478, south -51.175, east -72.622, north -50.825
  - `fuji`: Mount Fuji, west 138.433, south 35.278, east 139.007, north 35.582
  - `everest`: Everest, west 86.757, south 27.892, east 87.093, north 28.084
  - `kilimanjaro`: Kilimanjaro, west 37, south -3.303, east 37.72, north -2.837
- `loadLocation(id) => Promise<{ samples, size, aspect, minMeters, maxMeters, zoom }>`: a built-in place's 256 × 256 grid. Each place is its own dynamic import of 35 to 150 KB (25 to 115 KB compressed), so a page downloads only the places it uses. The `location` setting calls it for you.
- `fetchLocation({ west, south, east, north }, { relief?, smooth?, tiles?, signal? }) => Promise<{ samples, size, aspect, minMeters, maxMeters, zoom, tiles }>`: downloads Terrarium elevation tiles (AWS Open Data) for any area and makes the same 256 × 256 grid as the playground. The shorter side must be at least 1.5 km, the longer side at most 300 km, and the sides within 3:1. `relief` is `dramatic` (default) or `measured`; `smooth` is `light`, `medium` (default) or `strong`; `tiles` is a Terrarium URL template with `{z}`, `{x}` and `{y}`. Pass the result as `location`.
- `decodeGrid16(bytes) => Promise<{ samples, size }>`: reads a grid saved as a square 16-bit greyscale PNG, north at the top, the format of the built-in places.
- `areaProblem(bbox)` returns `'order' | 'small' | 'large' | 'aspect' | null`, and `areaSpan(bbox)` the size in metres.

## Helpers

`resolveSettings`, `mergeSettings`, `toUniforms`, `fromAttributes`, `toAttributes`, `toQuery`, `fromQuery`, `parseColor`, `resolveTheme`, plus the data exports `PARAMS`, `SHAPES` and `LOOKS`, and `findLook(id)`.
