Class ChunkedBitmap

The world bitmap, partitioned into fixed-size square chunks.

ChunkedBitmap is the source of truth for terrain state. Every cell stores a single material id (0 = air, 1..255 = registered material). Mutations dirty the owning chunk; consumers (renderers, physics adapter) walk dirty chunks at end-of-frame to project the bitmap into colliders and textures.

The bitmap is dependency-free and framework-agnostic; it can be used outside Phaser (e.g. with PixiJS or in a headless tool chain).

Constructors

Properties

chunks: readonly Chunk[]

Row-major chunk grid: chunks[cy * chunksX + cx].

chunkSize: number

Edge length of each chunk in pixels.

chunksX: number

Number of chunks along the X axis.

chunksY: number

Number of chunks along the Y axis.

height: number

World height in pixels.

materials: MaterialRegistry

Material registry shared with this bitmap.

width: number

World width in pixels.

Accessors

  • get activeCells(): Set<number>
  • Sparse set of cell indices (y * width + x) the cellular- automaton step should visit on its next call.

    Lazy-allocated on first access. Once it exists, every setPixel mutation auto-marks the changed cell and its 8 neighbors. The sim consumes this set at the start of each step (snapshot + clear), then the same set fills back up during processing via setPixel calls and explicit markActive calls for cells with ongoing state (fire timers, settling sand counters).

    Returns Set<number>

  • get cellTimers(): Uint8Array
  • Lazy-allocated Uint8Array(width * height) of per-cell counters used by CellularAutomaton.step for features that need state across ticks (sand-rest counter, fire-burn timer, etc.).

    On first read, the array is zero-initialized. Subsequent reads return the same instance. setPixel auto-resets the cell's timer to 0 because the cell's content just changed; the cellular-automaton step manages increments and threshold checks on cells that didn't move this tick.

    Caps at 255 per cell (Uint8Array max); threshold checks above 255 saturate.

    Returns Uint8Array

  • get hasActiveCellTracking(): boolean
  • Whether active-cell tracking has been initialized on this bitmap. Used by sim helpers that want to peek without lazy- initializing the set.

    Returns boolean

  • get horizFlowSource(): Uint16Array
  • Lazy-allocated Uint16Array(width * height) of per-cell horizontal-flow source-X memory used by the v2.7.6 anti- oscillation rule in CellularAutomaton.step. 0xFFFF means "no recent horizontal move."

    On first read the array is initialized to all-0xFFFF. The sim writes to it after every successful horizontal flow move (flowSource[targetIdx] = sourceX); reading back at the next call lets the moved cell skip a flow back to the same source — preventing 2-tick oscillation cycles that would otherwise force a conservative same-rank-beyond guard.

    setPixel resets the cell's entry to 0xFFFF because the occupant changed.

    Returns Uint16Array

