# `TerrainGenerator`
[🔗](https://github.com/fabio-t/terrain-generator/blob/main/lib/terrain_generator.ex#L1)

Procedural terrain generation with fractal noise, hydraulic erosion and
river extraction, backed by a Rust NIF.

The pipeline has four stages, each optional after the first:

  1. **Base heightmap** — fractal (fBm) OpenSimplex noise
  2. **Island mask** — radial falloff with noise-perturbed coastlines
  3. **Hydraulic erosion** — a droplet simulation that carves valleys and
     drainage networks
  4. **Hydrology** — flow routing over the eroded terrain; high-flow
     cells become rivers, filled depressions become lakes

Generation is fully deterministic for a given seed and option set, and
runs on a dirty CPU scheduler so it never blocks the BEAM.

## Examples

    {:ok, terrain} = TerrainGenerator.generate(width: 512, height: 512, seed: 42)
    TerrainGenerator.Terrain.height_at(terrain, 100, 100)
    #=> 0.53

    # render straight to a PNG file without keeping the data:
    :ok = TerrainGenerator.render_png("map.png", seed: 42)

## Options

All functions take the same option list; omitted keys use the defaults.

  * `:width`, `:height` — map size in cells (default `512 × 512`), at most
    4,294,967,295 cells in all
  * `:seed` — generation seed (default `0`)
  * `:sea_level` — sea level in `[0, 1)` (default `0.08`)
  * `:octaves` — noise octaves (default `8`)
  * `:frequency` — number of large features across the map (default `2.5`)
  * `:lacunarity` — frequency multiplier between octaves (default `2.0`)
  * `:persistence` — amplitude multiplier between octaves (default `0.5`)
  * `:island` — island diameter as a fraction of the shorter map
    dimension, `true` for the default size, or `false` to let terrain
    reach the borders (default `1.0`)
  * `:coast_noise` — coastline perturbation strength; `0` gives a
    perfectly round island (default `1.0`)
  * `:erosion` — `true`/`false`, or a droplet count to override the
    default of one droplet per cell (default `true`)
  * `:rivers` — approximate fraction of land covered by rivers, `true`
    for the default density, or `false` to disable rivers and lakes
    (default `0.01`)
  * `:river_min_upstream` — absolute river threshold in cells of upstream
    catchment; overrides `:rivers` when set (default `false`)
  * `:archipelago` — number of islands to place instead of a single
    island, or `false` for one island (default `false`)
  * `:islands_size`, `:islands_variation`, `:islands_spread`,
    `:islands_spacing` — archipelago island size, per-island size
    variation, how far centres spread, and minimum centre spacing
    (defaults `0.35`, `0.35`, `0.85`, `0.22`)
  * `:breach_basins` — carve outlets from closed basins larger than this
    fraction of the map so they drain instead of becoming inland seas,
    or `false` (default `false`)

All options are validated; invalid values raise `ArgumentError` with a
descriptive message before any native code runs.

# `option`

```elixir
@type option() ::
  {:width, pos_integer()}
  | {:height, pos_integer()}
  | {:seed, non_neg_integer()}
  | {:sea_level, float()}
  | {:octaves, pos_integer()}
  | {:frequency, number()}
  | {:lacunarity, number()}
  | {:persistence, number()}
  | {:island, number() | boolean()}
  | {:coast_noise, number()}
  | {:archipelago, pos_integer() | false}
  | {:islands_size, number()}
  | {:islands_variation, number()}
  | {:islands_spread, number()}
  | {:islands_spacing, number()}
  | {:erosion, boolean() | pos_integer()}
  | {:breach_basins, number() | false}
  | {:rivers, number() | boolean()}
  | {:river_min_upstream, number() | false}
```

# `generate`

```elixir
@spec generate([option()]) :: {:ok, TerrainGenerator.Terrain.t()}
```

Generates a terrain and returns `{:ok, %TerrainGenerator.Terrain{}}`.

Raises `ArgumentError` on unknown options.

    {:ok, terrain} = TerrainGenerator.generate(seed: 7, width: 256, height: 256)
    terrain.width
    #=> 256

# `render_png`

```elixir
@spec render_png(Path.t(), [option()], :shaded | :greyscale) ::
  :ok | {:error, String.t()}
```

Generates a terrain and writes it to `path` as a PNG file.

`style` is `:shaded` (biome colours with hillshading, the default) or
`:greyscale` (plain heightmap).

Returns `:ok`, or `{:error, reason}` if the file cannot be written.

    :ok = TerrainGenerator.render_png("map.png", seed: 3)
    :ok = TerrainGenerator.render_png("height.png", [seed: 3], :greyscale)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
