import type { RenderBounds } from "./bounds.js"; import { aabbIntersects } from "./bounds.js"; import type { Frustum } from "./frustum.js"; export interface SpatialIndexOptions { /** Grid cell size in world units. Default 16. Objects are hashed into every cell their AABB overlaps. */ readonly cellSize?: number; /** * An object whose AABB spans more cells than this on any axis is parked in an "oversized" * bucket that every query visits, rather than smearing across the grid. Default 6. */ readonly maxCellSpan?: number; } /** Integer cell bounds an object covers, inclusive on both ends. */ export interface SpatialCellRange { x0: number; y0: number; z0: number; x1: number; y1: number; z1: number; } /** * Plain JSON state of a {@link SpatialIndex}. Cells and their ids keep insertion order, so a restored * index returns query results in the same order. */ export interface SpatialIndexState { /** Per object: its cell range (`null` when oversized) and whether it was inserted as dynamic. */ entries: [id: string, range: SpatialCellRange | null, dynamic: boolean][]; cells: [x: number, y: number, z: number, ids: string[]][]; oversized: string[]; } /** * A uniform 3D spatial hash the renderer and streaming system query for potentially-visible * objects instead of scanning the whole scene. Objects are keyed by their world AABB into * every overlapping cell; a moving object only rewrites the cells that actually changed. * Static objects are inserted once and never touched again. Oversized objects (huge terrain * chunks, world bounds) are held separately so they are always considered. */ export interface SpatialIndex { insert(id: string, bounds: RenderBounds, dynamic?: boolean): void; update(id: string, bounds: RenderBounds): void; remove(id: string): void; has(id: string): boolean; size(): number; /** Collect ids whose cells the frustum's world-AABB touches (broad phase). Reuses `out`. */ queryFrustum(frustum: Frustum, out: string[]): string[]; queryBox(minX: number, minY: number, minZ: number, maxX: number, maxY: number, maxZ: number, out: string[]): string[]; querySphere(cx: number, cy: number, cz: number, radius: number, out: string[]): string[]; cells(): { key: string; count: number; minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number; }[]; cellSize(): number; clear(): void; snapshot(): SpatialIndexState; restore(next: SpatialIndexState): void; } export declare function createSpatialIndex(options?: SpatialIndexOptions): SpatialIndex; /** Precise AABB-vs-AABB test re-exported for callers refining a broad-phase result. */ export { aabbIntersects };