ReadonlystatsDiagnostic counters. Mutable, read-only by convention. Reset on clearCache.
Pass-through — the memoiser has no state of its own that invalidates results, so callers should react to inner state changes.
Drop the cached entry and reset stats. The cache also self-invalidates on inner-strategy state changes via the epoch mechanism — call this only when an external event (e.g. a known-stale Scene mutation) needs to force a re-pick.
Tear down any held subscriptions or cached resources. Idempotent. Optional — most leaves don't need it.
Run one pick. Sync — even when the GPU backend is involved,
gl.readPixels blocks until the result is in. See
PickResult for what's populated.
Decorator strategy that memoises the most recent PickResult from an inner PickStrategy.
Single-entry cache, sized 1 — the value here is amortising the common stillness case (cursor parked on a vertex while the user decides where to click; multiple unrelated callers picking the same canvas point in the same frame). For multi-entry LRU or cross-frame batching we'd compose another decorator on top.
Invalidation
The cache key includes the inner strategy's PickStrategy.stateEpoch. When that bumps — e.g. RoutingPickStrategy sees the renderer attach or the WebGL context get lost — every prior cached result silently becomes a miss on the next call. No explicit subscription needed.
Skipped paths
Both bypass the cache and call the inner strategy directly. The
skippedcounter on stats surfaces how often that happens.Result identity
Cache hits return the cached PickResult by reference. The PickResult contract is "plain immutable struct" — if a caller mutates a returned result, subsequent cache hits will see the mutation. Don't.