import { Container, Renderer, Ticker } from 'pixi.js'; import { Disposable } from '../utils/Disposable.js'; import { ReelSymbol } from '../symbols/ReelSymbol.js'; import { SymbolFactory } from '../symbols/SymbolFactory.js'; import { SymbolData, Stacking } from '../config/types.js'; import { ReelAxis } from './ReelAxis.js'; import { ReelCurve, ReelCurveInput } from './ReelCurve.js'; import { EventEmitter } from '../events/EventEmitter.js'; import { ReelEvents } from '../events/ReelEvents.js'; import { RandomSymbolProvider } from '../frame/RandomSymbolProvider.js'; import { ColumnTarget } from '../frame/ColumnTarget.js'; import { ReelViewport } from './ReelViewport.js'; import { SpinningMode } from '../spin/modes/SpinningMode.js'; import { Gsap } from '../utils/gsap.js'; /** * Options for `Reel.nudge()` / `ReelSet.nudge()`. a post-stop reposition * that shifts the reel by `distance` symbol positions and reveals new * caller-supplied symbols. * * Nudges run only while the reel is at rest (post-stop). Calling on a * moving reel throws. */ export interface NudgeOptions { /** * Number of full symbol positions to shift. Must be a positive integer * strictly less than the reel's total strip capacity * (`bufferStart + visibleCells + bufferEnd`). `incoming.length` must * equal this exactly. */ distance: number; /** * Travel direction, **relative to the reel's own axis**. * * - `'forward'`. the strip travels the way this reel normally spins. * On a vertical/forward reel that is downward, with new symbols * entering from the top. * - `'reverse'`. the strip travels the other way, with new symbols * entering from the opposite edge. * * Which screen edge feeds the reel is derived from the axis polarity, * so a reel built with `direction('reverse')` nudges upward on * `'forward'` without the caller re-deriving anything. */ direction: 'forward' | 'reverse'; /** * Symbol ids in **start-to-end order of their final on-strip position** * (top-down for vertical, left-to-right for horizontal), including any * overflow into the off-screen buffer. Length must equal `distance` * exactly. * * - `incoming[0]` ends up at the start-most new position. When the * reel feeds from its start edge this is the new first visible cell * (or, if `distance > bufferStart + visibleCells`, spills into * bufferEnd tail-first via the trailing entries). When it feeds from * the end edge and `distance > visibleCells`, `incoming[0]` lands in * bufferStart (still start-most). * - `incoming[distance-1]` ends up at the end-most new position. * Mirror of the above. * * For the common case of `distance <= visibleCells`, every entry is a * visible cell in strip order and you can ignore the overflow rules. */ incoming: string[]; /** Total animation duration in ms. Defaults to `200 * distance`. */ duration?: number; /** * GSAP easing function name. Defaults to `'power2.out'`. a smooth * deceleration with NO overshoot. If you pass an overshooting ease * (`back.out(N)`, `elastic.out(...)`), the engine clamps the displacement * so wraps never fire past the landing position; the eased value is * computed but the strip's travel is bounded. */ ease?: string; /** * Optional delay (ms) before the tween begins. Validation throws fire * immediately on the call, but the actual reel mutation + tween are * deferred by this much. Useful with `Promise.all([...])` to stagger * parallel nudges: * * ```ts * await Promise.all( * reels.map((reel, i) => * reelSet.nudge(reel, { ..., startDelay: i * 80 }), * ), * ); * ``` * * `ReelSet.nudge(reel, options, { stagger })` is sugar for the common * uniform-stagger case. */ startDelay?: number; /** * Abort the nudge mid-flight. If signalled before the tween starts, the * call rejects with an `AbortError` and no strip mutation happens. If * signalled during the tween, the tween is killed, the strip is snapped * to its post-nudge position (deterministic landing. the contract is * "incoming lands at these positions"), and the promise rejects with an * `AbortError`. `nudge:cancelled` fires on the reel-set bus. */ signal?: AbortSignal; } export interface ReelConfig { reelIndex: number; visibleCells: number; bufferStart: number; bufferEnd: number; symbolWidth: number; symbolHeight: number; symbolGapX: number; symbolGapY: number; symbolsData: Record; initialSymbols: string[]; /** * Y offset of this reel relative to the viewport's top edge. Set by the * builder so jagged shapes (pyramids) align according to `reelAnchor`. * Default 0. */ mainOffset?: number; /** Travel projection for this reel. Defaults to vertical/forward. */ axis?: ReelAxis; /** * Pixel height of this reel's box. Used for MultiWays cell-height * derivation (`extent / visibleCells`). Defaults to * `visibleCells * symbolHeight`. */ extent?: number; /** * SPIN-time uniform cell height. During SPIN every reel uses this same * height. AdjustPhase later swaps to per-reel `extent / visibleCells`. * Defaults to `symbolHeight`. */ spinCellSize?: number; /** * Render order of cells within the reel. Default `'ascending'`. the cell * at the larger main coordinate draws in front. */ /** * The gsap instance this reel's tweens live on. Defaults to the one * resolved at lib-load time; `ReelSetBuilder.gsap(...)` overrides it PER * SET, so two sets on one stage can use different instances. */ gsap?: Gsap; cellStacking?: Stacking; /** * Render order of reels within the set. Default `'ascending'`. the last * reel draws in front. */ reelStacking?: Stacking; /** * Cylinder curvature for this reel. Omitted or `0` leaves it dead flat and * costs nothing. See {@link ReelCurveConfig}. */ curve?: ReelCurveInput; /** * Reel-local cross coordinate the curve's perspective converges on. Set by * the builder from `curveFocus(...)`; omitted means the reel's own centre. */ curveFocus?: number; /** * Renderer to draw the reel's warp texture with. Present only when the set * was built with `curveMode('warp')` and a `renderer(...)`; its presence is * what switches this reel from the per-symbol projection to the whole-reel * vertex warp. */ curveRenderer?: Renderer; /** Ticker driving the warp's per-frame texture refresh. Warp mode only. */ curveTicker?: Ticker; /** * Cross-axis room, in pixels per side, for art that is wider than its cell. * Warp mode only; set by `ReelSetBuilder.curveBleed()`. */ curveBleed?: number; } /** * Internal sentinel marking non-anchor cells of a big symbol's block. * Never crosses the public API. `getVisibleSymbols()` resolves it to the * anchor's id. */ export declare const OCCUPIED_SENTINEL = "__pixi_reels_occupied__"; /** * One vertical column of a slot board. * * A `Reel` owns: * - the `ReelSymbol[]` currently on screen (a small buffer above the * visible cells + the visible cells + a small buffer below. so symbols * can fade in from off-screen cleanly) * - the `ReelMotion` that adds a Y delta each tick and wraps symbols * that scroll off the ends * - a `StopSequencer`. the queue of target symbols the reel still has * to land on before it can stop * * You generally do not touch a `Reel` directly. Drive the `ReelSet` and * let it fan out. Reels are exposed on `reelSet.reels` so you can read * the current grid (`reel.getSymbolAt(cell)`) or listen to per-reel * events (`phase:enter`, `landed`, `symbol:created`, ...). */ export declare class Reel implements Disposable { readonly container: Container; readonly events: EventEmitter; readonly reelIndex: number; /** Current symbols in order (top buffer → visible → bottom buffer). */ symbols: ReelSymbol[]; /** Current spin speed (pixels per frame). Set by phases. */ speed: number; /** Current spinning mode. */ spinningMode: SpinningMode; private readonly _axis; /** Cylinder curvature, or `undefined` when this reel is flat. */ private _curve?; /** Reel-local cross coordinate the curve converges on. `null` = own centre. */ private readonly _curveFocus; /** * Whole-reel vertex warp, when the set is in `curveMode('warp')`. Present * means the reel container is rendered to a texture and drawn through a * displaced mesh instead of being drawn directly, and symbols are left * completely alone - the warp bends all of them at once. */ private _warp?; /** True when this reel is drawn through a whole-reel warp. */ private readonly _warping; private readonly _mainCell; private readonly _mainGap; private readonly _crossGap; /** This reel's cell extent along the strip. Varies per reel; reshape mutates it. */ private _cellMain; /** This reel's cell extent across the strip. Uniform across the set. */ private readonly _cellCross; private _symbolFactory; private _randomProvider; private _viewport; private _symbolsData; private _visibleCells; private _bufferStart; private _mainOffset; private readonly _gsap; private _cellStacking; private _reelStacking; private _extent; private _spinCellSize; private _symbolGapY; private _symbolGapX; private _isDestroyed; private _isStopping; /** * True between `notifySpinStart()` and `notifySpinEnd()`. While set, * `_replaceSymbol` fires `onReelSpinStart(true)` on each freshly * installed symbol so it can join the spin presentation (pool recycling * wipes per-instance state, so the symbol can't know on its own). */ private _spinPresentationActive; private _anticipationActive; /** * True only while the reel is fully at rest (build time, and from * `notifyLanded()` until the next `notifySpinStart()`). NOT the inverse * of `_spinPresentationActive`: that flag drops at `notifySpinEnd()`, * just before the bounce, while the strip is still visibly moving and * the stop sequencer is still installing the result symbols. */ private _atRest; private _isNudging; /** * Symbol-id queue consulted by `_onSymbolWrapped` during a nudge. Each * wrap pulls one id from the front; when empty (or `null`), the wrap * falls back to `stopSequencer` (if `_isStopping`) or `_randomProvider`. * * Populated by `nudge()` and cleared once the tween completes. */ private _nudgeQueue; /** * GSAP tween handle for the active nudge animation. Stored so `destroy()` * and `skipNudge()` can `kill()` it cleanly; cleared in `onComplete` and * on cancellation. `null` between nudges. */ private _nudgeTween; /** * Rejection function for the in-flight nudge's promise. Called by * `destroy()` and `signal.abort()` so consumers `await`-ing the nudge * see a deterministic error instead of a hung promise. Cleared on * `onComplete`. `null` between nudges. */ private _nudgeReject; /** * Internal stub instances reused for OCCUPIED cells inside a big-symbol * block. Allocated on demand (one per concurrent OCCUPIED cell on this * reel), never pooled through `SymbolFactory`. The views are invisible. * the anchor symbol is sized up to cover the whole block. */ private _occupiedStubs; /** * Per-cell marker recording which cells are non-anchor cells of a big * symbol. Populated when frames are placed; consulted by `getVisibleSymbols` * and `getSymbolAt` so anchor identity propagates through the block. * * Indexed by visible-cell 0..visibleCells-1. Each entry is `null` for a * normal cell, or `{ anchorCell }` for a cell occupied by another cell's * anchor. */ private _occupancy; /** * Optional resolver for cross-reel OCCUPIED cells. Set by `ReelSet` so * `getVisibleSymbols()` returns the anchor's id even when the anchor * lives on a different reel (a 2x2 bonus straddles reels c, c+1). * Without it, cross-reel OCCUPIED cells return the OCCUPIED sentinel. */ private _crossReelResolver; constructor(config: ReelConfig, symbolFactory: SymbolFactory, randomProvider: RandomSymbolProvider, viewport: ReelViewport); get isDestroyed(): boolean; get isStopping(): boolean; set isStopping(value: boolean); /** True while a `nudge()` tween is in flight on this reel. */ get isNudging(): boolean; get bufferStart(): number; get bufferEnd(): number; get visibleCells(): number; /** * This reel's cell width in SCREEN pixels - the first argument to * `ReelSymbol.resize`. On a vertical set this is the cross extent and is * constant; on a horizontal set it is the MAIN extent, so a pyramid or * MultiWays reshape moves it. */ get symbolWidth(): number; /** * This reel's cell height in SCREEN pixels - the second argument to * `ReelSymbol.resize`. Mirror of {@link Reel.symbolWidth}: on a vertical * set this is the main extent and a MultiWays reshape moves it; on a * horizontal set it is the constant cross extent. * * During SPIN the main extent is still `spinCellSize`; the per-reel target * comes into effect when AdjustPhase commits the reshape. */ get symbolHeight(): number; /** * Project an axis-relative (main, cross) pair back to the screen * `(width, height)` that `ReelSymbol.resize` takes. The single place the * engine converts back, so a jagged horizontal set varies width from * exactly the arithmetic a vertical one uses to vary height. */ private _screenSize; /** * Screen size of an `reels x cells` block, gaps included. `reels` always * spans the cross axis and `cells` the main axis, so the screen width and * height this maps to swap between orientations. */ private _blockSize; /** This reel's cell extent ALONG the strip. A reshape moves it. */ get cellMain(): number; /** This reel's cell extent ACROSS the strip. Uniform across the set. */ get cellCross(): number; /** The inter-cell gap along the strip (symbolGap.y vertical, .x horizontal). */ get mainGap(): number; /** The inter-reel gap across the strip (symbolGap.x vertical, .y horizontal). */ get crossGap(): number; /** Pixel extent of this reel's box along the strip. Set by builder. */ get extent(): number; /** Y offset of this reel relative to the viewport top. Set by builder, immutable. */ get mainOffset(): number; /** * SPIN-time uniform cell height. All reels in a slot use this value during * the SPIN phase regardless of their per-reel `symbolHeight`. Frozen at * construction. */ get spinCellSize(): number; /** The gsap instance this reel's tweens live on. Read by every phase. */ get gsap(): Gsap; /** This reel's travel projection (orientation + direction). */ get axis(): ReelAxis; /** * This reel's cylinder curvature, or `undefined` when it renders flat. * Read it to map your own overlays onto the bent grid. */ get curve(): ReelCurve | undefined; /** Update reel for one frame. Called by SpinController via ticker. */ update(deltaMs: number): void; /** * Get visible symbol IDs (top to bottom, excluding buffers). * * Big-symbol cells resolve to the anchor's id. both **same-reel** * (the anchor lives on this reel) and **cross-reel** (the anchor is on * a leftward reel of a wider block). The cross-reel resolver is * injected by `ReelSet`; without it, cross-reel OCCUPIED cells would * return the OCCUPIED sentinel, which is the only difference vs. * `ReelSet.getVisibleGrid()`. With the resolver wired, the two are * equivalent for any reel. `reels.map(r => r.getVisibleSymbols())` * matches `reelSet.getVisibleGrid()`. */ getVisibleSymbols(): string[]; /** * This reel's full strip as a `ColumnTarget` -- buffers included, anchors * at their true positions. * * `getVisibleSymbols()` reports the visible window only, so it cannot be * handed back: a block anchored in `bufferStart` with just its tail * showing reads as that id at visible cell 0, and feeding it to * `setResult` re-anchors the block there. This keeps the anchor where it * is, so `setResult(reelSet.getTargets())` reproduces the board. */ getTarget(): ColumnTarget; /** * Get symbol at a visible cell (0-indexed from top visible). * For non-anchor cells of a big symbol, walks up to the anchor cell and * returns the anchor symbol so animations target the actual visual. */ getSymbolAt(visibleCell: number): ReelSymbol; /** * Pull any lifted (unmasked) view that has ended up in a buffer slot back * under the mask. * * `_replaceSymbol` never lifts a buffer slot, but a symbol lifted while it * was VISIBLE can still travel into a buffer slot without being replaced: * a nudge rotates the array and only the wrapped symbol goes through * `_replaceSymbol`, so an unmask symbol nudged out of the window kept its * seat above the mask and hung there outside the grid. Runs on every * settle, where the strip's final slots are known. */ private _reMaskLiftedBufferSlots; /** * Swap the symbol at a single visible cell in-place, without restarting * the spin or rebuilding the rest of the strip. * * Useful for live presentation effects at rest. converting a wild * after a cascade pop, swapping to a sticky variant after a win. * without going through the full `placeSymbols` / `setResult` paths. * * The symbol's `zIndex`, parent (masked vs unmasked), and visual state * are reset by `_replaceSymbol` so callers don't need to follow up * with `refreshZIndex`. The motion layer is **not** snapped. call * `snapToGrid()` separately if you need to re-grid. * * Throws if: * - the reel is currently moving (`speed !== 0` or `isStopping`). * A mid-spin swap would be overwritten by the next wrap/stop frame * anyway; the fail-loud throw spares the caller the silent loss. * - `visibleCell` is out of `[0, visibleCells)`. * - `symbolId` is not registered. * - the cell is a non-anchor cell of an existing big-symbol block. * - the cell currently holds the anchor of a big-symbol block. big * blocks span multiple cells (and possibly reels) and require * `placeSymbols` + the cross-reel OCCUPIED coordinator. * - `symbolId` itself is a big symbol. same reason. * * Pin overlap is **not** detected at this layer (Reel doesn't see the * pin map). Use `ReelSet.setSymbolAt(reel, cell, id)` for the safe * caller-facing surface that also throws on pinned cells. */ setSymbolAt(visibleCell: number, symbolId: string): void; /** * Shift the reel by `distance` symbol positions, animating the strip with * a GSAP tween and revealing caller-supplied `incoming` symbols. The reel * must be at rest (post-stop). throws otherwise. * * The wrap pipeline drives identity changes during the tween: any incoming * symbol whose final destination is reachable via pre-placement (within * the leading buffer) is set up front; the rest stream through the wrap * callback as the strip moves. `incoming` is always top-down by final * on-strip position. see `NudgeOptions.incoming` for the overflow rules. * * **Big symbols are supported** as long as every block on the strip * (anchor + stubs) survives the rotation without crossing the wrap * boundary: * - down: anchorCell + h - 1 + distance < total * - up: anchorCell ≥ distance * * Blocks that wouldn't survive throw, as do cross-reel blocks (w > 1). * Use case: a 1xH block lands with stubs in bufferEnd. nudge up to * bring the whole block into view. * * Throws if: * - the reel is spinning, stopping, already nudging, or destroyed, * - `distance < 1`, `>= total strip capacity`, `direction` invalid, or * `incoming.length !== distance`, * - any `incoming` id is unregistered or is a big symbol, * - any block on the reel wouldn't survive the rotation, * - any cell on this reel is part of a cross-reel block (w > 1), * - the abort signal is already aborted on entry. * * Resolves with `{ symbols }`. the new visible column top-to-bottom. * Rejects with an `AbortError` if `options.signal` aborts mid-tween or * if the reel is destroyed before the tween completes. * * @param onPrepared Internal hook fired once pre-placement + grid snap * are done but before the tween starts. `ReelSet.nudge` uses this to * emit `nudge:start` after the strip has been mutated, so listeners * observe the about-to-animate state, not the pre-mutation state. */ nudge(options: NudgeOptions, onPrepared?: () => void): Promise<{ symbols: string[]; }>; /** * Fast-forward the active nudge tween to its landed state and resolve. * No-op if no nudge is in flight. The tween's `onComplete` fires * synchronously, the strip snaps to the final position, `_nudgeQueue` * drains, and the original `nudge()` promise resolves on the next * microtask. * * Useful for player-driven "skip" buttons or accessibility paths that * want to land immediately without waiting for the full animation. */ skipNudge(): void; /** * Place a target column immediately (for skip/turbo/cascade landing). * * `target.visible[0..n-1]` fills the visible window; `bufferStart` and * `bufferEnd` fill the off-window slots either side, closest cell first. * Slots the target does not specify are filled with random symbols. */ placeSymbols(target: ColumnTarget): void; /** * Compute the canonical zIndex for a single symbol view at a given * array index. Centralizes the formula used by both `refreshZIndex` * (full rescan) and the per-swap activate path (so newly placed * symbols land with their correct zIndex without the caller needing * to remember to call `refreshZIndex` afterwards). */ private _computeSymbolZIndex; /** * Recompute `zIndex` for every symbol in the reel. * * Formula: `symbolData.zIndex ?? 0` (scaled by 100 to leave room for cell * ordering), plus the symbol's current array index. so bottom-cell symbols * render in front of top-cell symbols and any symbol with a higher * configured base zIndex (e.g. wild, bonus) renders above its neighbors. * * Called automatically after wraps, snaps, and direct placement. Also * called inline by `_replaceSymbol` for the single newly-placed symbol. * so consumers who swap one symbol at a time (via the public APIs that * funnel into `_replaceSymbol`) get correct layering for free, no * manual `refreshZIndex` required. Call it manually after mutating * `symbolsData.zIndex` at runtime. */ refreshZIndex(): void; destroy(): void; /** * Whether the symbol with this id has `unmask: true` in its data. i.e. * its view should be parented to `viewport.unmaskedContainer` to render * above the reel mask. */ private _isUnmasked; /** Whether strip slot `index` sits outside the visible window. */ private _isBufferSlot; /** * Which pool slot `index` draws from: the visible window is `'spinning'`, * and a buffer cell names its side so that side's pools apply. */ private _slotKind; /** * Whether the symbol at strip slot `index` should render above the mask * RIGHT NOW. Unmask is an at-rest presentation of a VISIBLE cell: * * - while the reel is in motion (including the stop approach and * bounce, when the result symbols are installed), every view * (unmask ids included) stays in the masked reel container so * nothing scrolls visibly outside the grid, and * - a buffer slot never lifts at all. it is parked outside the window * precisely so the mask hides it, and a lifted one hangs above or * below the grid in plain sight until the next spin re-masks it. * `placeStrip` writes buffer slots at rest on every skip, which is * exactly when that used to happen. * * Landed visible-cell symbols are lifted by `notifyLanded()`; * `notifySpinStart()` pulls them back down before the strip moves. */ private _effectiveUnmask; /** * Pick the right parent container for a symbol view based on its * `unmask` flag, its slot, and the reel's spin state. At-rest unmasked * symbols in a visible cell sit in `viewport.unmaskedContainer` (above * the reel mask); everything else lives in this reel's own container * (which is itself inside `viewport.maskedContainer`). */ private _parentForSymbolId; /** * Position a symbol view at a given reel-local Y, choosing X and any * parent-translation offset based on whether the symbol is unmasked. * * Unmasked views live in `viewport.unmaskedContainer` (at viewport * (0,0)), so we add `reel.container.x` and `reel.container.y` to keep * the at-rest cell position aligned with the reel column. Masked views * live in `this.container`, so reel-local coords map directly. */ private _placeSymbolView; /** * Build the curvature for this reel, or `undefined` when it would be flat. * A flat set must not carry a curve object at all: that keeps the render * loop and every placement byte-identical to an uncurved build. */ private _buildCurve; /** * Convert a view's current y back to reel-local coords. The view may * be parented to either `this.container` (already reel-local) or * `viewport.unmaskedContainer` (viewport-local. needs the reel offset * subtracted). */ private _toReelLocalY; /** * Re-bake the reel's `container.x/y` offset into any currently-lifted * (unmasked) view. * * `ReelMotion.snapToGrid()` writes bare reel-local Y to every symbol * view — it has no notion that some views were re-parented into * `viewport.unmaskedContainer` and need the reel offset added to stay * aligned. Masked reels have `container.y === 0`, so the two spaces * coincide and this is a no-op; on a jagged/pyramid layout (non-zero * `mainOffset`) the snap would drop the offset and jump the lifted view. * * Call this after ANY motion write. `advance()` derives positions from the * array index and writes them absolutely (it was `+=` in v1, which is why * this used to be snap-only), so it drops the offset just as `snapToGrid` * does. * * During a spin the loop finds nothing: `beginMotion()` pulls every lifted * view back down before the strip moves. A nudge is the case that matters, * because it runs at rest with views still lifted. */ private _syncUnmaskedViewOffsets; private _setupSymbolPositions; private _onSymbolWrapped; private _replaceSymbol; /** * Acquire an OCCUPIED stub. Reuses any free stub stored locally; allocates * a new one if none are available. Stubs are never returned to * `SymbolFactory`. */ private _acquireOccupiedStub; private _releaseOccupiedStub; /** * After the visible target frame has been placed, scan the strip to * size big-symbol anchors and populate the OCCUPIED occupancy map. * * Called from `snapToGrid` and `placeSymbols` so it runs both for normal * stop landing AND for skip/turbo. For non-anchor cells of a block, the * anchor symbol is sized to span the block; the OCCUPIED stub at that * cell stays invisible underneath. * * **Two scans:** * * 1. Visible anchors. sizes blocks whose anchor is in `[0, visibleCells)`. * This is the common case (most blocks land fully visible). Blocks * whose stubs spill into bufferEnd are handled here: the anchor is * in visible, the sprite is sized to span `h * cellH`, and the mask * clips the off-screen tail. No occupancy entry is written for the * bufferEnd stubs because `_occupancy` is keyed by visible cells * only. consumers can't query a non-visible cell anyway. * 2. BufferAbove anchors. sizes blocks whose anchor sits above visible * but whose body extends into the visible window. This is the "tail * visible" partial-visibility case: a 1xH block whose top is clipped * by the reel mask, with only its bottom cells showing in the visible * window. Without this scan, the anchor sprite would stay at the * default 1x1 size and the block wouldn't render its visible portion * correctly. * * **No Scan 3 for bufferEnd-only anchors.** A block whose anchor is at * `cell >= visibleCells` would lie entirely off-screen (the strip ends at * `visibleCells + bufferEnd - 1` and `h >= 1`, so no visible cell is * covered). The cross-reel coordinator already accepts such anchors as a * legal-but-invisible placement; there's nothing to size and nothing for * the consumer-facing query API to return. If you ever add a scenario * where bufferEnd-only anchors need rendering, add Scan 3 here. * * For bufferStart anchors, `_occupancy[visibleCell].anchorCell` is set to * a NEGATIVE value. the offset from `bufferStart`. So * `this.symbols[this._bufferStart + anchorCell]` walks back to the anchor * regardless of which side it lives on. Consumers (`getSymbolFootprint`, * `getBlockBounds`) handle negative anchor cells by clipping bounds to * the visible portion of the block. */ private _finalizeFrame; } //# sourceMappingURL=Reel.d.ts.map