Stop a Bhote Koshi attachment that resumes after its scene was released

This commit is contained in:
German Khokhlov committed 2026-11-03 14:47:35 +04:00
1 parent 2853938df7
commit baabff6acf
2 files changed
+77 -5

No files matched your search

+18 -5
View File
@@ -1177,7 +1177,7 @@ export function createBhoteKoshiEventLayer({
return _event;
}
async function createImagery(event) {
async function createImagery(event, current = () => true) {
const rectangle = Cesium.Rectangle.fromDegrees(
event.imagery.rectangle.west,
event.imagery.rectangle.south,
@@ -1194,13 +1194,13 @@ export function createBhoteKoshiEventLayer({
credit: 'Vantor WorldView Open Data · CC BY-NC 4.0',
}),
]));
if (!_enabled) return;
if (!_enabled || !current()) return;
if (_native) {
await _native.loadImages(event.imagery.rectangle, [
beforeProvider,
afterProvider,
]);
setSplit(_split);
if (current()) setSplit(_split);
return;
}
_imageryLayerCollection = _viewer.imageryLayers;
@@ -3516,7 +3516,15 @@ export function createBhoteKoshiEventLayer({
_imageryLayerCollection = null;
}
async function attachRenderer(viewer, { engine = null } = {}) {
// Each attachment and release advances this. An attachment that resumes
// after a release, or after its scene signal aborted, stops right there;
// the release that follows it owns whatever it had already created.
let _attachGeneration = 0;
async function attachRenderer(viewer, { engine = null, signal = null } = {}) {
const generation = ++_attachGeneration;
const current = () =>
generation === _attachGeneration && signal?.aborted !== true;
_viewer = viewer;
_engine = engine;
if (!_viewer && _engine) _native = createNativeRenderer(engine);
@@ -3532,16 +3540,20 @@ export function createBhoteKoshiEventLayer({
: 'esri',
});
await _comparisonLease.ready;
if (!current()) return;
}
await createImagery(_event);
await createImagery(_event, current);
if (!current()) return;
if (_floodDataSource) {
if (_native) _nativeFlood = _native.draw(_floodDataSource);
else await _viewer.dataSources.add(_floodDataSource);
if (!current()) return;
}
registerDynamicCredit(engine ?? viewer, BHOTE_KOSHI_CREDIT);
_scenePathPositionCache.clear();
_lastCinematicProgress = null;
await syncTerrainComparison();
if (!current()) return;
createSplitControl(_event);
syncPresentationMode();
attachCameraInputRelease();
@@ -3551,6 +3563,7 @@ export function createBhoteKoshiEventLayer({
}
async function detachRenderer() {
++_attachGeneration;
detachCameraInputRelease();
detachEvidenceCardActivation();
_splitControl?.destroy();
+59
View File
@@ -2992,3 +2992,62 @@ test('Bhote Koshi corridor remains ordered downstream and inside the imagery rec
priorChainage = point.chainageM;
}
});
for (const stop of ['detach', 'scene-abort']) {
test(`Bhote Koshi attach resumed after a ${stop} leaves the next scene untouched`, async () => {
const event = JSON.parse(await readFile(eventUrl, 'utf8'));
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
installEventDom();
globalThis.window = { setTimeout: () => 1, clearTimeout() {} };
const first = eventViewer();
const second = eventViewer();
const layer = createBhoteKoshiEventLayer({
eventLoader: async () => event,
imageryProviderFactory: async (url) => ({ url }),
embeddedMediaFactory: () => ({ show() {}, hide() {}, pause() {}, destroy() {} }),
overlayHost: { setEntries() {}, setVisible() {}, clearSource() {} },
renderHost: { request() {}, hold() {}, release() {} },
});
const stack = (initial, gate = null) => {
let active = initial;
let generation = 1;
return {
getActiveId: () => active,
getSwitchGeneration: () => generation,
async setStack(id) {
await gate;
active = id;
generation += 1;
},
};
};
layer.attachMapStackController(stack('esri-imagery'));
const manager = new LayerLifecycle(first);
manager.register(layer);
try {
assert.equal(await manager.setEnabled(layer.id, true, { origin: 'local-restore' }), true);
await layer.detachRenderer();
// The incoming map has to switch first: the attachment waits on it.
let openGate;
const gate = new Promise((resolve) => { openGate = resolve; });
layer.attachMapStackController(stack('photoreal', gate));
const scene = new AbortController();
const attaching = layer.attachRenderer(second, { signal: scene.signal });
let detaching = null;
if (stop === 'detach') detaching = layer.detachRenderer();
else scene.abort();
openGate();
await attaching;
await detaching;
assert.deepEqual(second._test.imagery, []);
assert.deepEqual(second._test.dataSources, []);
if (stop === 'scene-abort') await layer.detachRenderer();
} finally {
await manager.destroyLayer(layer.id);
globalThis.document = originalDocument;
globalThis.window = originalWindow;
}
});
}