mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
refactor(vessels): Cesium rendering behind a renderer interface
src/layers/vessels/render/cesium.js holds the Cesium objects the layer built itself, one for one: the translucent BillboardCollection of chevrons (centred, screen-aligned, never depth-tested), the horizon occluder, window projection, canvas size, viewer.trackedEntityChanged, and the trail (data/trailRenderer's entity polyline, kept on Cesium: the engine.lines Primitive path did not draw below the photoreal mesh in round 6 — the plan's T13/T15 blocker; flights does the same). The logic imports no Cesium: geo math, Rgba WHITE, the chevron artwork in icons.js, clicks through the engine's selection dispatcher (own-rule owner, same rule). Registered as CESIUM_LAYER_RENDERERS['ais-live-vessels']; src/layers/vessels joins the boundary rule. W1 replay vs7c7422e2: vessels/flights/military/localAdsb counts, known objects, screen points, requests and shot sha256 identical. qa-vessel-cards --data synthetic 2/2 and qa-vessel-datum (?qaFeed= vessels:fixture) 4/4, the same as6eec8f6c. Tests moved with the code (same meaning): cameraHandoff (the click body is handleClick), inputOwnership (the guard precedes the click rule's read of the pick), renderers.test (vessels in the extracted list), importDirections (an unmoved example layer is satellites), the ownership services fixture (renderer service).
This commit is contained in:
1 parent
6b27b21cc4
commit
1fe8267550
23 files changed
+810
-198
No files matched your search
@@ -117,11 +117,13 @@ export function createLifecycle({
|
||||
state.feed.firstConnectDeadline = null;
|
||||
state.feed.firstConnectTimer = null;
|
||||
state.feed.abort = null;
|
||||
state.billboardCollection = null;
|
||||
state.renderer?.destroy();
|
||||
state.renderer = null;
|
||||
state.records.all = [];
|
||||
state.records.byMmsi = new Map();
|
||||
state.records.unkeyed = [];
|
||||
state.clickHandler = null;
|
||||
state.selectionRemove = null;
|
||||
state.keyTarget = null;
|
||||
state.keydownHandler = null;
|
||||
state.trackedEntityRemover = null;
|
||||
@@ -141,7 +143,8 @@ export function createLifecycle({
|
||||
const methods = {
|
||||
/** Configure the source before initialization; an active layer keeps its owner. */
|
||||
setSource(source) {
|
||||
if (state.viewer)
|
||||
// Initialized: a viewer, or an engine's renderer without one.
|
||||
if (state.viewer || state.renderer?.engine)
|
||||
throw new Error('Configure the source before layer initialization');
|
||||
if (typeof source?.getSnapshot !== 'function')
|
||||
throw new TypeError('A snapshot source is required');
|
||||
@@ -149,17 +152,28 @@ export function createLifecycle({
|
||||
this.source = source.label || this.source;
|
||||
},
|
||||
|
||||
init(viewer) {
|
||||
/**
|
||||
* Creates the engine's vessel renderer (render/*.js), its sprite
|
||||
* collection, the click rule and the per-frame pass.
|
||||
* @param {object|null} viewer The Cesium viewer (null on an engine
|
||||
* without one).
|
||||
* @param {{ engine?: object }} [context] LayerLifecycle's context.
|
||||
*/
|
||||
init(viewer, { engine = null } = {}) {
|
||||
if (typeof vesselState._source?.getSnapshot !== 'function')
|
||||
throw new TypeError('A snapshot source is required');
|
||||
state.viewer = viewer;
|
||||
components.rendering.ensureCollections(viewer);
|
||||
state.renderer ??= createRenderer(viewer, engine);
|
||||
// The scene the layer draws in: the viewer, or the renderer's view on
|
||||
// an engine without one.
|
||||
const scene = viewer || state.renderer?.view || null;
|
||||
components.rendering.ensureCollections(scene);
|
||||
vesselState._vesselOverlayHost.setVisible(
|
||||
VESSEL_OVERLAY_SOURCE_ID,
|
||||
false,
|
||||
);
|
||||
components.selection.installInteraction(viewer);
|
||||
components.rendering.installRuntime(viewer);
|
||||
components.selection.installInteraction(scene);
|
||||
components.rendering.installRuntime(scene);
|
||||
restoreSpriteOrder(viewer);
|
||||
},
|
||||
|
||||
@@ -169,8 +183,11 @@ export function createLifecycle({
|
||||
if (!wasEnabled) beginAisSession();
|
||||
holdContinuousRender('ais-vessels'); // per-frame animator (perf wave 2)
|
||||
const activeViewer = viewer || state.viewer;
|
||||
components.rendering.ensureCollections(activeViewer);
|
||||
components.selection.installInteraction(activeViewer);
|
||||
// The scene the layer draws in: the viewer, or the renderer's view on
|
||||
// an engine without one.
|
||||
const scene = activeViewer || state.renderer?.view || null;
|
||||
components.rendering.ensureCollections(scene);
|
||||
components.selection.installInteraction(scene);
|
||||
components.rendering.setVisible(true);
|
||||
// Height-datum fix: warm the geoid grid once per layer-enable, never
|
||||
// blocking a poll. The first refresh may land pre-resolve (N = 0), and
|
||||
@@ -198,12 +215,20 @@ export function createLifecycle({
|
||||
restoreSpriteOrderOnEnable('ais', activeViewer);
|
||||
// Vessels are asked for by view, so ask again when the view moves away.
|
||||
removeViewListener();
|
||||
const moveEnd = activeViewer?.camera?.moveEnd;
|
||||
if (typeof moveEnd?.addEventListener === 'function')
|
||||
state.feed.removeViewListener = moveEnd.addEventListener(() => {
|
||||
components.ingestion.refreshIfMoved(activeViewer);
|
||||
});
|
||||
return components.ingestion.loadLivePositions(activeViewer);
|
||||
const onMoveEnd = () => {
|
||||
components.ingestion.refreshIfMoved(scene);
|
||||
};
|
||||
if (state.renderer)
|
||||
state.feed.removeViewListener = state.renderer.view.on(
|
||||
'moveEnd',
|
||||
onMoveEnd,
|
||||
);
|
||||
else {
|
||||
const moveEnd = activeViewer?.camera?.moveEnd;
|
||||
if (typeof moveEnd?.addEventListener === 'function')
|
||||
state.feed.removeViewListener = moveEnd.addEventListener(onMoveEnd);
|
||||
}
|
||||
return components.ingestion.loadLivePositions(scene);
|
||||
},
|
||||
|
||||
disable() {
|
||||
@@ -225,18 +250,15 @@ export function createLifecycle({
|
||||
state.feed.loadingLabel = '';
|
||||
},
|
||||
|
||||
destroy(viewer) {
|
||||
destroy() {
|
||||
removeViewListener();
|
||||
const activeViewer = viewer || state.viewer;
|
||||
invalidateAisSession();
|
||||
releaseContinuousRender('ais-vessels'); // direct-destroy path (perf wave 2 fix)
|
||||
if (state.feed.abort) state.feed.abort.abort();
|
||||
unregisterPickOwner('ais-live-vessels');
|
||||
components.selection.clearVesselInspection();
|
||||
components.tracking.destroySelectedVesselTrail();
|
||||
if (state.billboardCollection && activeViewer?.scene?.primitives) {
|
||||
activeViewer.scene.primitives.remove(state.billboardCollection);
|
||||
}
|
||||
state.renderer?.fleet.detach();
|
||||
vesselState._vesselOverlayHost.clearSource(VESSEL_OVERLAY_SOURCE_ID);
|
||||
vesselState._vesselOverlayHost.setVisible(
|
||||
VESSEL_OVERLAY_SOURCE_ID,
|
||||
@@ -250,6 +272,17 @@ export function createLifecycle({
|
||||
},
|
||||
};
|
||||
|
||||
/** The engine's vessel renderer (the Cesium one without an engine). */
|
||||
function createRenderer(viewer, engine) {
|
||||
return (
|
||||
services.renderer?.create({
|
||||
viewer,
|
||||
engine,
|
||||
createTrail: services.trails?.createTrail ?? null,
|
||||
}) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
function removeViewListener() {
|
||||
const remove = state.feed.removeViewListener;
|
||||
state.feed.removeViewListener = null;
|
||||
@@ -266,6 +299,7 @@ export function createLifecycle({
|
||||
isDefinitiveTransportFailure,
|
||||
markAisUnavailable,
|
||||
resetState,
|
||||
createRenderer,
|
||||
methods,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user