/**
 * Maximum contact points per manifold. Four is the universal industry choice
 * (Bullet's btPersistentManifold, Box2D, Jolt) — enough to express stable
 * face-on-face contact, small enough to fit inertia-driven solver passes.
 * @type {number}
 */
export const MAX_CONTACTS_PER_MANIFOLD: number;
/**
 * Per-contact field stride in the data Float64Array.
 *
 * Layout per contact (all world-space):
 *   0..2  : world_a    (contact point on body A's surface)
 *   3..5  : world_b    (contact point on body B's surface)
 *   6..8  : normal_w   (from B toward A)
 *   9     : depth      (positive = penetration, negative = speculative gap)
 *   10    : j_n        (accumulated normal impulse, warm-start)
 *   11    : j_t1       (accumulated tangent impulse, axis 1)
 *   12    : j_t2       (accumulated tangent impulse, axis 2)
 *   13    : feature_id (uint32 packed as f64 — stable cross-frame ID of the
 *                       geometric feature pair that produced this contact;
 *                       0 means "no info, fall back to position matching")
 *   14    : friction   (already-combined coefficient for THIS contact's
 *                       specific source-collider pair — see combine_material)
 *   15    : restitution(already-combined coefficient for this contact)
 *
 * Solver uses `(world_a + world_b) * 0.5` as the application point; storing
 * both surface points enables the per-frame warm-start matcher in the
 * narrowphase to compare contact-point positions when feature_id is 0.
 *
 * Friction / restitution are stored **per contact** (not per body pair) so a
 * compound body whose colliders have different materials gets the right
 * coefficient at each contact: the narrowphase knows the exact source collider
 * on each side of every contact and combines there.
 *
 * @type {number}
 */
export const CONTACT_STRIDE: number;
/**
 * Per-contact feature_id offset within {@link CONTACT_STRIDE}.
 * Exposed for the narrowphase matcher; solver code does not read this.
 * @type {number}
 */
export const CONTACT_FEATURE_ID_OFFSET: number;
/**
 * Per-slot Float64 stride: room for {@link MAX_CONTACTS_PER_MANIFOLD} contacts.
 * @type {number}
 */
export const SLOT_DATA_STRIDE: number;
/**
 * Sentinel for "no slot".
 * @type {number}
 */
