Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...
    Index
    pickScene: Scene
    pickTarget: WebGLRenderTarget
    nodeMap: Map<number, PickNodeEntry> = ...
    nextPickId: number = 1
    _warnedPickIdCeiling: boolean = false
    raycaster: Raycaster
    ndcCoord: Vector2
    _lastReadX: number = 0
    _lastReadY: number = 0
    _savedClearColor: Color = ...
    _savedClearAlpha: number = 0
    _lensUV: { x: number; y: number } = ...
    _pickResolution: Vector2 = ...
    _dirty: boolean = true
    _drawBufSize: Vector2 = ...
    _pickSeq: number = 0
    _lastPickW: number = -1
    _lastPickH: number = -1
    _canvasRect: DOMRect | null = null
    scheduler: SettleScheduler
    _worldBoxCache: Map<number, Box3> = ...
    _readDst: Float32Array
    _readFlipped: Float32Array
    _votes: Map<number, VoteEntry> = ...
    _shouldPick: () => boolean = ...

    Optional predicate gating whether picks should fire. Defaults to always-true. App wires this to overlayManager visibility so we skip the pick entirely when no hover tooltip would display the result.

    _explicitPickSeq: number = 0

    Explicit picks ignore hover/view invalidation until their result handler finishes.

    _explicitPicksInFlight: number = 0
    _lastExplicitPickStartedAt: number = -Infinity
    postProcessing: PostProcessingManager | null = null

    Optional post-processing reference for lens distortion correction.

    renderer: Renderer
    capabilities: RendererCapabilities
    camera: Camera
    onPickResult: (result: PickResult | null) => Promise<void>
    _lastVisibleSig: number = -1

    Order-stable signature of the effectively-visible registered set at the last pick-buffer render. Visibility can flip WITHOUT any of the dirty-marking events firing (layers-panel toggles, embedder API) — the visibility gate would then serve a stale cached buffer (a re-shown layer would be unpickable until the next camera move or commit). performPick recomputes and compares before trusting the cache.

    • get pickGeneration(): number

      Monotonic generation counter for pick validity (issue #1917).

      Advances on EVERY event that can make an already-delivered PickResult no longer describe what is under the cursor: markDirty() (camera move via the controls' change, window resize, perspective ↔ ortho swap, a FOV edit via projection-changed, the layers-panel invalidator), onMouseMove(), onMouseLeave(), dispose(), and the start of each performPick. A consumer that caches a result alongside this value can tell, in O(1) and synchronously, whether the cache still describes reality — which is what makes click-to-act possible without a fresh GPU readback (and therefore without spending the browser's transient user activation on an await).

      Deliberately NOT advanced by suppress: pointerdown → controls start → suppress(true) is the FIRST half of an ordinary click, so treating it as invalidating would make every click refuse itself.

      Returns number

    • get visibleSignature(): number

      Order-stable signature of the effectively-visible registered set, right now (issue #1917).

      Companion to pickGeneration, and not redundant with it: hiding or showing a layer from the layers panel changes what is pickable WITHOUT dirtying the buffer — applyVisibility in ui/layers/layer-apply.ts deliberately only calls requestRender(). performPick already recomputes and compares this before trusting its cached buffer (see _lastVisibleSig); exposing it lets a cached-result consumer make the same check.

      Returns number

    • Allocate the next pick ID (incrementing counter, starts at 1). 0 = background.

      The counter is checked against MAX_PICK_NODE_ID: past 2^24 the f32 r channel of the pick buffer stops resolving consecutive ids, so the readback would silently name the wrong node — and the vote key (nodeId * VOTE_KEY_STRIDE) would leave the exact-integer range. Unreachable in practice (it needs 16.7M node registrations in one session), but the vote-key argument rests on this bound, so say so loudly rather than assume it.

      Returns number

    • Register a main scene node and its picking shadow node.

      Parameters

      • mainNode: Object3D
      • pickNode: Object3D
      • pickId: number

      Returns void

    • Invalidate cached world-space AABBs. Call after geometry or matrixWorld changes; camera-only motion does NOT need this and should use markDirty alone.

      Parameters

      • OptionalpickId: number

        Specific node to invalidate, or omit to drop all.

      Returns void

    • Set the predicate gating whether picks fire. Used by app.ts to skip picking when no hover overlay is visible (no consumer for the result).

      Parameters

      • predicate: () => boolean

      Returns void

    • Dispose the material(s) attached to a pick mesh. Handles the rare material: array case so a future custom-multi-material pick node doesn't leak shaders.

      Parameters

      • mesh: Mesh

      Returns void

    • Read-only snapshot of settle-scheduler timestamps and registration count. Exposed for E2E tests (the hover-tooltip spec polls lastPickFiredTime to verify a pick fired without reaching into private fields). Timestamps are performance.now() values; 0 means "never". Not part of the production API surface — treat as an observability hook, not an interaction point.

      Returns {
          lastPickFiredTime: number;
          lastMouseMoveTime: number;
          lastDirtyTime: number;
          registeredNodeCount: number;
          suppressed: boolean;
      }

    • Drop all node registrations without disposing the pick materials. Used after a WebGL context-loss event: the pick materials' shader programs are already invalid (the context they were compiled against is gone), and calling dispose() on them would throw on some drivers. The caller (NodeFactory.rebuildAfterContextRestore) is responsible for re-registering every scene node afterward, which produces fresh pick materials against the new context.

      Pick materials are registered with materialManager.register(...) at construction (see node-factory.ts). Without unregistering them here, repeated context-restore cycles accumulate stale references in the materialManager registry — camera-uniform updates would target dead materials and the getStats().totalRegistered count grows unboundedly. We unregister WITHOUT disposing (calls materialManager.unregister(material) not dispose(material)), matching the "no-dispose during context loss" contract for visible materials.

      Distinct from unregisterNode(id) which intentionally disposes the pick material when removing a single live node.

      Returns void

    • Update camera reference (e.g., after perspective ↔ orthographic swap).

      Routed through markDirty rather than setting _dirty directly: swapping the camera reprojects every element on screen, so besides re-rendering the pick buffer it must also advance the generation counter and fade the now-stale tooltip. Setting _dirty alone left an already-delivered PickResult looking valid — the same family of bug as an FOV edit (#1916) — so a click after an ortho toggle with a stationary cursor acted on whatever used to be under it (#1917).

      Parameters

      • camera: Camera

      Returns void

    • Invalidate the cached pick buffer. Call when camera, geometry, or viewport changes. Fades the current hover overlay (matches the drag-suppression UX: while the camera is moving, tooltips hide). While a recent explicit tap pick is in flight, keep its delivery authoritative and defer that fade for at most EXPLICIT_PICK_FADE_GUARD_MS; the tap describes the frame where the finger lifted, while a stalled handler eventually fades. The rAF scheduler will fire a fresh pick once everything has been still for HOVER_SETTLE_MS.

      Returns void

    • Invalidate ONLY the cached canvas rect. Call on page scroll / layout shifts that move the canvas without changing the 3D view: the rect (from getBoundingClientRect()) maps event.clientX/Y into canvas-local pick coordinates, so a stale rect after a scroll would offset every pick. Unlike markDirty this does NOT re-render the pick buffer or fade the tooltip — the view is unchanged, only the canvas's screen position moved. The next mousemove lazily recomputes the rect.

      We also drop any pending settle pick: its stored coordinate is already canvas-local (converted at mousemove time against the now-stale rect), so firing it after the scroll would pick the wrong spot. Cancelling lets the next mousemove re-arm with a fresh, correctly-mapped coordinate.

      Returns void

    • Suppress or resume picking (e.g. during orbit/pan/zoom interactions). true cancels any pending rAF; false re-enables the scheduler and re-arms it when there's a pending cursor position. The re-arm matters for "orbit-and-release without moving the mouse": the camera dirtied during the suppressed window, so once it settles for HOVER_SETTLE_MS the rAF tick fires the camera-settle re-pick naturally — no mouse wiggle required.

      Parameters

      • value: boolean

      Returns void

    • Handle mouse move. Records the cursor position, fades any visible tooltip, and arms the settle scheduler. Performs zero picking work directly — the actual pick fires from the rAF loop once both the mouse and the pick buffer have been still for HOVER_SETTLE_MS.

      Note: we do NOT early-return while suppressed (orbit/pan/zoom). The scheduler still tracks the latest cursor position during suppression (without scheduling a pick) so the re-pick on release uses where the cursor actually is, not a stale pre-orbit position.

      Parameters

      • event: MouseEvent

      Returns void

    • Pick NOW at a viewport position — the touch counterpart of the hover-settle path. A finger never hovers, so a tap has nothing to settle: it bypasses the scheduler (cancelling any pending settle so the same point is not picked twice) and runs the pick directly. Resolves after the result has been delivered through onPickResult AND that handler has finished (or the result was dropped as stale by the sequence guard), so the caller can read the picked-element cache immediately afterwards. The wait matters: the app's handler stores the cache only after an asynchronous label fetch, so resolving on delivery alone would hand a tap the cache as it was BEFORE its own pick landed. Honours the same setShouldPick gate as a hover pick: with no consumer there is nothing to pick for.

      Parameters

      • clientX: number
      • clientY: number

      Returns Promise<void>

    • Cursor left the canvas. Drop the pending position so the camera-settle re-pick path doesn't fire a stale pick when the cursor isn't even over the viewer, and cancel any pending rAF.

      Returns void

    • Perform a pick at the given screen coordinates.

      If the pick buffer is dirty (camera/geometry/resize changed), re-renders all effectively visible registered nodes to the cached buffer first. Otherwise just reads from the cached buffer — zero GPU cost on hover.

      Parameters

      • screenX: number
      • screenY: number
      • authoritative: boolean = false
      • explicitPickSeq: number = 0

      Returns Promise<void>

    • Hand a result to onPickResult, containing both synchronous throws and async rejections so a failing handler is logged rather than leaked.

      Returns a promise that settles once the handler has finished, so the awaited delivery in performPick — which is what lets pickAt resolve only after the picked-element cache is written — gets the same containment as the fire-and-forget fades.

      The Promise.resolve is load-bearing, not ceremony — it is the narrowed type's restatement of the truthiness guard the containment helper was written with. The handler type is not enforced at runtime, and on a callback that returns nothing .catch throws a TypeError that the catch below would misreport as a handler failure while silently dropping the wait that pickAt depends on.

      Parameters

      Returns Promise<void>

    • Read back the 5x5 pick buffer and perform brightness-weighted majority voting. Returns the winning PickResult or null if all pixels are background.

      Async readback (readRenderTargetPixelsAsync) works on both WebGLRenderer and WebGPURenderer in r185. The 1-frame latency on hover is documented in PICKING_DESIGN.md.

      Parameters

      • screenX: number
      • screenY: number

      Returns Promise<PickResult | null>