Methods

  • Internal fast-path used by CellularAutomaton.stepLiquid. Returns the underlying mass array (allocating + seeding it on first use). Callers MUST treat the array read-only for cells they don't own and MUST call _markCellChanged after writing so chunk dirty / active-cell tracking stay consistent.

    Bypasses the validation overhead of setMass; safe only inside the well-behaved sim loop. Public users should stick to getMass / setMass.

    Returns Float32Array

  • Internal fast-path: returns the per-cell pool-id sidecar (Uint16Array(width × height)), allocating it lazily on first access and filling with the NO_POOL = 0xFFFF sentinel. Used by the v3.1 pool-detection step in CellularAutomaton. Public callers don't have a use for this — it's pool-id tracking, not material id.

    Returns Uint16Array

  • Internal fast-path companion to _getMassArrayUnchecked. Marks the chunk owning (x, y) visually dirty and adds the cell to the active set if active-cell tracking is on. Skips the 8-neighbor mark — pass idChanged: true only when the cell's material id transitions (air ↔ fluid), which warrants neighbor wake-up. For mass-only changes, leave it false.

    Critically, does NOT set chunk.dirty (the collider- rebuild flag). Only static-material id changes affect the static contour, and those always go through setPixel. Fluid mass changes / fluid id transitions only need a visual repaint.

    Bypasses bounds checks; caller guarantees (x, y) is in range.

    Parameters

    • x: number
    • y: number
    • idChanged: boolean

    Returns void

  • Internal fast-path id reader for the sim. Reads the material id at (x, y). Skips out-of-bounds checks (the caller must clamp / break first).

    Parameters

    • x: number
    • y: number

    Returns number

  • Internal fast-path id writer for the sim. Writes materialId to the bitmap cell at (x, y) and resets the cell's per-cell timer + horizontal-flow source memory. Skips bounds + integer validation, the no-op-on-same-id check, and the active-set / dirty-chunk bookkeeping — caller is responsible for following up with _markCellChanged.

    Use only inside the sim hot path. Public callers use setPixel.

    Parameters

    • x: number
    • y: number
    • materialId: number

    Returns void

  • Clears the collider dirty flag on a chunk. Called by the physics adapter after a successful rebuild. Does not touch visualDirty.

    Parameters

    Returns void

  • Clears the visual dirty flag on a chunk. Called by the renderer after a successful texture upload. Does not touch dirty.

    Parameters

    Returns void

  • Idempotent. Initializes the active-cell set and seeds it with every non-air, non-static cell currently in the bitmap so the sim can pick up cells that were placed before tracking turned on. The first CellularAutomaton.step call invokes this; you can also call it eagerly if you need the auto-mark side-effect on setPixel before the first step runs.

    Returns void

  • Visits every currently-dirty chunk in stable row-major (cy, cx) order.

    The callback may not mutate the dirty flag during iteration (call clearDirty afterwards). Iteration order is fixed so consumers (Box2D rebuild, GPU upload) see a deterministic sequence — useful for replay debugging and best-effort determinism (architecture doc § Determinism).

    Parameters

    • callback: ((chunk: Chunk) => void)
        • (chunk): void
        • Parameters

          Returns void

    Returns void

  • Returns the chunk at the given chunk-grid coordinates.

    Parameters

    • cx: number
    • cy: number

    Returns Chunk

    If cx or cy is outside [0, chunksX) or [0, chunksY).

  • Reads the mass of the cell at (x, y).

    For binary cells (air, static, sand, fire) returns 0 for air and 1 for any registered material — same as getPixel(x, y) === 0 ? 0 : 1. For mass-tracked fluid cells (water, oil, gas under the v3 rules) returns the actual stored mass, which can be 0..MAX_MASS + MAX_COMPRESS.

    Out-of-bounds returns 0 (treats outside-the-world as air-equivalent), matching getPixel's behavior.

    Parameters

    • x: number
    • y: number

    Returns number

  • Reads a cell. Returns 0 for any out-of-bounds coordinate, treating outside-the-world as air. This simplifies neighbor sampling at world edges in algorithms like marching squares.

    Parameters

    • x: number
    • y: number

    Returns number

  • Adds (x, y) to the active-cell set so the next step will visit it. Used by the cellular automaton to keep cells with ongoing state (fire timer ticking, sand rest counter incrementing) in the rotation when setPixel wasn't called.

    No-op when active-cell tracking hasn't been initialized (callers in the sim run after enableActiveCellTracking so the guard is conservative). Out-of-bounds coordinates are silently ignored.

    Parameters

    • x: number
    • y: number

    Returns void

  • Writes the mass for the cell at (x, y). The cell's material id is updated as a side effect:

    • If mass <= 0, the cell becomes air (id = 0).
    • If mass > 0, the cell keeps its current id (or, if the cell was previously air, takes the id from the optional idIfAir argument).

    Lazy-allocates the mass array on first call. Marks the owning chunk dirty and propagates active-cell activation (same as setPixel).

    Used by CellularAutomaton.step's mass-transfer rules; not typically called by application code.

    Parameters

    • x: number
    • y: number
    • mass: number
    • idIfAir: number = 0

    Returns void

    If (x, y) is out of bounds, or if mass is not finite.

  • Writes a cell.

    • Throws if (x, y) is outside the bitmap. Carve / deposit ops are responsible for clipping their footprint before calling.
    • Throws if materialId is not an integer in 0..255.
    • No-ops (does not mark dirty) when the new value equals the current.

    On any real change, marks the owning chunk's dirty and visualDirty flags. This is the single mutation primitive that satisfies CLAUDE.md hard rule #5.

    Parameters

    • x: number
    • y: number
    • materialId: number

    Returns void

  • Maps a world coordinate to the enclosing chunk's grid coordinates. Does not validate the input is within the world; callers in the algorithm layer may need to query out-of-bounds positions.

    Parameters

    • x: number
    • y: number

    Returns {
        cx: number;
        cy: number;
    }

    • cx: number
    • cy: number
  • Maps a world coordinate to chunk-local pixel coordinates within the enclosing chunk.

    Parameters

    • x: number
    • y: number

    Returns Point