/** * Path-driven terrain modifiers: one authored centerline reshapes a heightfield across a corridor — * flatten a roadbed, grade a constant-slope ramp, carve a river/trench, and hold retaining walls where * the corridor cuts or fills into the surrounding ground. Genre-agnostic: roads, rails, rivers, * trenches, runways, and accessibility ramps are all the same profile with different numbers, so no game * hand-rolls its own flatten-mask generator. Pure XZ math over a polyline — deterministic, bounded * (O(points) per sample), and serializable; it composes onto any height sampler and pairs with * `resolveTerrainField` so the same authored path drives both the rendered mesh and player collision. * @capability path-terrain flatten / grade / carve a heightfield from an authored path with retaining profiles * @consumer packages/core/src/world/terrain.ts (resolveTerrainField), Games/scrap-signal roads */ import type { Vec2 } from "./geometry.js"; /** * Where a path corridor drives its centerline height: * - `sample` — follow the base terrain under the centerline, so the corridor drapes level across its * width while still tracking the hills the path crosses (the natural policy for a road or trail). * - `fixed` — hold one constant height for the whole corridor (a level causeway, runway, or dam crest). * - `grade` — interpolate linearly from `start` to `end` height along the path's arc length, a * constant-slope ramp between two anchors (a switchback, a graded rail bed, an accessibility ramp). */ export type PathHeightPolicy = { readonly kind: "sample"; } | { readonly kind: "fixed"; readonly height: number; } | { readonly kind: "grade"; readonly start: number; readonly end: number; }; /** * Retaining behavior for a path corridor's shoulders. A wall rises only where the corridor already cuts or * fills into the surrounding ground by at least `threshold` — gentle stretches stay open — so a road * through a canyon gets held banks while the same profile on flat ground does not. The wall fades in * smoothly across `taper` above the threshold so it never steps on or off between adjacent samples. */ export interface PathRetaining { /** Crest height raised above the corridor edge on both shoulders (canyon walls, road berms, levee banks). */ readonly wallHeight: number; /** Minimum cut/fill (world units) between corridor and surrounding ground before a wall rises. Default 0 (wall the whole path). */ readonly threshold?: number; /** Cut/fill band above `threshold` over which the wall fades from none to full height. Default equal to `threshold`. */ readonly taper?: number; } /** * A serializable path-driven terrain modifier. The `points` centerline reshapes the heightfield across a * corridor of `width`, feathering back to the surrounding ground across `shoulder`. `height` sets the * centerline target (sample / fixed / grade); `depth` carves a channel below it (deepest at the * centerline, easing to zero at the core edge — rivers and trenches); `retaining` raises walls where the * cut/fill exceeds a threshold; `maxCut`/`maxFill` cap how far the ground may move from its base height. * All fields are plain data (numbers, string enums, point arrays) so a profile round-trips through the * scene document and evaluates identically at author time and runtime. */ export interface TerrainPathProfile { /** Centerline vertices in world XZ; needs at least two points. Repeat the first point as the last for a closed loop. */ readonly points: readonly Vec2[]; /** Full corridor width; the reshaped core extends `width / 2` either side of the centerline. */ readonly width: number; /** Blend-ring width outside the core where the corridor height smoothsteps back to the surrounding ground. Default `width * 0.5`. */ readonly shoulder?: number; /** Centerline target-height policy. Default `{ kind: "sample" }` (drape level across the base terrain). */ readonly height?: PathHeightPolicy; /** Carve depth below the target height, deepest at the centerline and easing to zero at the core edge (0 = flat corridor). Default 0. */ readonly depth?: number; /** Never cut the ground more than this far below its base height. Omit for unlimited cut. */ readonly maxCut?: number; /** Never fill the ground more than this far above its base height. Omit for unlimited fill. */ readonly maxFill?: number; /** Raise retaining walls along the shoulders where the corridor cuts/fills into the surrounding ground. */ readonly retaining?: PathRetaining; } /** * Wraps a height sampler so each authored `TerrainPathProfile` reshapes it. Profiles apply in list order * (later profiles compose over earlier ones, so an intersection's last profile wins its core); each reads * the running height as its surrounding ground and the original `base` for `sample` centerline heights. * The returned sampler is pure and deterministic. Degenerate profiles (fewer than two points, non-positive * width or length) are skipped; with no usable profile the original `base` is returned unchanged. * @capability path-terrain compose flatten/grade/carve/retaining path profiles onto a height sampler */ export declare function withPathProfiles(base: (x: number, z: number) => number, profiles: readonly TerrainPathProfile[]): (x: number, z: number) => number;