Skip to content

Line charts blank the newly exposed region while an M4 refine is in flight (T1 "never blank" is density-only) #352

Description

@FarhanAliRaza

Symptom

On a large line chart (reproduced on the 10M-point line_chart example, "10M
points — wheel to zoom into the spike"), zooming or panning to a region outside
the currently loaded window leaves the newly exposed part of the plot blank
for a few hundred milliseconds, then the line pops in.

Observed sequence: view at Feb 05 – Mar 12, then wheel to Jan 08 – Feb 19.
For the first frames the line is drawn only over roughly the right two-thirds
(starting ~Jan 22); the left third is empty. A moment later the full line
fills in and the chart is correct.

The end state is right — this is purely a transient hole during the refine.

Why this looks like a spec violation

spec/design/lod-architecture.md:355 states the transition law as universal:

T1 — never blank: a coarser covering representation draws until the finer
one arrives (density-under-points; broadest-cache fallback).

Both mechanisms named there are density-path machinery, and T1 does not appear
to be implemented for the decimated (M4 line) tier.

Code-level analysis

A line trace is served by the decimated tier: the kernel runs M4 decimation
over the current window [x0, x1) (src/lib.rs xy_m4_indices /
xy_m4_points, src/kernels.rs:2476). On a view change the client re-requests
that window via _scheduleViewRequest (js/src/54_kernel.ts:26).

Density has a covering fallback; decimated does not.

  • Density keeps a multi-window cache, g.densityCache
    (js/src/45_lod.ts:627), and lodDensityForView picks "the smallest cached
    window whose sample covers the whole view", falling back to the best partial
    overlay only when nothing covers it (js/src/45_lod.ts:303-311). There is
    also a retained home grid, g._homeDensity (js/src/54_kernel.ts:256).
  • The decimated tier has no equivalent. Every reference to it in the client
    is a "do I need to re-request?" gate — js/src/54_kernel.ts:38 and
    js/src/54_kernel.ts:501. No prior window is retained, and there is no home
    or overview M4 buffer to draw while the new one is in flight.

So when the view moves outside the single uploaded M4 buffer, there is nothing
covering to draw and the uncovered region renders empty until the kernel reply
lands — the observed hole.

Suggested direction

Give the decimated tier the covering-representation fallback T1 already
promises, e.g. retain the home/overview M4 buffer per decimated trace (and
optionally a small window cache mirroring densityCache) and draw the broadest
covering buffer while a refine is pending. The existing density machinery —
covering-window search plus the T8 discipline that a hold cannot outlive its
reply — is the obvious model.

Either way, per the repo's spec-as-source-of-truth rule, T1 and the
decimated tier should end up consistent: either the tier implements the law,
or lod-architecture.md records that T1 is density-only and says what a line
chart does instead.

Notes

  • Not a regression from Give every capture-owning gesture the same capture-loss policy #348 (pointer-capture loss). The wheel handler
    (js/src/53_interaction.ts:400) never acquires pointer capture, and that
    change does not touch data loading.
  • The symptom is user-reported from the Reflex example app; the causal chain
    above is from reading the source, not from an instrumented repro. Worth
    confirming with a trace of the pending M4 request before fixing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions