mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-09 21:02:19 +00:00
Restore Wildlife privacy fills through engine.ground
Draw distinct privacy cells in one ground set with taxon-coloured fills at 25 percent opacity and two-pixel outlines at 80 percent opacity. Restore the original Cesium ground appearance on both engines, replacing the shared display's raised, depth-free outline. Ground classification handles terrain, tiles and horizon occlusion; picks pass through. Retain bounds-and-accent deduplication, unchanged-geometry reuse, selection, wide-view removal and detach cleanup. Check the fill and outline on fake and headless Cesium engines, and keep engine-switch ownership coverage. Update the Wildlife switch check to read ground cells. No coupling count fell or grew: all eleven metrics are unchanged, including 891 viewer lines and three external Cesium import files. Checked in the browser, keyless: the Wildlife engine switch draws the cell set on both engines; its keyboard step still meets the known Tab issue.
This commit is contained in:
1 parent
fbadf792d8
commit
db4bdba64f
4 files changed
+155
-150
No files matched your search
@@ -16,7 +16,7 @@ import {
|
||||
// Wildlife Sightings over Cesium → Navara → Cesium → Navara: on each engine
|
||||
// the four fixture sightings are badges in engine.movers at their positions
|
||||
// (each one picks as itself), the density glow is its own sprite set, and
|
||||
// the obscured record's privacy cell is an outline on engine.lines; a
|
||||
// the obscured record's privacy cell is a fill and outline on engine.ground; a
|
||||
// pointer click on a badge selects that sighting and opens its card.
|
||||
//
|
||||
// The first two switches are made from the keyboard with the card open (Tab
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
const LAYER = 'wildlife-sightings';
|
||||
const BADGES = 'wildlife-sightings';
|
||||
const GLOW = 'wildlife-sightings-density';
|
||||
const CELL_LINE = 'wildlife-sightings-cell:';
|
||||
const CELL_SET = 'wildlife-sightings-cells';
|
||||
// One density cell per iNaturalist observation (Acartia has no grid).
|
||||
const GLOW_CELLS = WILDLIFE_SIGHTINGS.filter((s) =>
|
||||
s.id.startsWith('inat:'),
|
||||
@@ -113,10 +113,10 @@ await strict.run({}, async (b) => {
|
||||
.filter((set) => set.id.startsWith('wildlife-sightings'))
|
||||
.map((set) => [set.id, { kind: set.kind, count: set.count }]),
|
||||
);
|
||||
const cells = engine.lines
|
||||
const cells = engine.ground
|
||||
.list()
|
||||
.filter((line) => line.id.startsWith(prefix))
|
||||
.map(({ points, shown }) => ({ points, shown }));
|
||||
.filter((set) => set.id === prefix)
|
||||
.map(({ kind, count, shown }) => ({ kind, count, shown }));
|
||||
const badges = ids.map((id) => {
|
||||
const position = window.qaWild.getSightingPosition(id);
|
||||
const screen =
|
||||
@@ -149,7 +149,7 @@ await strict.run({}, async (b) => {
|
||||
return { sets, cells, badges };
|
||||
},
|
||||
[...byId.keys()],
|
||||
CELL_LINE,
|
||||
CELL_SET,
|
||||
);
|
||||
// Every badge in engine.movers and picked as itself, the glow drawn and
|
||||
// the privacy cell outlined (the engines draw a few frames after the data).
|
||||
@@ -164,7 +164,8 @@ await strict.run({}, async (b) => {
|
||||
const ok =
|
||||
drawn.sets[BADGES]?.count === byId.size &&
|
||||
drawn.sets[GLOW]?.count === GLOW_CELLS &&
|
||||
drawn.cells.length === CELLS.length &&
|
||||
drawn.cells.length === 1 &&
|
||||
drawn.cells[0].count === CELLS.length &&
|
||||
drawn.badges.every(
|
||||
(badge) => badge.picked?.id === `wildlife:${badge.id}`,
|
||||
);
|
||||
@@ -417,9 +418,10 @@ await strict.run({}, async (b) => {
|
||||
await strict.check(
|
||||
p,
|
||||
LAYER,
|
||||
'the obscured record’s privacy cell is a shown outline on engine.lines',
|
||||
drawn.cells.length === CELLS.length &&
|
||||
drawn.cells.every((line) => line.shown && line.points === 33),
|
||||
'the obscured record’s privacy cell is a shown fill and outline on engine.ground',
|
||||
drawn.cells.length === 1 &&
|
||||
drawn.cells[0].count === CELLS.length &&
|
||||
drawn.cells.every((set) => set.shown && set.kind === 'cell'),
|
||||
drawn.cells,
|
||||
);
|
||||
console.log(
|
||||
|
||||
@@ -836,7 +836,22 @@ test('selection subscriptions retain omitted rows and clear on disable and destr
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
test('privacy cells draw outlines on the engine, replace changed batches and release them', async () => {
|
||||
function watchCells(engine) {
|
||||
let current;
|
||||
const createSet = engine.ground.createSet.bind(engine.ground);
|
||||
engine.ground.createSet = (options) => {
|
||||
const set = createSet(options);
|
||||
const { replace } = set;
|
||||
set.replace = (items) => {
|
||||
replace(items);
|
||||
current = { set, items };
|
||||
};
|
||||
return set;
|
||||
};
|
||||
return () => current;
|
||||
}
|
||||
|
||||
test('privacy cells draw on ground, replace changed batches and release them', async () => {
|
||||
let rows = [
|
||||
{
|
||||
...base,
|
||||
@@ -845,29 +860,27 @@ test('privacy cells draw outlines on the engine, replace changed batches and rel
|
||||
},
|
||||
];
|
||||
const h = harness({ getSnapshot: async () => rows });
|
||||
const cells = watchCells(h.engine);
|
||||
await h.update();
|
||||
const first = h.engine.lines.list();
|
||||
assert.equal(first.length, 1);
|
||||
assert.equal(h.frameWork(), 0, 'outlines need no frame work');
|
||||
const first = cells();
|
||||
assert.equal(h.engine.ground.list().length, 1);
|
||||
assert.equal(h.frameWork(), 0, 'static cells need no frame work');
|
||||
rows = [{ ...rows[0], iconic: 'Fungi' }];
|
||||
await h.update();
|
||||
const second = h.engine.lines.list();
|
||||
assert.equal(second.length, 1);
|
||||
assert.notEqual(second[0].id, first[0].id, 'a new accent replaces the line');
|
||||
assert.equal(h.engine.fake.line(first[0].id), undefined);
|
||||
const second = cells();
|
||||
assert.equal(h.engine.ground.list().length, 1);
|
||||
assert.notEqual(second.set, first.set, 'a new accent replaces the geometry');
|
||||
assert.notDeepEqual(second.items[0].fill, first.items[0].fill);
|
||||
assert.throws(() => first.set.commit(), /destroyed/);
|
||||
h.layer.selectSighting(base.stableId);
|
||||
assert.equal(h.focus.at(-1).kind, 'sighting-area');
|
||||
const selectedRing = h.badges()[0];
|
||||
assert.deepEqual(selectedRing.color, [1, 1, 1, 1]);
|
||||
assert.equal(selectedRing.scale, 1);
|
||||
assert.match(h.badgeSet().options.sprite.atlas.url, /^data:image\/svg/);
|
||||
assert.deepEqual(
|
||||
h.engine.lines.list(),
|
||||
second,
|
||||
'selection reuses unchanged privacy geometry',
|
||||
);
|
||||
assert.equal(cells(), second, 'selection reuses unchanged privacy geometry');
|
||||
h.layer.disable();
|
||||
assert.deepEqual(h.engine.lines.list(), []);
|
||||
assert.deepEqual(h.engine.ground.list(), []);
|
||||
h.layer.destroy();
|
||||
});
|
||||
|
||||
@@ -2185,6 +2198,7 @@ test('an engine switch keeps the data and selection and redraws the same badges
|
||||
fetchedAt: NOW,
|
||||
}),
|
||||
});
|
||||
const firstCells = watchCells(h.engine);
|
||||
await h.update();
|
||||
assert.equal(h.layer.selectSighting('inat:1'), true);
|
||||
const drawn = (engine) =>
|
||||
@@ -2221,12 +2235,13 @@ test('an engine switch keeps the data and selection and redraws the same badges
|
||||
const beforeStyle = style(first);
|
||||
const beforeGlow = glow(first);
|
||||
assert.equal(beforeGlow.count, 1);
|
||||
const outline = first.fake.line(first.lines.list()[0].id);
|
||||
const cell = firstCells();
|
||||
const stats = h.layer.getStats();
|
||||
|
||||
h.layer.detachRenderer();
|
||||
assert.deepEqual(first.movers.list(), [], 'nothing stays drawn');
|
||||
assert.deepEqual(first.lines.list(), []);
|
||||
assert.deepEqual(first.ground.list(), []);
|
||||
assert.equal(first.camera.moveEnd.numberOfListeners, 0);
|
||||
assert.equal(first.frames.preRender.numberOfListeners, 0);
|
||||
assert.equal(first.frames.postRender.numberOfListeners, 0);
|
||||
@@ -2236,14 +2251,17 @@ test('an engine switch keeps the data and selection and redraws the same badges
|
||||
|
||||
const other = ENGINE_KINDS.find((kind) => kind !== h.engine.kind);
|
||||
const next = h.globe(other);
|
||||
const nextCells = watchCells(next);
|
||||
assert.equal(h.layer.attachRenderer(null, { engine: next }), true);
|
||||
h.flushIdle();
|
||||
assert.deepEqual(drawn(next), before, 'the same badges, drawn the same');
|
||||
assert.deepEqual(style(next), beforeStyle);
|
||||
assert.deepEqual(glow(next), beforeGlow);
|
||||
const redrawn = next.fake.line(next.lines.list()[0].id);
|
||||
assert.deepEqual(redrawn.options.color, outline.options.color);
|
||||
assert.deepEqual([...redrawn.positions], [...outline.positions]);
|
||||
assert.deepEqual(
|
||||
nextCells().items,
|
||||
cell.items,
|
||||
'same draped cell colours and bounds',
|
||||
);
|
||||
assert.equal(requests, 1, 'the retained view needs no new request');
|
||||
assert.equal(h.layer.getStats().count, stats.count);
|
||||
assert.equal(
|
||||
|
||||
@@ -28,44 +28,9 @@ import {
|
||||
export const WILDLIFE_BADGE_SET_ID = 'wildlife-sightings';
|
||||
export const WILDLIFE_BADGE_ORDER =
|
||||
SPRITE_LAYER_ORDER.indexOf('wildlife-sightings');
|
||||
const CELL_LINE_PREFIX = 'wildlife-sightings-cell:';
|
||||
const CELL_LINE_WIDTH_PX = 2;
|
||||
// Points per privacy-cell edge, so long edges follow the surface.
|
||||
const CELL_EDGE_STEPS = 8;
|
||||
export const WILDLIFE_CELL_SET_ID = 'wildlife-sightings-cells';
|
||||
|
||||
/** The closed outline of a privacy cell at `height` metres, as flat ECEF. */
|
||||
export function wildlifeCellOutline({ south, west, north, east }, height = 0) {
|
||||
const corners = [
|
||||
[west, south],
|
||||
[east, south],
|
||||
[east, north],
|
||||
[west, north],
|
||||
[west, south],
|
||||
];
|
||||
const flat = new Float64Array((4 * CELL_EDGE_STEPS + 1) * 3);
|
||||
let at = 0;
|
||||
const scratch = geo.cartesian3.create();
|
||||
for (let edge = 0; edge < 4; edge++) {
|
||||
const [lon0, lat0] = corners[edge];
|
||||
const [lon1, lat1] = corners[edge + 1];
|
||||
for (let step = 0; step < CELL_EDGE_STEPS + (edge === 3 ? 1 : 0); step++) {
|
||||
const t = step / CELL_EDGE_STEPS;
|
||||
geo.cartesian3.fromDegrees(
|
||||
lon0 + (lon1 - lon0) * t,
|
||||
lat0 + (lat1 - lat0) * t,
|
||||
height,
|
||||
undefined,
|
||||
scratch,
|
||||
);
|
||||
flat[at++] = scratch.x;
|
||||
flat[at++] = scratch.y;
|
||||
flat[at++] = scratch.z;
|
||||
}
|
||||
}
|
||||
return flat;
|
||||
}
|
||||
|
||||
/** Own sighting badges, density glow and privacy-cell outlines on the engine. */
|
||||
/** Own sighting badges, density glow and draped privacy cells on the engine. */
|
||||
export function createRendering({ state, services, parts }) {
|
||||
const badgeSprites = services.badgeSprites ?? wildlifeSpriteCache();
|
||||
let batchDepth = 0;
|
||||
@@ -73,7 +38,6 @@ export function createRendering({ state, services, parts }) {
|
||||
let billboards = null;
|
||||
let density = null;
|
||||
let cellsSignature = null;
|
||||
let cellLineSerial = 0;
|
||||
let ambientAnchorIds = new Set();
|
||||
const badgeScreens = new Map();
|
||||
const crowdedBadgeIds = new Set();
|
||||
@@ -179,14 +143,6 @@ export function createRendering({ state, services, parts }) {
|
||||
point.show = show;
|
||||
changed = true;
|
||||
}
|
||||
// A depth-free outline shows through the globe; keep only front ones.
|
||||
for (const cell of state.cells) {
|
||||
const shown = !horizon || horizon.isPointVisible(cell.center) === true;
|
||||
if (cell.shown === shown) continue;
|
||||
cell.shown = shown;
|
||||
cell.line.show(shown);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) requestRender('cull');
|
||||
return changed;
|
||||
}
|
||||
@@ -326,13 +282,12 @@ export function createRendering({ state, services, parts }) {
|
||||
}
|
||||
|
||||
function clearCells() {
|
||||
for (const cell of state.cells) cell.line.destroy();
|
||||
for (const cell of state.cells) cell.destroy();
|
||||
state.cells = [];
|
||||
cellsSignature = null;
|
||||
}
|
||||
|
||||
// Privacy cells as outlines in their taxon accent. Outlines sit at the
|
||||
// cell's sampled ground height and draw through the scene, as the badges do.
|
||||
// Privacy cells drape their taxon fill and outline on terrain and tiles.
|
||||
function stageCells(rows) {
|
||||
const cellsVisible = state.band === 'cells' || state.band === 'cards';
|
||||
const unique = new Map();
|
||||
@@ -349,25 +304,22 @@ export function createRendering({ state, services, parts }) {
|
||||
clearCells();
|
||||
cellsSignature = signature;
|
||||
if (!unique.size) return true;
|
||||
const horizon = occluder();
|
||||
for (const row of unique.values()) {
|
||||
const { south, west, north, east } = row.cell;
|
||||
const lat = (south + north) / 2;
|
||||
const lon = (west + east) / 2;
|
||||
const floor = services.groundFloor?.cachedGroundFloor?.(lat, lon);
|
||||
const height = Number.isFinite(floor) ? floor : 0;
|
||||
const line = state.engine.lines.createLine({
|
||||
id: `${CELL_LINE_PREFIX}${++cellLineSerial}`,
|
||||
widthPx: CELL_LINE_WIDTH_PX,
|
||||
color: rgbaFromCss(wildlifeAccent(row.iconic)).withAlpha(0.8).rgba(),
|
||||
depthFailAlpha: 1,
|
||||
});
|
||||
line.setPositions(wildlifeCellOutline(row.cell, height));
|
||||
const center = geo.cartesian3.fromDegrees(lon, lat, height);
|
||||
const shown = !horizon || horizon.isPointVisible(center) === true;
|
||||
if (!shown) line.show(false);
|
||||
state.cells.push({ line, center, shown });
|
||||
}
|
||||
const set = state.engine.ground.createSet({
|
||||
id: WILDLIFE_CELL_SET_ID,
|
||||
kind: 'cell',
|
||||
});
|
||||
state.cells.push(set);
|
||||
set.replace(
|
||||
[...unique.values()].map((row) => {
|
||||
const tint = rgbaFromCss(wildlifeAccent(row.iconic));
|
||||
return {
|
||||
...row.cell,
|
||||
fill: tint.withAlpha(0.25).rgba(),
|
||||
outline: { color: tint.withAlpha(0.8).rgba(), widthPx: 2 },
|
||||
};
|
||||
}),
|
||||
);
|
||||
set.commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import * as geo from '../../geo/index.js';
|
||||
import {
|
||||
createFakeGroundHarness,
|
||||
createCesiumGroundHarness,
|
||||
} from '../../testSupport/groundDisplayEngines.mjs';
|
||||
import { createFakeEngine } from '../../globe/testing/fakeEngine.js';
|
||||
import {
|
||||
createRendering,
|
||||
WILDLIFE_BADGE_ORDER,
|
||||
WILDLIFE_BADGE_SET_ID,
|
||||
WILDLIFE_CELL_SET_ID,
|
||||
} from './rendering.js';
|
||||
import { WILDLIFE_DENSITY_SET_ID } from './density.js';
|
||||
import { wildlifeNearFarValue } from './display.js';
|
||||
@@ -33,9 +38,12 @@ const latOf = (position) =>
|
||||
(geo.cartographic.fromCartesian(position).latitude * 180) / Math.PI;
|
||||
|
||||
/** The real rendering on a fake engine above the fixture, 100 px a degree. */
|
||||
function harness(rows = [base], { project, ...extra } = {}) {
|
||||
const engine = createFakeEngine();
|
||||
engine.fake.setPose({
|
||||
function harness(rows = [base], { project, groundHarness, ...extra } = {}) {
|
||||
const engine = groundHarness?.engine ?? createFakeEngine();
|
||||
engine.camera.setView({
|
||||
destination: geo.cartesian3.fromDegrees(base.lon, base.lat, 30_000),
|
||||
});
|
||||
engine.fake?.setPose({
|
||||
longitude: degrees(base.lon),
|
||||
latitude: degrees(base.lat),
|
||||
height: 30_000,
|
||||
@@ -44,7 +52,7 @@ function harness(rows = [base], { project, ...extra } = {}) {
|
||||
roll: 0,
|
||||
});
|
||||
let projections = 0;
|
||||
engine.fake.script('worldToWindow', (position) => {
|
||||
engine.projector.worldToWindow = (position) => {
|
||||
projections++;
|
||||
return project
|
||||
? project(position)
|
||||
@@ -52,7 +60,7 @@ function harness(rows = [base], { project, ...extra } = {}) {
|
||||
x: 100 + (lonOf(position) - base.lon) * 100,
|
||||
y: 100 - (latOf(position) - base.lat) * 100,
|
||||
};
|
||||
});
|
||||
};
|
||||
let visible = true;
|
||||
engine.projector.horizonVisible = () => visible;
|
||||
const counts = { created: 0, removed: 0, cleared: 0, writes: [] };
|
||||
@@ -446,56 +454,81 @@ test('a selected observation remains rendered outside the viewport and observati
|
||||
h.rendering.destroy();
|
||||
});
|
||||
|
||||
test('privacy cells draw accent outlines through the scene and release them', () => {
|
||||
const cell = { south: 47.6, west: -122.4, north: 47.8, east: -122.2 };
|
||||
const h = harness([{ ...base, obscured: true, cell }], {
|
||||
groundFloor: { cachedGroundFloor: () => 250 },
|
||||
for (const make of [createFakeGroundHarness, createCesiumGroundHarness]) {
|
||||
test(`privacy cells drape accent fills and outlines and release them (${make.name})`, () => {
|
||||
const cell = { south: 47.6, west: -122.4, north: 47.8, east: -122.2 };
|
||||
const groundHarness = make();
|
||||
const h = harness(
|
||||
[
|
||||
{ ...base, obscured: true, cell },
|
||||
{ ...base, stableId: 'duplicate', obscured: true, cell },
|
||||
],
|
||||
{ groundHarness, groundFloor: { cachedGroundFloor: () => 250 } },
|
||||
);
|
||||
h.rendering.rebuild();
|
||||
const set = groundHarness.set(WILDLIFE_CELL_SET_ID);
|
||||
assert.equal(set.options.kind, 'cell');
|
||||
assert.equal(set.options.drape, 'both');
|
||||
assert.equal(set.options.pickable, false);
|
||||
assert.equal(set.items.length, 1, 'same bounds and taxon share a cell');
|
||||
const item = set.items[0];
|
||||
assert.deepEqual(
|
||||
{
|
||||
south: item.south,
|
||||
west: item.west,
|
||||
north: item.north,
|
||||
east: item.east,
|
||||
},
|
||||
cell,
|
||||
);
|
||||
const tint = ICONIC_TINTS.Aves;
|
||||
const rgb = [1, 3, 5].map((offset) =>
|
||||
parseInt(tint.slice(offset, offset + 2), 16),
|
||||
);
|
||||
assert.deepEqual(
|
||||
item.fill.map((v) => Math.round(v * 255)),
|
||||
[...rgb, Math.round(0.25 * 255)],
|
||||
);
|
||||
assert.deepEqual(
|
||||
item.outline.color.map((v) => Math.round(v * 255)),
|
||||
[...rgb, Math.round(0.8 * 255)],
|
||||
);
|
||||
assert.equal(item.outline.widthPx, 2);
|
||||
assert.equal(item.pickId, undefined, 'picks pass through the cell');
|
||||
assert.equal(set.shown, true);
|
||||
assert.deepEqual(h.engine.lines.list(), [], 'ground owns the outline');
|
||||
if (groundHarness.cesium) {
|
||||
const { fills, lines } = groundHarness.primitives(WILDLIFE_CELL_SET_ID);
|
||||
assert.equal(fills.length, 1);
|
||||
assert.equal(lines.length, 1);
|
||||
assert.equal(fills[0].options.allowPicking, false);
|
||||
assert.equal(lines[0].options.allowPicking, false);
|
||||
}
|
||||
h.rendering.rebuild();
|
||||
assert.equal(
|
||||
groundHarness.set(WILDLIFE_CELL_SET_ID).commits,
|
||||
1,
|
||||
'unchanged cells keep their geometry',
|
||||
);
|
||||
h.visible(false);
|
||||
h.rendering.cull();
|
||||
assert.equal(
|
||||
groundHarness.set(WILDLIFE_CELL_SET_ID).shown,
|
||||
true,
|
||||
'ground handles horizon occlusion',
|
||||
);
|
||||
h.visible(true);
|
||||
h.state.band = 'regional';
|
||||
h.rendering.rebuild();
|
||||
assert.deepEqual(h.engine.ground.list(), [], 'wide views draw no cells');
|
||||
h.state.band = 'cards';
|
||||
h.rendering.rebuild();
|
||||
assert.equal(h.engine.ground.list().length, 1);
|
||||
h.rendering.destroy();
|
||||
assert.deepEqual(h.engine.ground.list(), []);
|
||||
groundHarness.destroy();
|
||||
});
|
||||
h.rendering.rebuild();
|
||||
const lines = h.engine.lines.list();
|
||||
assert.equal(lines.length, 1);
|
||||
const line = h.engine.fake.line(lines[0].id);
|
||||
assert.equal(line.options.widthPx, 2);
|
||||
assert.equal(line.options.depthFailAlpha, 1);
|
||||
const tint = ICONIC_TINTS.Aves;
|
||||
assert.deepEqual(
|
||||
line.color.map((value) => Math.round(value * 255)),
|
||||
[
|
||||
parseInt(tint.slice(1, 3), 16),
|
||||
parseInt(tint.slice(3, 5), 16),
|
||||
parseInt(tint.slice(5, 7), 16),
|
||||
Math.round(0.8 * 255),
|
||||
],
|
||||
);
|
||||
const flat = line.positions;
|
||||
const first = geo.cartesian3.create(flat[0], flat[1], flat[2]);
|
||||
const last = geo.cartesian3.create(...flat.slice(-3));
|
||||
assert.ok(geo.cartesian3.distance(first, last) < 1e-6, 'a closed ring');
|
||||
assert.ok(Math.abs(lonOf(first) - cell.west) < 1e-9);
|
||||
assert.ok(Math.abs(latOf(first) - cell.south) < 1e-9);
|
||||
assert.ok(
|
||||
Math.abs(geo.cartographic.fromCartesian(first).height - 250) < 1e-3,
|
||||
'at the sampled ground height',
|
||||
);
|
||||
assert.equal(line.shown, true);
|
||||
// An unchanged cell set keeps its lines.
|
||||
h.rendering.rebuild();
|
||||
assert.deepEqual(h.engine.lines.list(), lines);
|
||||
h.visible(false);
|
||||
h.rendering.cull();
|
||||
assert.equal(line.shown, false, 'outlines behind the globe hide');
|
||||
h.visible(true);
|
||||
h.rendering.cull();
|
||||
assert.equal(line.shown, true);
|
||||
h.state.band = 'regional';
|
||||
h.rendering.rebuild();
|
||||
assert.deepEqual(h.engine.lines.list(), [], 'wide views draw no cells');
|
||||
h.state.band = 'cards';
|
||||
h.rendering.rebuild();
|
||||
assert.equal(h.engine.lines.list().length, 1);
|
||||
h.rendering.destroy();
|
||||
assert.deepEqual(h.engine.lines.list(), []);
|
||||
});
|
||||
}
|
||||
|
||||
test('font readiness swaps the atlas in place without badge recreation or position work', () => {
|
||||
const listeners = new Set();
|
||||
|
||||
Reference in new issue
Block a user