Files
gods-eye-view/src/layers/satellites/lifecycle.js
T

184 lines
7.0 KiB
JavaScript

import * as Cesium from 'cesium';
import { ISS_OVERLAY_SOURCE_ID } from './policy.js';
export function createLifecycle({
state: layerState,
services,
parts,
source,
}) {
const { clearFocusTarget } = services.focus;
const { holdContinuousRender, releaseContinuousRender } = services.render;
const { registerPickOwner, unregisterPickOwner } = services.picking;
const methods = {
// Catalog data refresh; propagation remains preRender-owned.
async init(viewer) {
parts.catalog._abortActiveUpdates();
clearFocusTarget('satellites');
layerState._viewer = viewer;
layerState._catalog = new Map();
layerState._points = new Map();
layerState._detectionObjects = new Map();
layerState._orbitPaths = new Map();
layerState._count = 0;
layerState._lastUpdate = null;
layerState._coreStale = false;
layerState._denseStale = false;
layerState._trackedNorad = null;
parts.tracking._cancelPendingTrackingRestore();
layerState._trackedEntity = null;
layerState._trackedFrameNumber = -1;
layerState._trackedFrameGeo = null;
layerState._trackedFrameNowForTest = null;
layerState._lastFocusUpdate = 0;
layerState._activeFocusCount = 0;
layerState._enabled = false;
layerState._overlayHost.clearSource(ISS_OVERLAY_SOURCE_ID);
layerState._overlayHost.setVisible(ISS_OVERLAY_SOURCE_ID, false);
// Dense extras rebuild via update() when _params.catalog === 'dense'
// (the catalog-mode preference itself is sticky across init/destroy).
layerState._denseIds = [];
layerState._denseCursor = 0;
layerState._denseLoadPromise = null;
layerState._denseLoadToken++;
layerState._denseStatus = 'idle';
layerState._denseError = null;
layerState._catalogRevision++;
// Point primitives for satellite dots
layerState._pointCollection = new Cesium.PointPrimitiveCollection();
viewer.scene.primitives.add(layerState._pointCollection);
parts.interaction._installClickHandler(viewer);
// Pre-render listener for real-time position updates
// (fleet propagation + tracked per-frame dot + orbit ring GMST rotation)
layerState._preRenderListener = viewer.scene.preRender.addEventListener(
parts.rendering._preRenderTick,
);
console.log('[Data:Satellites] Initialized');
},
enable(viewer) {
layerState._enabled = true;
holdContinuousRender('satellites'); // per-frame animator (perf wave 2)
if (layerState._pointCollection)
layerState._pointCollection.show =
parts.controls.satelliteVisualsVisible(
layerState._enabled,
layerState._params.showPoints,
);
// Orbit ring primitives + persistent ISS host label — show them
for (const path of layerState._orbitPaths.values())
path.primitive.show = parts.controls.satelliteVisualsVisible(
layerState._enabled,
layerState._params.showOrbits,
);
parts.labels._syncIssOverlay();
// Re-attach input handlers and preRender propagation
parts.interaction._installClickHandler(viewer);
// Pick-ownership (H2): satellite dot ids are numeric NORAD catalog numbers;
// the registry hands predicates String()-coerced ids, so match via Number().
registerPickOwner('satellites', (pickedId) => {
const norad = Number(pickedId);
return Number.isFinite(norad) && layerState._points.has(norad);
});
if (!layerState._preRenderListener && viewer) {
layerState._preRenderListener = viewer.scene.preRender.addEventListener(
parts.rendering._preRenderTick,
);
}
parts.tracking._applyPendingTrackingRestore();
},
disable(viewer) {
parts.catalog._abortActiveUpdates();
parts.tracking._cancelPendingTrackingRestore();
layerState._enabled = false;
releaseContinuousRender('satellites');
if (layerState._pointCollection) layerState._pointCollection.show = false;
for (const path of layerState._orbitPaths.values())
path.primitive.show = false;
parts.tracking._clearTracking();
parts.labels._syncIssOverlay();
// Remove click handler + keydown listener + preRender propagation while disabled
if (layerState._clickHandler) {
layerState._clickHandler.destroy();
layerState._clickHandler = null;
}
if (layerState._trackedEntityChangedRemove) {
layerState._trackedEntityChangedRemove();
layerState._trackedEntityChangedRemove = null;
}
document.removeEventListener('keydown', parts.interaction._onKeyDown);
unregisterPickOwner('satellites');
if (layerState._preRenderListener) {
layerState._preRenderListener();
layerState._preRenderListener = null;
}
},
destroy(viewer) {
parts.catalog._abortActiveUpdates();
releaseContinuousRender('satellites'); // direct-destroy path (perf wave 2 fix)
layerState._enabled = false;
parts.tracking._clearTracking();
parts.tracking._cancelPendingTrackingRestore();
if (layerState._clickHandler) {
layerState._clickHandler.destroy();
layerState._clickHandler = null;
}
if (layerState._trackedEntityChangedRemove) {
layerState._trackedEntityChangedRemove();
layerState._trackedEntityChangedRemove = null;
}
document.removeEventListener('keydown', parts.interaction._onKeyDown);
unregisterPickOwner('satellites');
if (layerState._preRenderListener) {
layerState._preRenderListener();
layerState._preRenderListener = null;
}
layerState._overlayHost.clearSource(ISS_OVERLAY_SOURCE_ID);
layerState._overlayHost.setVisible(ISS_OVERLAY_SOURCE_ID, false);
if (layerState._pointCollection) {
viewer.scene.primitives.remove(layerState._pointCollection);
layerState._pointCollection = null;
}
// Orbit ring primitives are removed (and destroyed) here
for (const path of layerState._orbitPaths.values()) {
viewer.scene.primitives.remove(path.primitive);
}
layerState._points.clear();
layerState._detectionObjects.clear();
layerState._orbitPaths.clear();
layerState._catalog.clear();
layerState._denseIds = [];
layerState._denseCursor = 0;
layerState._denseLoadToken++;
layerState._denseStatus = 'idle';
layerState._denseError = null;
layerState._catalogRevision++;
layerState._rowControlsListener = null;
layerState._count = 0;
layerState._lastUpdate = null;
layerState._coreStale = false;
layerState._denseStale = false;
layerState._lastError = null;
layerState._lastFocusUpdate = 0;
layerState._activeFocusCount = 0;
layerState._trackingRefreshEpoch += 1;
layerState._lastTrackingRefreshOutcome = {
epoch: layerState._trackingRefreshEpoch,
status: 'destroyed',
failedGroups: [],
};
layerState._viewer = null;
},
};
return { methods };
}