Take a hidden shape off the globe whole, and keep every drawn shape

Answers the review of bdbbc3d7 and the IC8 pass after it.

A shape hidden from Draw's list was only set to alpha 0, so the line's
flowing stroke, which keeps its own colour, stayed painted. Now:
- the world renderer's sync() sets every entity of a hidden mark to
  show = false, and FlowMaterialProperty follows the mark's alpha;
- the screen renderer does not project a hidden callout, so it is not drawn
  and cannot push a visible label aside;
- hiding acts at once, even before a mark's first frame, and with every
  mark hidden the annotations render hold is released after one last frame;
- fly_route skips a hidden route.

Hand-drawn shapes are never merged as duplicates: every one is drawn on
purpose and unnamed until its row names it, so a second pin dropped near
the first was dropped, or replaced the first when its colour differed. A
restyle the renderer cannot draw now also syncs the renderer.

Tests cover each: renderer show/hide and flow alpha, the screen skip, the
render hold and its last frame, a show with no fade-in, hidden routes, two
nearby pins, and the restyle rollback.
This commit is contained in:
Said Turksever committed 2026-11-02 16:43:39 +00:00
1 parent 4ebb287d36
commit 38eb98b8f8
8 files changed
+288 -6

No files matched your search

+19 -1
View File
@@ -5,6 +5,7 @@ import { greatCircleMeters } from '../geoDistance.js';
import {
holdContinuousRender,
releaseContinuousRender,
governorRequestRender,
} from '../renderGovernor.js';
import {
isRateLimitedOutcome,
@@ -201,8 +202,11 @@ export function createAnnotationEngine({
// World annotations carry persistent per-frame scene animation (pulsing
// live colors, camera-scaled rings, route-flow uniforms in the renderer),
// so the scene must render continuously while any mark exists. (perf wave 2)
// Only a mark someone can see needs every frame drawn: a board of marks
// hidden from Draw's list lets the scene idle.
function syncAnnotationHold() {
if (annotations.size > 0) holdContinuousRender('annotations');
const visible = [...annotations.values()].some((anno) => !anno.hidden);
if (visible) holdContinuousRender('annotations');
else releaseContinuousRender('annotations');
}
@@ -250,6 +254,9 @@ export function createAnnotationEngine({
if (changed) {
renderer.sync(annotations);
syncAnnotationHold();
// The frame that takes a just-hidden mark off: with every mark hidden
// the hold is gone, and nothing else would draw it.
governorRequestRender('annotations');
}
if (expired) notifyChange();
// Stop once the board is STABLE — persistent marks (persist=true is the default)
@@ -811,6 +818,11 @@ export function createAnnotationEngine({
* degenerate geocode stacks on one point (several Capitol monuments) would erase each other. */
function findDuplicate(anno) {
if (!anno.anchor) return null;
// A shape drawn by hand is always new: each click is meant, and every
// shape is unnamed until its row names it, so two nearby pins would
// otherwise read as one and the second would replace or vanish into the
// first.
if (anno.manual) return null;
const near = (a, b) =>
a &&
b &&
@@ -1017,6 +1029,11 @@ export function createAnnotationEngine({
anno.hidden = Boolean(hidden);
// Skip the fade-in, so showing it again is immediate.
anno.bornAt = Math.min(anno.bornAt, performance.now() - 260);
// Take it off (or put it back) now, not when its alpha next changes: a
// mark hidden before its first frame has no alpha change to wait for.
renderer.sync(annotations);
syncAnnotationHold();
governorRequestRender('annotations');
ensureTicking();
notifyChange();
return true;
@@ -1043,6 +1060,7 @@ export function createAnnotationEngine({
} catch (error) {
rollbackRendererState(anno);
annotations.delete(id);
renderer.sync(annotations);
syncAnnotationHold();
notifyChange();
throw error;