# @ferret/physics-terrain: API

layered soil height field that can be dug, filled, slumps and floods; sinkage; tunnels; terrain as kernel meshes.

22 exports. Generated from the source (`packages/physics-terrain/src`), so it matches the code.

## Classes

### SoilLibrary

*class* · `packages/physics-terrain/src/soil.ts:32`

```ts
class SoilLibrary
```

- `constructor(initial: Record<string, Soil> = EXAMPLE_SOILS)`
- `add(name: string, s: Soil)`
- `get(name: string): Soil`
- `has(name: string)`
- `names()`

### Terrain

*class* · `packages/physics-terrain/src/terrain.ts:69`

```ts
class Terrain
```

- `readonly nx: number`
- `readonly nz: number`
- `readonly cell: number`
- `readonly origin: [number, number]`
- `readonly base: number`
- `readonly soils: SoilLibrary`
- `readonly tile: number`
- `readonly reach: number`
- `waterTable: ((x: number, z: number) => number) | null`
- `readonly tunnels: Tunnel[]` — bores through the ground: volumes the heightfield cannot hold; `solidAt` knows them
- `onSlump: ((r: SlumpResult & { at: [number, number] }) => void) | null` — called with what a slump moved (the host turns it into `terrain:slump`)
- `constructor(o: TerrainOptions)`
- `setColumn(i: number, j: number, strata: Array<{ soil: string; thickness: number }>)` — Replace the strata of one column (bottom to top); the surface height follows.
- `static fromLandscape(grid: LandscapeGrid, o: { soils: SoilLibrary; map: Record<string, string>; fallback: string; bedrock?: string; top?: number; depth?: number; tile?: number; waterTable?: TerrainOptions['waterTable'] }): Terrain` — A terrain from the grid `ue/landscape.py` writes (Unreal's cm, z up): the heights are the surface; every column is `top` m of the soil its dominant landscape layer maps to (`layers` names the layers, `layerAt` has an index per sample, -1 for none, which takes `fallback`) over bedrock down to `depth` m below the lowest point. Axes as in the exported mesh: x = x / 100, z = y / 100, height = z / 100.
- `inside(i: number, j: number)`
- `x(i: number)`
- `z(j: number)`
- `col(x: number, z: number): [number, number]`
- `height(i: number, j: number): number`
- `heightAt(x: number, z: number): number` — Surface height at a point, bilinear between columns (clamped at the edge).
- `layers(i: number, j: number): readonly Stratum[]`
- `topSoil(i: number, j: number): { name: string; soil: Soil }` — The soil at the surface of a column.
- `soilAt(x: number, z: number)`
- `tableAt(x: number, z: number): number`
- `moistureAt(i: number, j: number): number` — 0..1: set by `wet`, and 1 for a column whose surface is under the water table.
- `volume(): number` — Ground in place plus heap, m³: what a dig and a dump must keep.
- `heapVolume(): number` — Volume of heap (loose) in the terrain, m³.
- `addLayer(i: number, j: number, soil: string, thickness: number, loose = false)` — Add a layer on top of a column (the sparse edit used by fill and by imports).
- `dig(o: { footprint: Footprint; depth: number; tool: Tool; dt: number }): DigResult` — Dig. `depth` is taken off the surface of every column of the footprint, top down, as far as the tool's energy (power x dt, at the soil's hardness per m³) and its reach allow. What comes out is returned as heap.
- `dump(at: [number, number], spoil: readonly Spoil[]): SlumpResult` — Pour heap at a point: it lands on the column and runs down to the soil's slope.
- `fill(o: { footprint: Footprint; soil: string; height: number; loose?: boolean }): { added: number; slump: SlumpResult }` — Fill a footprint with a soil up to a height (in place, not heap). Returns the volume added.
- `level(o: { footprint: Footprint; height: number; soil?: string }): { cut: number; filled: number; spoil: Spoil[]; slump: SlumpResult }` — Flatten a footprint to a height: cut what stands above it and fill what is below. Returns the net volume moved (positive = cut).
- `compact(footprint: Footprint): number` — Pack heap in a footprint into ground: each loose layer shrinks by its bulking. Returns the volume that vanished.
- `wet(footprint: Footprint, moisture: number)` — Wet a footprint (0 = dry, 1 = saturated).
- `excavate(o: { centre: [number, number]; radius: number; depth: number; throw?: number; reachOuter?: number }): { removed: number; spoil: Spoil[]; thrown: number; fallback: number; slump: SlumpResult; }` — A crater: a paraboloid of radius R and depth d (depth(r) = d·(1 − (r/R)²)) is taken out, with no tool and no energy account. The heap it makes (volume x bulking) comes down in two places: `throw` of it as ejecta on the ring from R out to `reachOuter`·R, falling off as r⁻³, and the rest back in the crater, in proportion to what was taken. Then the ground relaxes.
- `solidAt(x: number, y: number, z: number): boolean` — Is there ground at the point: below the surface and outside every bore. A body in a tunnel stands in the open.
- `tunnel(o: { from: [number, number, number]; to: [number, number, number]; radius: number; lined?: boolean; limit?: number }): TunnelResult` — Bore a tunnel. The roof is checked by Broms and Bennermark's stability number N = gamma (H + R) / c for the soil at the crown, with H the cover above the crown and R the radius, and the soil's cohesion c (wet ground holds more): an unlined bore stands while N is below `limit` (6, the flow limit of an open face in clay), a lined one always; sand with no cohesion cannot stand unlined. When it does not stand the roof caves in: the bore fills with the soil above it, which swells by the soil's bulking, and the surface settles over it by the volume that came down.
- `tileMesh(ti: number, tj: number): { positions: number[]; indices: number[] }` — The surface of tile (ti, tj) as a triangle mesh in world coordinates: one vertex per column, the edge columns shared with the next tile so the seams close.
- `tunnelMesh(t: Tunnel, sides = 16, step = 1): { positions: number[]; indices: number[] }` — The inside of a bore as a tube mesh in world coordinates (rings every `step` m along the axis, `sides` round).
- `takeDirtyTiles(): string[]` — Columns changed since the last call, as tile keys "i,j": what the navigation and the mesh have to rebuild.
- `slump(maxPasses = SLUMP_PASSES): SlumpResult` — Relax the columns that changed (and what they disturb) until every step stands, moving soil from the higher column to the lower.

