import { type Aabb, type AvoidMasks, type AvoidZone, type Vec2 } from "./geometry.js"; import type { SceneDocumentLike, ScenePathLike } from "./sceneShapes.js"; import type { TerrainNormal } from "./terrain.js"; /** The editor path kind that marks a closed polyline as a foliage/scatter region. */ export declare const SCATTER_PATH_KIND = "scatter"; /** One species/prop in a scatter region's palette, with a relative spawn weight. */ export interface ScatterPaletteEntry { item: string; weight: number; } /** How a scatter region fills its polygon: density, spacing, variation, and masking rules. */ export interface ScatterRegionRules { /** Items per square meter across the region's footprint. */ density: number; /** Minimum spacing between placements in meters; 0 allows clumping. */ minSpacing: number; /** Seed string — same seed reproduces the same field. */ seed: string; minScale: number; maxScale: number; /** Yaw range in radians applied per placement. */ minYaw: number; maxYaw: number; /** Lift every instance by this many meters off the ground. */ verticalOffset: number; /** Orient instances to the terrain normal instead of straight up. */ alignToNormal: boolean; /** Reject placements on ground steeper than this slope (rise/run); 0 disables. */ maxSlope: number; minHeight: number; maxHeight: number; /** Thin placements out within this many meters of the region edge; 0 disables. */ edgeFalloff: number; /** Grid jitter 0..1 — 0 is a rigid lattice, 1 fully scattered. */ jitter: number; /** Weighted species palette; falls back to a single `grass` entry. */ palette: readonly ScatterPaletteEntry[]; /** Explicit no-scatter clearance discs this region always honors (manual exclusion). */ avoid: readonly AvoidZone[]; /** Honor document-wide clearance zones (tagged markers/paths) too; false = manual `avoid` only. */ autoAvoid: boolean; } /** Defaults a bare scatter region fills with: sparse grass, lightly spaced. */ export declare const SCATTER_DEFAULTS: ScatterRegionRules; /** Default clearance radius (m) for an auto-avoided gameplay marker with no explicit `meta.clearance`. */ export declare const DEFAULT_MARKER_CLEARANCE = 3.5; /** Marker/volume kinds that repel scatter and flatten terrain by default (spawns, objectives, vendors). */ export declare const DEFAULT_CLEARANCE_KINDS: readonly string[]; /** A resolvable scatter region: a closed polygon footprint plus its fill rules. */ export interface ScatterRegion { id: string; polygon: readonly Vec2[]; rules: ScatterRegionRules; } /** A single placed scatter instance — grounded world position plus per-instance variation. */ export interface ScatterInstance { id: string; regionId: string; item: string; x: number; y: number; z: number; rotationY: number; scale: number; normal?: TerrainNormal; } /** Ground sampler a scatter resolve reads height/normal from (the sculpt terrain or the game's ground). */ export interface ScatterTerrain { sampleHeight(x: number, z: number): number; sampleNormal(x: number, z: number): TerrainNormal; } /** Ray-casting point-in-polygon test on the XZ plane. */ export declare function pointInPolygon(point: Vec2, polygon: readonly Vec2[]): boolean; /** Shortest distance from a point to a polygon's boundary. */ export declare function distanceToPolygonEdge(point: Vec2, polygon: readonly Vec2[]): number; /** Axis-aligned bounds of a polygon, or null if it has no points. */ export declare function polygonBounds(polygon: readonly Vec2[]): Aabb | null; /** Shoelace area of a polygon (always non-negative), in square meters. */ export declare function polygonArea(polygon: readonly Vec2[]): number; /** * Deterministic placements for one scatter region: scatter its polygon footprint at `density` * items/m² (respecting `minSpacing`), clip to the polygon, thin near the edge, drop placements * outside the slope/height mask, and derive item/scale/yaw from the region id + seed — so the same * saved region always grows the same field. Grounds each instance on `terrain` when provided. */ export declare function resolveScatterRegion(region: ScatterRegion, terrain?: ScatterTerrain, avoid?: AvoidMasks): ScatterInstance[]; /** Parses a scatter region's palette from meta: a weighted `palette` array, else a single `item`. */ export declare function readScatterPalette(meta: Record | undefined): ScatterPaletteEntry[]; /** True when an editor path is a foliage/scatter region. */ export declare function isScatterPath(path: ScenePathLike): boolean; /** The path's scatter rules with defaults filled in; null for non-scatter paths. */ export declare function readScatterRules(path: ScenePathLike): ScatterRegionRules | null; /** * Parses a region's manual clearance discs from `meta.avoid` (array of {x,z,radius,feather?}). * @internal — used by `readScatterRules`; games author `avoid` through the region inspector. */ export declare function readAvoidZones(meta: Record | undefined): AvoidZone[]; /** Builds a resolvable {@link ScatterRegion} from a scatter path (XZ polygon + rules), or null. */ export declare function scatterRegionFromPath(path: ScenePathLike): ScatterRegion | null; /** Estimated placement count for a scatter path — density × polygon area, for a live UI readout. */ export declare function scatterRegionEstimate(path: ScenePathLike): { area: number; count: number; }; /** Controls which gameplay objects auto-repel scatter (and, for markers, flatten terrain). */ export interface ClearanceOptions { /** Marker/volume/path kinds that auto-clear even without a `meta.clearance` tag. Default {@link DEFAULT_CLEARANCE_KINDS}. */ kinds?: readonly string[]; /** Restrict auto-clearing to these object ids only (any kind); overrides `kinds` when set. */ ids?: readonly string[]; /** Clearance radius (m) for a kind-matched object with no explicit tag. Default {@link DEFAULT_MARKER_CLEARANCE}. */ defaultClearance?: number; /** Soft outer band (m) on every derived zone/corridor. Default 2. */ feather?: number; /** Include a corridor along non-scatter paths (roads/routes) in {@link clearanceMasksFrom}. Default true. */ includePaths?: boolean; } /** * Point-pad clearance **discs** from a document's markers/volumes — the terrain-flatten set (spawns, * plots, POIs get a level pad). A marker/volume contributes a disc when it carries `meta.clearance` * or its kind is in `kinds`. Paths are *not* included (they render draped, never flattened — see * {@link clearanceMasksFrom} for their foliage corridor). Pass `ids`/`kinds` to scope it. */ export declare function clearanceZonesFrom(doc: SceneDocumentLike, options?: ClearanceOptions): AvoidZone[]; /** * The full clearance region for scatter avoidance: point-pad {@link clearanceZonesFrom} **discs** * plus clean-edged **corridors** along non-scatter paths (each `halfWidth` = `path.width/2 + * clearance`). Corridors give a straight tree-line along a road instead of a scalloped disc chain — * this is what `resolveScatter` auto-avoids. Set `includePaths: false` to drop the path corridors. * @internal — `resolveScatter` builds these automatically; games rarely call it directly. */ export declare function clearanceMasksFrom(doc: SceneDocumentLike, options?: ClearanceOptions): AvoidMasks; /** Extra inputs for {@link resolveScatter}: manual discs plus the auto-clearance toggle/scoping. */ export interface ResolveScatterOptions extends ClearanceOptions { /** Additional clearance discs applied to every region regardless of per-region `autoAvoid`. */ avoid?: readonly AvoidZone[]; /** Master switch for document-derived auto-clearance; false = per-region `avoid` (and `options.avoid`) only. */ autoAvoid?: boolean; } /** * Every scatter region's placements across a document, grounded on `terrain` when provided. Regions * honor clearance masks: their own manual `avoid` discs, plus (when the region's `autoAvoid` is on and * `options.autoAvoid !== false`) the document-wide discs + path corridors from {@link clearanceMasksFrom} * — so foliage auto-clears spawns, plots, and paths without hand-carving the polygon. */ export declare function resolveScatter(doc: SceneDocumentLike, terrain?: ScatterTerrain, options?: ResolveScatterOptions): ScatterInstance[]; /** A spatial bucket of scatter instances — one draw unit the renderer can frustum-cull as a whole. */ export interface ScatterChunk { /** `gx:gz` grid key of the chunk. */ key: string; /** Chunk minimum XZ corner in world space. */ min: Vec2; /** Chunk edge length in meters. */ size: number; instances: ScatterInstance[]; } /** * Buckets scatter instances into a uniform XZ grid of `chunkSize`-meter cells, so a dense region * renders as many small draw units instead of one huge mesh — each chunk carries its own bounds and * is frustum-culled independently, and offscreen chunks cost nothing. Empty chunks are omitted; * order is deterministic (row-major by grid coordinate). * @internal — the chunking behind the `InstancedScatter` renderer; not called directly by games. */ export declare function chunkScatterInstances(instances: readonly ScatterInstance[], chunkSize: number): ScatterChunk[];