Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,69 @@ in the README).
to the internal engine object.

### Changed
- **No sampled points above the resolution of the graph (#225).** Interactive
`density_view` replies no longer ship a point-sample overlay: a fixed-size
sample above the drill budget reads as individual data points at a zoom
where real points are sub-pixel, misrepresenting the dataset — the
mean-color density surface stands alone there. The retained first-payload
sample (also the standalone re-bin worker's CPU source) now draws only when
the view's estimated in-view count fits the direct point budget, i.e. when
individual points are actually resolvable; real points still ship the
moment a window fits the budget, so drilldown behavior is unchanged.
- **Mean-color density surfaces composite like the points they aggregate
(LOD doc §2).** A channel-bearing cell's displayed alpha is now the
physical compositing of its own points — `1 − (1 − ā)^count` for mean
point alpha ā — instead of a per-window log-count tone curve. Lightness
no longer swings between windows or across the texture↔points boundary
(the aggregate is exactly as saturated as overplotted real marks), the
texture upload is normalization-free (no exposure re-uploads), and
mean-color drills swap at native opacity with no intensity handoff.
Count-only (constant-color) surfaces keep the log ramp — count is their
only structure. Same law in the client, the SVG/PNG exporters, and the
standalone re-bin worker.
- **The aggregate tier no longer refines; density requests only probe the
points band (LOD doc T13).** Whatever density texture already covers the
view stands — however blurry — until the estimated in-view count comes
within `LOD_POINTS_REQUEST_BAND ×` the direct budget; only then does a
`density_view` go out, and the kernel answers with exact points once the
count fits. The estimate takes the lower of an area-scaled cached-window
count and the retained first-payload sample counted in-view — the sample
follows the data's actual distribution, so sparse regions reach their
points without being stranded in blur by uniform-density assumptions.
Display-side, the aggregate sharpens in QUANTIZED ladder steps between
home and points (`LOD_AGG_STEP_FACTOR`/`LOD_AGG_STEP_MAX`: the view
snapped outward to a power-of-4 block grid over the extent, at most two
steps) — pan-stable, dedupable windows, so a zoom sees at most two
smooth-to-smooth texture swaps and worst-case softness is bounded at
~4× stretch per axis. A step reply is the only density reply that may
repaint a covered view; mid-band probe replies (the band's exact grids
have a speckled character that read as zoom-level jumping against the
smooth standing surface) land as facts-only cache entries for the gate.
Replies for uncovered views still apply, and standalone clients keep
applying everything. A 100M-scatter field capture had shipped a ~2.7 MB full-screen
grid on every pan/zoom step (including sub-pixel window twins, now deduped
within half an output texel) for what was the same aggregate with
marginally different blur; intermediate-zoom blur is the accepted,
recorded cost. Transition-band replies that do go out are clamped to the
pyramid's source resolution — never more cells than the finest level
resolves under the window. Kernel-attached clients also no longer draw the
retained first-payload sample at any zoom (resolvable views get real
points, not retained sample rows); it remains the standalone client's
fallback, gated by resolvability. A tried fine-over-broad texture layering
was reverted (recorded): density textures alpha-composite, so overlaps
double-count opacity, and per-window normalization makes the seam a
brightness step.
- **Full-point windows are padded, aligned, cached, and never re-requested
(LOD doc T13).** A points-tier reply now ships the largest aligned window
around the view whose exact count still fits the budget (bounds snapped to
a power-of-two grid over the trace's extent, per dimension), so consecutive
pans resolve to the same window; the client retires replaced exact windows
into a bounded per-trace cache and promotes them back — pan ping-pong and
zoom-out/zoom-in render entirely from the GPU with zero round-trips. Picks
against a promoted (older) window still resolve exactly through a bounded
kernel-side subset history. Identical `density_view` requests (same window,
same screen, unchanged data) are suppressed client-side, and a suppressed
duplicate's in-flight reply is accepted instead of dying to the seq race.
- **Density surfaces now wear the data's own colors (LOD doc §2).** A Tier-2
scatter's aggregated view colors each cell with the alpha-weighted **mean of
its binned points' resolved colors** (continuous colormap, categorical
Expand Down
21 changes: 19 additions & 2 deletions benchmarks/test_codspeed_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,26 @@ def drilldown_figure() -> Figure:
x = rng.uniform(0.0, 100.0, DRILL_N).astype(np.float64, copy=False)
y = rng.uniform(0.0, 100.0, DRILL_N).astype(np.float64, copy=False)
fig = xy.chart(xy.scatter(x=x, y=y, density=True)).figure()
fig._benchmark_deep_expected = int(np.count_nonzero((x < 10.0) & (y < 10.0)))

# Warm the lazily-built pyramid so CodSpeed tracks interactive viewport
# refresh cost, not one-time index construction.
fig.density_view(0, 0.0, 100.0, 0.0, 100.0, GRID_W, GRID_H)
fig.density_view(0, 0.0, 10.0, 0.0, 10.0, GRID_W, GRID_H)
deep, _ = fig.density_view(0, 0.0, 10.0, 0.0, 10.0, GRID_W, GRID_H)
fig.density_view(0, 0.0, 100.0, 0.0, 100.0, GRID_W, GRID_H)

# The drill ships the widest ALIGNED window around the view that still
# fits the budget (LOD doc T13), so the count oracle is the SHIPPED
# window's, taken from the warm-up reply — alignment is deterministic,
# every benchmark iteration resolves to the same window.
trace = deep["traces"][0]
assert trace["mode"] == "points"
(wx0, wx1), (wy0, wy1) = trace["x_range"], trace["y_range"]
assert wx0 <= 0.0 and wx1 >= 10.0 and wy0 <= 0.0 and wy1 >= 10.0
fig._benchmark_deep_window = (wx0, wx1, wy0, wy1)
fig._benchmark_deep_expected = int(
np.count_nonzero((x >= wx0) & (x <= wx1) & (y >= wy0) & (y <= wy1))
)
assert fig._benchmark_deep_expected == trace["visible"]
return fig


Expand Down Expand Up @@ -1068,6 +1081,10 @@ def _adaptive_drilldown_cycle(fig: Figure) -> int:
assert wide_trace["mode"] == "density"
assert deep_trace["mode"] == "points"
assert str(wide_trace.get("binning", "")).startswith("pyramid-L")
# T13: the shipped window is the deterministic padded aligned superset of
# the view, so both the window and its exact count repeat every cycle.
assert tuple(deep_trace["x_range"]) == fig._benchmark_deep_window[:2]
assert tuple(deep_trace["y_range"]) == fig._benchmark_deep_window[2:]
assert deep_trace["visible"] == fig._benchmark_deep_expected
drill_seq = deep_trace["drill_seq"]
row = fig.pick(0, 0, drill_seq)
Expand Down
4 changes: 2 additions & 2 deletions docs/core-concepts/large-data-and-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ readout and refinement.
| --- | --- | --- |
| Direct | Small visible traces | One rendered point or segment per retained row |
| M4-decimated | Long ordered lines and areas | A bounded sequence preserving per-bucket extrema |
| Density + sample | Dense scatter overviews | A fixed-resolution count grid plus a deterministic point sample |
| Refined view | A narrower pan/zoom window | A new viewport-specific aggregate or exact visible points when they fit |
| Density | Dense scatter overviews | A count grid wearing the data's own mean point colors. It stands across pans and zooms — softening as you go deeper — rather than re-aggregating per viewport; in standalone (kernel-less) charts a deterministic point sample overlays it once the view's estimated count would fit the direct budget |
| Refined view | A window near or below the direct budget | Exact visible points for a padded aligned window — nearby pans and zooms then render from the cached window with no further requests |

The current defaults begin M4 line decimation above 10,000 rows and automatic
scatter density above 200,000 points. Density grids default to 512×384 cells,
Expand Down
Loading
Loading