## Functions

### criticalHeight

*function* · `packages/physics-terrain/src/soil.ts:60`

```ts
function criticalHeight(s: Soil, moisture = 0): number
```

The height of a vertical cut that stands, Hc = (4c/γ)·tan(45° + φ/2); 0 for a soil without cohesion.

### culmannHeight

*function* · `packages/physics-terrain/src/soil.ts:69`

```ts
function culmannHeight(s: Soil, betaDeg: number, moisture = 0): number
```

Culmann: the height of a face of slope angle β (degrees) that stands, Hc = (4c/γ)·sinβ·cosφ / (1 − cos(β − φ)). It is Infinity at
β ≤ φ (a slope of repose never fails), meets `criticalHeight` at 90°, and is 0 without cohesion above the repose angle.

### effective

*function* · `packages/physics-terrain/src/soil.ts:51`

```ts
function effective(s: Soil, moisture: number): { cohesion: number; density: number }
```

Cohesion and density at a moisture between 0 (dry) and 1 (saturated).

### sinkageDepth

*function* · `packages/physics-terrain/src/soil.ts:78`

```ts
function sinkageDepth(s: Soil, pressure: number): number
```

Sinkage z (m) under a pressure p (Pa): none up to the bearing, then z = ((p − bearing)/k)^(1/n).

### terrain

*function* · `packages/physics-terrain/src/index.ts:37`

```ts
function terrain(t: Terrain): TerrainCapability
```

## Interfaces

### DigResult

*interface* · `packages/physics-terrain/src/terrain.ts:47`

```ts
interface DigResult
```

- `removed: number` — volume of ground taken out, m³
- `spoil: Spoil[]` — what was taken, as heap: loose volume per soil
- `energy: number` — J spent
- `blocked: boolean` — the tool met a soil harder than it can break
- `flooded: number` — volume of water the pit needs to reach the water table, m³ (0 when it stayed above)
- `slump: SlumpResult`

### Footprint

*interface* · `packages/physics-terrain/src/terrain.ts:30`

```ts
interface Footprint
```

- `box?: [number, number, number, number]` — world rectangle: x0, z0, x1, z1
- `centre?: [number, number]` — or a disc
- `radius?: number`

### LandscapeGrid

*interface* · `packages/physics-terrain/src/terrain.ts:28`

```ts
interface LandscapeGrid
```

The height grid of an Unreal landscape (cm) with, per sample, the index of its dominant layer (the physical material of the layer the trace hit).

- `origin: number[]`
- `step: number`
- `nx: number`
- `ny: number`
- `heights: number[]`
- `layers?: string[]`
- `layerAt?: number[]`

### SlumpResult

*interface* · `packages/physics-terrain/src/terrain.ts:61`

```ts
interface SlumpResult
```

- `moved: number`
- `passes: number`
- `columns: number`
- `converged: boolean`

### Soil

*interface* · `packages/physics-terrain/src/soil.ts:2`

```ts
interface Soil
```

A soil or rock as data: the same record for a pile of sand, a clay bank, a dig and a footstep.