export const MANIFOLD_SLOT_ABSENT: number;
/**
 * Persistent contact-manifold cache keyed by canonical body-id pairs.
 *
 * Owns:
 *  - the SoA `Float64Array` of contact points (per-slot stride 52),
 *  - the small meta `Uint32Array` (bodyA, bodyB, packed flags + count),
 *  - a {@link PairUint32Map} from `(idA, idB)` to slot id,
 *  - a dense `__live_slots` array of currently-allocated slot ids,
 *  - a min-heap-free-stack of released slot ids so reuse is deterministic.
 *
 * Lifecycle per simulation step:
 *  1. Broadphase calls {@link acquire} for every encountered pair, which marks
 *     each touched slot as "fresh this frame".
 *  2. Narrowphase fills in contact points via {@link set_contact} /
 *     {@link clear_contacts}.
 *  3. End-of-step calls {@link advance_frame} to roll touched → prev_touched
 *     and evict slots that have been untouched for {@link DEFAULT_GRACE}
 *     consecutive frames.
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class ManifoldStore {
    /**
     * @param {number} [initial_capacity] slots
     */
    constructor(initial_capacity?: number);
    __capacity: number;
    __high_water: number;
    __data: Float64Array;
    __meta: Uint32Array;
    __pair_index: PairUint32Map;
    __live_slots: Uint32Array;
    __live_pos: Int32Array;
    __live_count: number;
    __free_heap: Uint32Array;
    __free_count: number;
    /**
     * @returns {number} number of currently-live slots
     */
    get count(): number;
    /**
     * @returns {number} total allocated slot capacity
     */
    get capacity(): number;
    /**
     * Look up a slot by canonical pair. Caller is responsible for ordering
     * `(idA, idB)` such that the same pair always hashes the same way.
     *
     * @param {number} idA
     * @param {number} idB
     * @returns {number} slot id or {@link MANIFOLD_SLOT_ABSENT}
     */
    find(idA: number, idB: number): number;
    /**
     * Acquire a slot for the pair, creating one if absent, and mark it
     * "touched this frame". Returns the slot id. Calling `acquire` twice in
     * the same step on the same pair is a no-op for the second call.
     *
     * @param {number} idA
     * @param {number} idB
     * @returns {number} slot id
     */
    acquire(idA: number, idB: number): number;
    /**
     * @param {number} slot
     * @returns {number} body A id stored at this slot
     */
    bodyA(slot: number): number;
    /**
     * @param {number} slot
     * @returns {number}
     */
    bodyB(slot: number): number;
    /**
     * @param {number} slot
     * @returns {number} 0..MAX_CONTACTS_PER_MANIFOLD
     */
    contact_count(slot: number): number;
    /**
     * @param {number} slot
     * @returns {boolean}
     */
    is_touched(slot: number): boolean;
    /**
     * @param {number} slot
     * @returns {boolean}
     */
    was_touched_prev(slot: number): boolean;
    /**
     * Iterate the dense list of currently-live slots without going through
     * the pair map. `i` ranges `[0, count)`.
     * @param {number} i
     * @returns {number}
     */
    live_at(i: number): number;
    /**
     * Clear the contact list for a slot (counts go to zero, warm-start
     * impulses also reset). Used when narrowphase determines the pair is
     * separated this frame.
     * @param {number} slot
     */
    clear_contacts(slot: number): void;
    /**
     * Reset the contact count to zero so the next `set_contact(slot, 0, …)`
     * lands at index 0, but DO NOT zero the data slab. Warm-start impulses
     * (`j_n`, `j_t1`, `j_t2`) at each contact slot survive — the subsequent
     * `set_contact` calls only overwrite the first 10 floats (position,
     * normal, depth) per contact.
     *
     * Use this when narrowphase determines the pair still has overlap and
     * is about to refill the slot with the new frame's contacts. Use
     * {@link clear_contacts} instead when the pair has genuinely separated
     * and the cached impulses should be evicted.
     *
     * @param {number} slot
     */
    begin_refill(slot: number): void;
    /**
     * Write a contact point into a slot. Increments contact_count if `idx`
     * exceeds the current count.
     *
     * Writes geometry (offsets 0..9), the feature_id at offset 13, and the
     * per-contact combined friction / restitution at offsets 14 / 15.
     * Deliberately does NOT touch the warm-start impulse fields (offsets
     * 10..12) — the narrowphase match-and-merge pass uses this to refill
     * a slot while preserving the cached impulses that the solver's
     * warm-start needs.
     *
     * @param {number} slot
     * @param {number} idx contact index in `[0, MAX_CONTACTS_PER_MANIFOLD)`
     * @param {number} lax
     * @param {number} lay
     * @param {number} laz
     * @param {number} lbx
     * @param {number} lby
     * @param {number} lbz
     * @param {number} nx normal world x (B toward A)
     * @param {number} ny
     * @param {number} nz
     * @param {number} depth
     * @param {number} feature_id stable cross-frame identifier of the
     *   geometric feature pair that produced this contact; 0 means
     *   "no info, fall back to position matching"
     * @param {number} friction combined friction for this contact's specific
     *   source-collider pair
     * @param {number} restitution combined restitution for this contact
     */
    set_contact(slot: number, idx: number, lax: number, lay: number, laz: number, lbx: number, lby: number, lbz: number, nx: number, ny: number, nz: number, depth: number, feature_id?: number, friction?: number, restitution?: number): void;
    /**
     * Read the per-contact combined friction stored at one contact.
     * @param {number} slot
     * @param {number} idx
     * @returns {number}
     */
    friction_of(slot: number, idx: number): number;
    /**
     * Read the per-contact combined restitution stored at one contact.
     * @param {number} slot
     * @param {number} idx
     * @returns {number}
     */
    restitution_of(slot: number, idx: number): number;
    /**
     * Zero the warm-start impulse fields (j_n, j_t1, j_t2) for one contact
     * index inside a slot. Called by the narrowphase match-and-merge pass
     * after writing a candidate that did NOT match any prior-frame contact:
     * the geometry at this index is "new" and inheriting impulses from
     * whatever happened to be cached at this index last frame would
     * destabilise the solver.
     *
     * Distinct from {@link clear_contacts} (which wipes the entire slot's
     * data slab including geometry) — `clear_impulses` only touches the
     * three impulse fields at one contact index, leaving the geometric
     * fields and feature_id intact.
     *
     * @param {number} slot
     * @param {number} idx
     */
    clear_impulses(slot: number, idx: number): void;
    /**
     * Read the feature_id stored at one contact within a slot.
     * @param {number} slot
     * @param {number} idx
     * @returns {number}
     */
    feature_id_of(slot: number, idx: number): number;
    /**
     * Read a contact point. Writes 10 floats: lax, lay, laz, lbx, lby, lbz, nx, ny, nz, depth.
     * @param {number} slot
     * @param {number} idx
     * @param {number[]|Float64Array} out
     * @param {number} out_offset
     */
    get_contact(slot: number, idx: number, out: number[] | Float64Array, out_offset: number): void;
    /**
     * Get the contact-normal world x component for a given contact.
     * Provided for testability without exposing the raw Float64Array view.
     * @param {number} slot
     * @param {number} idx
     * @returns {number}
     */
    contact_normal_x(slot: number, idx: number): number;
    /** @param {number} slot @param {number} idx @returns {number} */
    contact_normal_y(slot: number, idx: number): number;
    /** @param {number} slot @param {number} idx @returns {number} */
    contact_normal_z(slot: number, idx: number): number;
    /** @param {number} slot @param {number} idx @returns {number} */
    contact_depth(slot: number, idx: number): number;
    /**
     * Raw contact-point buffer. Hot-path solver code dereferences this
     * directly via {@link slot_data_offset} + {@link CONTACT_STRIDE} to avoid
     * per-field accessor overhead. Buffer identity is stable until grow.
     * @returns {Float64Array}
     */
    get data_buffer(): Float64Array;
    /**
     * Word offset into {@link data_buffer} where the first contact of `slot`
     * begins. Subsequent contacts within the slot are at
     * `offset + c * CONTACT_STRIDE`.
     * @param {number} slot
     * @returns {number}
     */
    slot_data_offset(slot: number): number;
    /**
     * Advance touched flags by one frame and evict slots that have been
     * untouched for {@link DEFAULT_GRACE} consecutive frames.
     */
    advance_frame(): void;
    /**
     * @private
     * @returns {number} newly-allocated slot id
     */
    private __allocate_slot;
    /**
     * @private
     * @param {number} slot
     */
    private __release_slot;
    /**
     * @private
     */
    private __grow;
    /**
     * @private
     * @param {number} v
     */
    private __heap_push;
    /**
     * @private
     * @returns {number}
     */
    private __heap_pop;
}
import { PairUint32Map } from "../../../core/collection/PairUint32Map.js";
//# sourceMappingURL=ManifoldStore.d.ts.map