Preserve radio playback and imagery comparisons when switching planets

This commit is contained in:
German Khokhlov committed 2026-11-03 04:16:00 +04:00
1 parent b98cbeebf2
commit b01f1ee7dc
9 files changed
+246 -18

No files matched your search

+3 -3
View File
@@ -249,7 +249,7 @@ export function createApplicationCatalog({
['cctv', () => createApplicationCctv({ surface, source: sources.cctv })],
[
'radio',
() => createApplicationRadio({ surface, source: sources.radio }),
() => sessionModel('radio', () => createApplicationRadio({ surface, source: sources.radio })),
],
[
'transit',
@@ -266,9 +266,9 @@ export function createApplicationCatalog({
[
'recent-imagery',
() =>
createApplicationRecentImagery({
sessionModel('recent-imagery', () => createApplicationRecentImagery({
source: sources['recent-imagery'],
}),
})),
],
['ais-live-vessels', vessels],
['military-installations', installations],
+24 -4
View File
@@ -17,6 +17,15 @@ export function createLifecycle({
const methods = {
/** Initialize scene resources and the single audio element. */
init(viewer, options = {}) {
this.attachRenderer(viewer, options);
layerState._sessionGeneration += 1;
parts.clustering.resetRadioClusterOverlayIdentities();
parts.playback.installAudio();
clearOverlaySource(RADIO_OVERLAY_SOURCE_ID);
setOverlaySourceVisible(RADIO_OVERLAY_SOURCE_ID, false);
},
attachRenderer(viewer, options = {}) {
if (parts.renderer?.ready)
throw new Error('Radio layer is already initialized');
if (typeof rendererFactory !== 'function')
@@ -29,11 +38,22 @@ export function createLifecycle({
engine: options.engine,
});
parts.renderer.init(viewer, options);
layerState._sessionGeneration += 1;
parts.clustering.resetRadioClusterOverlayIdentities();
parts.playback.installAudio();
if (layerState._enabled) {
parts.rendering.drawStations(layerState._stations);
parts.interaction.syncRadioLifecyclePresentation();
}
},
detachRenderer() {
parts.interaction.removeInteraction();
clearTimeout(layerState._overlayPublishTimer);
layerState._overlayPublishTimer = null;
parts.renderer?.destroy();
parts.renderer = null;
layerState._renderById.clear();
layerState._selectedPosition = null;
layerState._lastHorizonCameraPosition = null;
clearOverlaySource(RADIO_OVERLAY_SOURCE_ID);
setOverlaySourceVisible(RADIO_OVERLAY_SOURCE_ID, false);
},
/** Show stations. Enabling or preset restoration never starts audio. */
+8 -2
View File
@@ -46,7 +46,7 @@ export function createRendering({
function publishRadioOverlayEntries() {
layerState._overlayPublishTimer = null;
if (!parts.interaction.radioPresentationAllowed()) {
if (!parts.renderer?.ready || !parts.interaction.radioPresentationAllowed()) {
parts.clustering.resetRadioClusterOverlayIdentities();
layerState._overlayDiagnostics =
parts.clustering.emptyRadioOverlayDiagnostics();
@@ -257,7 +257,12 @@ export function createRendering({
layerState._selectedId = null;
}
parts.renderer?.clearStations();
drawStations(stations);
}
function drawStations(stations) {
if (!parts.renderer?.ready) return;
parts.renderer.clearStations();
layerState._renderById.clear();
for (const station of stations) {
const position = markerPosition(station);
@@ -278,5 +283,6 @@ export function createRendering({
scheduleRadioOverlayPublish,
updateRenderVisibility,
reconcileStations,
drawStations,
};
}
+23 -4
View File
@@ -808,15 +808,34 @@ export function createRecentImageryLayer({
source: 'NASA GIBS · HLS + VIIRS',
updateInterval: 0,
init(viewer, { engine } = {}) {
init(viewer, options) {
return this.attachRenderer(viewer, options);
},
attachRenderer(viewer, { engine } = {}) {
_viewer = viewer || null;
_engine = engine || null;
if (rendererFactory && !renderer)
renderer = rendererFactory({ viewer, engine });
if (!renderer) throw new TypeError('Recent imagery requires a renderer');
if (_enabled) syncRender();
return true;
},
async detachRenderer() {
_unsubscribeMap?.();
_unsubscribeMap = null;
_mapStackController = null;
const lease = _lease;
forgetLease();
renderer?.destroy();
renderer = null;
_hostInfo = {collection: null, kind: 'none'};
_viewer = _engine = _tileset = null;
_keptGeneration = null;
await Promise.all([lease?.release(), _releasing]);
},
enable(viewer) {
if (_destroyed) return false;
if (viewer) _viewer = viewer;
@@ -836,7 +855,7 @@ export function createRecentImageryLayer({
applyPreview(_followKey);
// A pinned or previewed day that was still being probed drapes now.
const shown = plan();
const owned = renderer.getOwned?.() || {};
const owned = renderer?.getOwned?.() || {};
const drapesChanged = SLOT_IDS.some(
(slotId) => (owned[slotId]?.key || null) !== shown[slotId],
);
@@ -1225,7 +1244,7 @@ export function createRecentImageryLayer({
const next = clampUnit(value, _alpha);
if (next === _alpha) return;
_alpha = next;
for (const slotId of SLOT_IDS) renderer.setAlpha(slotId, next);
for (const slotId of SLOT_IDS) renderer?.setAlpha(slotId, next);
notify('alpha');
},
@@ -1253,7 +1272,7 @@ export function createRecentImageryLayer({
_swapped = false;
if (_alpha !== 1) {
_alpha = 1;
for (const slotId of SLOT_IDS) renderer.setAlpha(slotId, 1);
for (const slotId of SLOT_IDS) renderer?.setAlpha(slotId, 1);
}
_notice = msg('Box and images cleared');
syncRender();
+1 -4
View File
@@ -228,9 +228,7 @@ const FRAME_TARGETS = new Map([
/**
* The tools that still need the Cesium viewer or a subsystem built on it:
* the scene director, annotations and routes over them, the camera verbs
* (orbit, pan, tilt), Contacts (the context mode), Radio and
* CCTV. Without a viewer (the Navara engine) each answers a structured
* annotations and routes over them, camera verbs, the context mode and CCTV. Without a viewer (the Navara engine) each answers a structured
* refusal (UNAVAILABLE_ON_ENGINE) instead of running.
*/
export const VIEWER_ONLY_ACTIONS = Object.freeze([
@@ -239,7 +237,6 @@ export const VIEWER_ONLY_ACTIONS = Object.freeze([
'fly_route',
'move_camera',
'set_context_mode',
'control_radio',
'control_cctv',
]);
/** The refusal code of a viewer-only tool on an engine without a viewer. */
-1
View File
@@ -94,7 +94,6 @@ test('the viewer-only actions are one exported list of real tools', () => {
'fly_route',
'move_camera',
'set_context_mode',
'control_radio',
'control_cctv',
]);
assert.ok(Object.isFrozen(VIEWER_ONLY_ACTIONS));