/**
 * Spatial Index Plugin for ECSpresso
 *
 * Provides a uniform-grid spatial hash for broadphase collision detection
 * and proximity queries. Replaces O(n²) brute-force with O(n·d) where
 * d = local density.
 *
 * Standalone usage: queryRect / queryRadius for proximity queries.
 * Automatic acceleration: collision and physics2D plugins detect the
 * spatialIndex resource at runtime and use it for broadphase when present.
 */
import type { TransformComponentTypes } from './transform';
import type { CollisionComponentTypes } from '../physics/collision';
import { type SpatialIndex } from '../../utils/spatial-hash';
export interface SpatialIndexResourceTypes {
    spatialIndex: SpatialIndex;
}
type SpatialIndexComponentTypes = TransformComponentTypes & Pick<CollisionComponentTypes<string>, 'aabbCollider' | 'circleCollider'>;
export type SpatialIndexPhase = 'fixedUpdate' | 'postUpdate';
export interface SpatialIndexPluginOptions<G extends string = 'spatialIndex'> {
    /** Cell size for the spatial hash grid (default: 64) */
    cellSize?: number;
    /** System group name (default: 'spatialIndex') */
    systemGroup?: G;
    /** Priority for rebuild systems (default: 2000, before collision) */
    priority?: number;
    /**
     * Phases to register rebuild systems in (default: ['fixedUpdate', 'postUpdate']).
     *
     * When both phases are registered, the `postUpdate` rebuild is
     * automatically skipped on cycles where:
     *   1. The `fixedUpdate` rebuild already ran this cycle, AND
     *   2. No entity hierarchy exists.
     *
     * In flat-hierarchy scenes `transform-propagation` copies
     * `localTransform → worldTransform` unchanged, so the postUpdate
     * grid would duplicate the fixedUpdate one. The skip is automatic
     * and re-engages once any parent relationship is set, or whenever
     * `fixedUpdate` doesn't run that cycle (sub-fixed-DT frames).
     *
     * Edge case: if you write to `worldTransform` directly between
     * phases without using `transform-propagation` or entity hierarchy,
     * the auto-skip will leave your writes unindexed. Set
     * `phases: ['postUpdate']` explicitly to bypass the auto-skip.
     */
    phases?: ReadonlyArray<SpatialIndexPhase>;
}
/**
 * Create a spatial index plugin for ECSpresso.
 *
 * Provides a uniform-grid spatial hash that accelerates collision detection.
 * When installed alongside the collision or physics2D plugins, they
 * automatically use the spatial index for broadphase instead of O(n²)
 * brute-force.
 *
 * Also provides proximity query methods for game logic (e.g. "find all
 * enemies within 200 units").
 *
 * @example
 * ```typescript
 * const ecs = ECSpresso.create()
 *   .withPlugin(createTransformPlugin())
 *   .withPlugin(createCollisionPlugin({ layers }))
 *   .withPlugin(createSpatialIndexPlugin({ cellSize: 128 }))
 *   .build();
 *
 * // Proximity query in a system:
 * const si = ecs.getResource('spatialIndex');
 * const nearby = si.queryRadius(playerX, playerY, 200);
 * ```
 */
export declare function createSpatialIndexPlugin<G extends string = 'spatialIndex'>(options?: SpatialIndexPluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, SpatialIndexComponentTypes>, SpatialIndexResourceTypes>, import("ecspresso").EmptyConfig, "spatial-index-rebuild-fixedUpdate" | "spatial-index-rebuild-postUpdate", G, never, never>;
export {};
