# Land here: the first block neighborhood

Mathematics and original world engine: **Lars Rocha's RangeBlends**.
This prototype adds a shared detail layer and an explorable local view. It does
not replace the original `World` math or claim to simulate a physical Earth.

## Try it

Open `examples/atlas.html` directly from disk. No install, server, internet
connection, or external assets are required. Keep the seed `k2v-1 k2em`, select
**Gevasu** in the field guide, then **Land here**. You begin near its house;
drag to look around, and walk toward the doorway. Coast and forest locations
provide other neighborhoods. Reopening the file resets the demo.

| Action | Control |
| --- | --- |
| Walk | W A S D, or hold the on-screen arrows |
| Look | Drag the landscape, or use keyboard arrows |
| Jump | Space, or hold Jump |
| Hurry | Shift while walking |
| Return | Return to orbit, or Escape with the landscape focused |

Click the landscape if keyboard controls are not responding. Releasing focus
or hiding the tab clears held movement. Selecting another place or regenerating
the seed ends the previous walk. Return to orbit centers on the tile you reached,
not necessarily your starting tile. There is no player save yet.

## Shared coordinate contract

```python
import nedgelaw as ned

world = ned.World("k2v-1 k2em")
ground = ned.Landscape("k2v-1 k2em")
assert ground.height_at(383 * 16, 127 * 16) == world.height(511, 127)
chunk = ground.chunk(511, 127)
columns = chunk["columns"]
boxes = chunk["props"]
```

```js
const ned = require("./js");
const ground = new ned.Landscape("k2v-1 k2em");
const height = ground.heightAt(383 * 16, 127 * 16);
const chunk = ground.chunk(511, 127);
console.table(chunk.columns.slice(0, 5));
```

- World `(x, y)` addresses become horizontal ground `(x, z)` addresses.
- One World tile spans 16 blocks on each axis. One chunk is 16×16 columns.
- When experimenting with edge continuations, pass the same `edge_mode` and
  `edge_strength` to World and Landscape. The atlas does this automatically;
  comparison switches remain anchored to the selected tile. See `EXTENSION.md`.
- Heights interpolate four integer World anchors. Indexed fine detail is zero
  on anchor grid lines and bounded by 1.5 vertical blocks between them.
- This interpolation is new geometry, not a higher-resolution globe texture.
- Heights remain normalized 0–1. Solid columns occupy block heights
  `0 <= y < floor(32 + (height - 0.37) * 64)`. That upper boundary is `surface`.
- `water` is true below normalized height 0.37. The visible water plane is at
  block height 32; the walker floats in water rather than sinking indefinitely.
- Columns are returned row-major, local z outermost and local x innermost.
- Props are axis-aligned boxes: `min` inclusive and `max` exclusive. Their
  material is `stone`, `wood`, `wall`, `roof`, or `leaves`. They belong entirely
  to their owning chunk. World tree/village flags determine eligibility.
- Houses have floors, walls, a doorway, and a tiered roof. Settlement categories
  currently share one small-house design; they are not full generated towns.
- Negative coordinates work in the library. Column coordinates are integers
  within ±1 billion; continuous height sampling accepts finite values in that
  range. Chunk indices are −62,500,000 through 62,499,999 inclusive.
- Python/JavaScript use equivalent calculations; tests compare normalized
  heights to 12 decimal places and tested block surfaces/props exactly.
  This is not a universal bitwise guarantee across all numeric runtimes.

## Streaming and rendering

`examples/ground-model.js` owns the demo's bounded neighborhood cache, mesh
builder, collision walker, and camera matrices. `examples/ground-view.js` owns
WebGL rendering, input, and GPU resources. The reusable package owns only the
terrain/prop data and can feed a different renderer or dataframe workflow.

The renderer builds at most two nearby chunk meshes per animation frame, keeps
up to 49 visible chunks, and releases distant GPU buffers. Fog softens the
view boundary. Meshes use exposed faces; water is opaque. Initial chunks appear
over a short sequence of frames. Nothing is downloaded or pre-generated for
the entire block world. Walking physics queries the same columns and prop
boxes, even while a newly entered chunk is waiting to be drawn.

The atlas covers 512×256 World tiles, or 8192×4096 blocks. Walking stops at this
rectangle's edges. The globe still wraps that finite patch onto a sphere;
landing does not make longitude seams or polar distortion disappear.

## Deliberate limits and next steps

This version is heightfield terrain with block props, not a Minecraft engine.
There is no digging, placement, inventory, cave system, multiplayer, persistence,
or integration of the separate river engine into this terrain. Trees are
sparse World-derived props, not a forestry simulation. Some steep terrain
requires detours; one-block steps are automatic and taller cliffs block walking.

Ground exploration requires WebGL. On failure, the return button remains
available; the orbit view retains its Canvas fallback. Automated tests cover
coordinates, seams, data parity, collisions, controls/resource lifecycle, and
standalone script assembly, not visual browser or GPU compatibility QA.

Good later increments: save a local player position, store block edits as sparse
overrides keyed by seed and coordinates, add richer vegetation, then introduce
distant terrain levels of detail. None requires replacing Lars's World anchors.


## Relief

`Landscape(seed, ..., relief=1)` / `new ned.Landscape(seed, {relief})`, 0.25 to
3: how tall a height index is, in blocks per 64 (`height_scale = 64 * relief`).
The normalized height and every tile-anchor identity are unchanged; `surface`
is `floor(32 + (height - 0.37) * 64 * relief)`. The atlas carries the same
number in its terrain record, so the globe's relief, the props painted on the
detail window, and the blocks you walk on agree. Python and JavaScript columns
and props match at `relief=2.5` (`tests/test_rangeblend.py`).