- `density: number` — kg/m³, dry in place
- `friction: number` — internal friction angle, degrees: the angle of repose of the loose material
- `cohesion: number` — cohesion, Pa (0 for dry sand and gravel)
- `hardness: number` — specific dig energy, J/m³: the energy it takes to remove a cubic metre
- `bulking: number` — volume of loose spoil per volume in place (1.3 = a 1 m³ hole of clay makes 1.3 m³ of heap)
- `bearing: number` — pressure, Pa, the ground carries without sinking
- `sinkage?: { k: number; n: number }` — below the bearing: pressure beyond it grows as k·zⁿ with the sinkage z (Bekker); stiff when missing
- `damping?: number` — damping of a sinking body, Pa·s/m
- `wet?: { cohesion?: number; density?: number }` — values when saturated; the moisture of a column (0..1) blends dry and wet

### Spoil

*interface* · `packages/physics-terrain/src/terrain.ts:45`

```ts
interface Spoil
```

- `soil: string`
- `inPlace: number`
- `loose: number`

### Stratum

*interface* · `packages/physics-terrain/src/terrain.ts:4`

```ts
interface Stratum
```

One layer of a column, bottom to top. `loose` thickness is heap (spoil, a dump), not ground in place.

- `soil: string`
- `thickness: number`
- `loose?: boolean`

### TerrainCapability

*interface* · `packages/physics-terrain/src/index.ts:23`

```ts
interface TerrainCapability extends Capability
```

- `readonly terrain: Terrain`
- `depthOf(b: Body): number` — Depth the body is below the surface of the ground, m (0 when above it).
- `collide(mode: TerrainCollision): void` — Put the ground into the kernel as static meshes: `tunnels` builds the bores (a body in a tunnel stands on its floor; the surface still goes by the sinkage law), `all` also builds the surface in tiles (a body then rests on the surface and the sinkage law has nothing to act on). Tiles dug since the last call and new bores are rebuilt; call it again after a dig, or it follows `terrain:changed` by itself.
- `readonly bodies: { tiles: Map<string, Body>; tunnels: Map<number, Body> }` — Bodies the capability made: tile meshes by "i,j" and bore meshes by id.

### TerrainOptions

*interface* · `packages/physics-terrain/src/terrain.ts:6`

```ts
interface TerrainOptions
```

- `nx: number` — columns along x and z
- `nz: number`
- `cell: number` — column spacing, m
- `origin?: [number, number]` — world x, z of column (0, 0)
- `base?: number` — elevation of the bottom of every column, m
- `soils: SoilLibrary`
- `strata: Array<{ soil: string; thickness: number }>` — the strata every column starts with, bottom to top
- `tile?: number` — columns per tile edge: what the NavMesh and the mesh rebuild by
- `waterTable?: number | ((x: number, z: number) => number)` — water table height, m (a number, or a function of x, z); none when missing
- `reach?: number` — columns looked at to find the foot of a face when judging its stability

### Tool

*interface* · `packages/physics-terrain/src/terrain.ts:38`

```ts
interface Tool
```

- `power: number` — W
- `reach: number` — the hardest soil (J/m³) the tool can break at all

### Tunnel

*interface* · `packages/physics-terrain/src/terrain.ts:66`

```ts
interface Tunnel
```

A tunnel: a round bore along a line from `a` to `b` (world coordinates, y up), `radius` m, unlined unless `lined`.

- `id: number`
- `a: [number, number, number]`
- `b: [number, number, number]`
- `radius: number`
- `lined: boolean`

### TunnelResult

*interface* · `packages/physics-terrain/src/terrain.ts:67`

```ts
interface TunnelResult
```

- `id: number`
- `stable: boolean`
- `stability: number`
- `roof: number`
- `subsided: number`
- `limit: number`

## Types

### TerrainCollision

*type* · `packages/physics-terrain/src/index.ts:21`

```ts
type TerrainCollision = 'none' | 'tunnels' | 'all'
```

What the capability puts into the rigid-body kernel: nothing (the height field is read by the sinkage law only), the inside of the bores, or the bores and the whole surface as static meshes.

## Constants

### EXAMPLE_SOILS

*const* · `packages/physics-terrain/src/soil.ts:23`

```ts
const EXAMPLE_SOILS: Record<string, Soil>
```

### TERRAIN_DEFAULTS

*const* · `packages/physics-terrain/src/index.ts:18`

```ts
const TERRAIN_DEFAULTS: { sinkage: boolean; footprint: number | undefined; }
```

the ground. A `Terrain` (layered height field) is given to the capability; it keeps the terrain relaxed, tells the
world when ground slumps or changes, and makes a body sink where its weight is more than the soil bears.

values (each at world / level / zone / entity scope):
  sinkage   true: bodies sink into soft ground and are held by its bearing and stiffness   (true)
  footprint contact area of a body, m²; the cross-section of a sphere when missing       (undefined)

events: `terrain:slump` { volume, passes, columns, at }, `terrain:changed` { tiles } after a command, `terrain:flooded` { volume }.

