/** One agent's circular footprint and top speed; positions and velocities live in the crowd. */ export interface RvoAgentSpec { x: number; z: number; radius: number; /** World units per second; the avoidance solve never returns a longer velocity. */ maxSpeed: number; vx?: number; vz?: number; } /** Serializable avoidance tuning, retunable in play through {@link RvoCrowd.retune}. */ export interface RvoTunables { /** Seconds of look-ahead over which the agent avoids an upcoming collision. */ timeHorizon: number; /** Radial distance, in world units, at which other agents enter the solve. */ neighborDist: number; /** Hard cap on agents one agent considers per tick — the bound that keeps a dense crowd linear. */ maxNeighbors: number; /** * Radians every preferred direction is turned the same rotational way. A perfectly mirrored pair * (both at rest, walking straight at each other) has no side to pick and would brake to a halt; * this bias makes the pair pass on each other's right. Set 0 for exact mirror symmetry. */ sideBias: number; /** * Fraction of any remaining overlap pushed apart after the velocity solve, `0`..`1`. The velocity * obstacle solve is a steering rule, not a hard guarantee; this positional pass keeps a dense * crowd from settling into overlaps. Set 0 for pure velocity-only avoidance. */ overlapRelief: number; } /** One agent's live kinematics, as the crowd exposes them. */ export interface RvoAgentView { x: number; z: number; vx: number; vz: number; radius: number; maxSpeed: number; } /** Serializable crowd state: tuning plus every agent's position, velocity and footprint. */ export interface RvoSnapshot { tunables: RvoTunables; agents: { id: string; x: number; z: number; vx: number; vz: number; radius: number; maxSpeed: number; }[]; } /** * Many circular agents steering around each other. `setPreferredVelocity` states where each agent * wants to go; `step` turns that into reciprocal collision-free velocities and integrates them. * Deterministic: agents are visited in slot order, neighbours in grid and slot order, and head-on * ties break on slot index rather than chance. */ export interface RvoCrowd { /** Add an agent; returns false when the id is already present. */ add(id: string, spec: RvoAgentSpec): boolean; remove(id: string): boolean; has(id: string): boolean; count(): number; /** Agent ids in slot order. */ ids(): readonly string[]; agent(id: string): RvoAgentView | null; /** Copy one agent's kinematics into a caller-owned record, for hot loops that must not allocate. */ readInto(id: string, out: { x: number; z: number; vx: number; vz: number; }): boolean; /** Where the agent wants to go this tick; `step` consumes and clears it. */ setPreferredVelocity(id: string, vx: number, vz: number): boolean; /** Overwrite kinematics, e.g. after a teleport or an external controller. */ setKinematics(id: string, position: { x: number; z: number; vx?: number; vz?: number; }): boolean; /** Solve avoidance for every agent and integrate positions by `dt` seconds. */ step(dt: number): void; retune(patch: Partial): void; snapshot(): RvoSnapshot; restore(next: RvoSnapshot): void; /** Drop the devtools probe registered by `devtoolsName`. Safe to call twice. */ dispose(): void; } /** Options for {@link createRvoCrowd}. */ export interface RvoCrowdOptions extends Partial { /** Register a devtools probe under this name so the perf overlay shows crowd counters. */ devtoolsName?: string; } /** * Reciprocal velocity obstacle avoidance over circular agents. Each nearby pair contributes one or * two half-plane constraints on the agent's velocity, and a small linear program picks the velocity * closest to the preferred one that satisfies them all. The neighbour search is a uniform grid * capped at `maxNeighbors`, so a tick costs the same for 50 agents and 5,000. * * @capability rvo-avoidance reciprocal collision avoidance between many steering agents */ export declare function createRvoCrowd(options?: RvoCrowdOptions): RvoCrowd;