UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

111 lines (110 loc) 4.85 kB
/** * Spatial Hash Grid * * Uniform-grid spatial hash for broadphase collision detection and * proximity queries. Pure data structure, no ECS dependencies. */ export interface SpatialEntry { entityId: number; x: number; y: number; halfW: number; halfH: number; /** Generation stamp used by query functions to dedup multi-cell hits without a Set. Internal. */ _lastSeenGen: number; /** Rebuild generation when this entry was last inserted. Internal. */ _aliveGen: number; } /** * A cell bucket — entries plus the alive-gen at which the bucket was last * filled. Buckets are reset lazily on the next insert in a new generation * (see `insertEntity`); queries skip buckets whose `_gen` is stale. * * Internal — exposed only through `SpatialHashGrid.cells`. */ interface CellBucket extends Array<SpatialEntry> { _gen: number; } export interface SpatialHashGrid { cellSize: number; invCellSize: number; cells: Map<number, CellBucket>; /** * Dense, indexed by entityId. Holes are `undefined`. Entries from previous * rebuilds remain in place for in-place reuse (zero allocation in steady * state); liveness is determined by `entry._aliveGen === grid._aliveGen`. * Internal — read live entries via `getEntry` / `liveEntryCount` helpers. * * High-water-mark grows with max entityId ever inserted; despawned ids * leave their slot occupied by a stale entry. Acceptable when the entity * manager recycles ids or peak count is bounded. */ entries: (SpatialEntry | undefined)[]; /** Monotonic counter bumped by each `clearGrid` call. Internal. */ _aliveGen: number; /** Monotonic counter bumped on each query; entries record their last-seen gen for O(1) dedup. Internal. */ _queryGen: number; } /** * Hash a cell coordinate pair to a single integer key. * Uses large-prime XOR to distribute values. */ export declare function hashCell(cx: number, cy: number): number; /** * Create a new empty spatial hash grid. */ export declare function createGrid(cellSize: number): SpatialHashGrid; /** * Prepare the grid for a rebuild. * * O(1): bumps the alive-generation counter so entries inserted prior to this * call are implicitly stale. `getLiveEntry` / `liveEntryCount` filter * entries by the current gen; queries skip buckets whose own `_gen` lags * behind the alive gen; `insertEntity` resets a bucket's `length` lazily * the first time it is touched in a new generation. * * Existing `SpatialEntry` objects and `CellBucket` arrays remain in place * for reuse, so steady-state rebuilds allocate zero entries and zero * buckets, regardless of how many cells have ever been touched. */ export declare function clearGrid(grid: SpatialHashGrid): void; /** * Insert an entity into all overlapping cells of the grid. */ export declare function insertEntity(grid: SpatialHashGrid, entityId: number, x: number, y: number, halfW: number, halfH: number): void; /** * Collect entity IDs from all cells overlapping the given rectangle. * * Appends to `result` (caller clears/truncates first if reusing). Multi-cell * entries are deduplicated via a per-grid generation stamp on each * `SpatialEntry`. * * When `minId` is provided, only entries with `entityId > minId` are added — * used for symmetric broadphase pair generation. */ export declare function gridQueryRect(grid: SpatialHashGrid, minX: number, minY: number, maxX: number, maxY: number, result: number[], minId?: number): void; /** * Collect entity IDs within a circle. AABB-to-point distance filter against * the cells overlapping the circle's bounding rect. Appends to `result`. */ export declare function gridQueryRadius(grid: SpatialHashGrid, cx: number, cy: number, radius: number, result: number[]): void; /** * Get the current-generation entry for an entityId, or `undefined` if the * entity isn't in the index for this rebuild. Stale entries from previous * rebuilds remain in `entries` for in-place reuse but are filtered here. */ export declare function getLiveEntry(grid: SpatialHashGrid, entityId: number): SpatialEntry | undefined; /** * Count entries inserted in the current rebuild generation. Linear scan — * intended for tests and diagnostics, not hot paths. */ export declare function liveEntryCount(grid: SpatialHashGrid): number; export interface SpatialIndex { readonly grid: SpatialHashGrid; queryRect(minX: number, minY: number, maxX: number, maxY: number): number[]; queryRectInto(minX: number, minY: number, maxX: number, maxY: number, result: number[], minId?: number): void; queryRadius(cx: number, cy: number, radius: number): number[]; queryRadiusInto(cx: number, cy: number, radius: number, result: number[]): void; getEntry(entityId: number): SpatialEntry | undefined; } export {};