Artist Map v2 — Phase F (fix): robust live/buffer partition + 30fps loop
Fixes the genre-map 'renders small/sparse after the reveal, zoom fixes it' bug. Root cause: tighter islands (Phase D) raised the fit-zoom so nearly every bubble crossed the live-size threshold → the buffer excluded them all (thought they were live) but the live layer is capped, so only ~600 of 1800 drew until a zoom rebuilt the partition. Fix: _artMapRebuildBuffer now counts would-be-live bubbles; if more than the live layer can draw (>450), it sets _liveOverflow and bakes EVERYTHING into the buffer (full, correct render). The live layer + bob only take over once zoomed in enough that few bubbles qualify. So the overview is always complete, regardless of zoom. Trade-off: very large maps (genre 1800) render from the buffer (no per-bubble bob, slightly softer when deeply zoomed until the zoom-rebuild sharpens) — correctness over flourish on the crowd. Also: whole animation loop capped at ~30fps (reveal/ripple/bob all read fine at 30) to cut the churn on dense maps; a pending rebuild (dirty) always draws so the throttle can't skip the post-reveal bake. 64 JS integrity tests pass.
This commit is contained in:
parent
682bdc7fb1
commit
21b6d17d3c
1 changed files with 25 additions and 7 deletions
|
|
@ -5418,6 +5418,16 @@ function _artMapRebuildBuffer() {
|
|||
_artMap._liveBuildZoom = _artMap.zoom;
|
||||
_artMap._drawAlphaMul = 1; // buffer bakes at full alpha; the blit applies the reveal fade
|
||||
|
||||
// If more bubbles would be "live" than the live layer can draw, bake them ALL
|
||||
// into the buffer (set the overflow flag BEFORE the draw loop so the live-size
|
||||
// check below returns false and nothing is skipped). This is what prevents the
|
||||
// genre-overview "small/sparse" render: the live layer caps out, so let the
|
||||
// buffer own the whole crowd; live + bob only kick in once zoomed in.
|
||||
const bz = _artMap.zoom;
|
||||
let liveN = 0;
|
||||
for (const n of visible) { if (!n._isLabel && (n.radius || 0) * bz >= _artMap.LIVE_PX) liveN++; }
|
||||
_artMap._liveOverflow = liveN > 450;
|
||||
|
||||
octx.scale(scale, scale);
|
||||
octx.translate(-minX, -minY);
|
||||
|
||||
|
|
@ -5606,6 +5616,12 @@ function _artMapCompositeNode(n) {
|
|||
// so the two sets are always exact complements.
|
||||
function _artMapIsLiveSize(n) {
|
||||
if (n._isLabel) return false;
|
||||
// When too many bubbles would be "live" at once (e.g. the genre overview),
|
||||
// the live layer's cap can't draw them all and the buffer would exclude them
|
||||
// → a sparse/half-rendered map. In that case treat NOTHING as live so the
|
||||
// buffer bakes everything (full, correct render); the live layer + bob only
|
||||
// take over once you've zoomed in to where few bubbles are big.
|
||||
if (_artMap._liveOverflow) return false;
|
||||
const z = _artMap._liveBuildZoom || _artMap.zoom;
|
||||
return (n.radius || 0) * z >= _artMap.LIVE_PX;
|
||||
}
|
||||
|
|
@ -5695,11 +5711,12 @@ function _artMapStartLoop() {
|
|||
if (!a.running) return;
|
||||
_artMap._now = t;
|
||||
const more = _artMapStepAnimations(t);
|
||||
// Reveal/ripple → full 60fps. Idle buoyancy → ~30fps (the gentle bob
|
||||
// doesn't need 60, and halving the redraws keeps dense zoomed-in maps
|
||||
// smooth + cool).
|
||||
const ambientOnly = !more && _artMap._ambient;
|
||||
if (!ambientOnly || (t - (a._lastDraw || 0)) >= 32) {
|
||||
// Cap the whole animation loop at ~30fps. The reveal bloom, ripples and
|
||||
// ambient bob all read fine at 30, and halving the redraws keeps the
|
||||
// 1800-bubble genre map smooth instead of churning every frame. Always
|
||||
// honour a pending buffer rebuild (dirty) so the throttle can't skip the
|
||||
// frame that bakes the map after the reveal ends.
|
||||
if (_artMap.dirty || (t - (a._lastDraw || 0)) >= 31) {
|
||||
_artMapDraw(); // sets _artMap._liveCount
|
||||
a._lastDraw = t;
|
||||
}
|
||||
|
|
@ -6840,9 +6857,10 @@ function _artMapSetupInteraction(canvas) {
|
|||
_artMap.zoom = newZoom;
|
||||
_artMapRender(); // fast blit
|
||||
_artMapEnsureAmbient(); // resume buoyancy if we zoomed bubbles into view
|
||||
// Debounce hi-res rebuild after zoom settles
|
||||
// Debounce hi-res rebuild after zoom settles; then resume buoyancy (the
|
||||
// rebuild may have flipped the live/overflow partition).
|
||||
clearTimeout(_artMap._zoomRebuild);
|
||||
_artMap._zoomRebuild = setTimeout(() => { _artMap.dirty = true; _artMapRender(); }, 300);
|
||||
_artMap._zoomRebuild = setTimeout(() => { _artMap.dirty = true; _artMapRender(); _artMapEnsureAmbient(); }, 300);
|
||||
}, { passive: false });
|
||||
|
||||
let clickStart = null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue