Files
gods-eye-view/src/layers/awareness/state.js
T
Manjunath a851199f5b fix(awareness): rest the range pulses unless the user moves the camera
Review follow-ups for the sonar pulses.

Wake on user input only: a camera move wakes the pulses only within
1.5 s of a pointer, wheel or touch on the Cesium canvas, where its
camera controller listens. Tracking, the cockpit view, an orbit or a
fly-to move the camera on every frame and no longer keep the pulses
running. Keys don't count: the camera controller reads none.

Below the disc: with the camera under 2.5 km the pulses rest
completely, with no disc, ring flash, echoes or frame requests.

Echo brightness: echoes scale by the contact bracket's drawn alpha,
which includes the Cyber sonar sweep, rounded to the nearest band with
no floor, so an echo is never brighter than its bracket.

Disc follow: the pulse disc follows the subject by a model-matrix
rotation about the Earth's centre and is rebuilt only every 20 km
instead of every 500 m.

Tests: a cap on burst length; teardown through disable(); the echo's
spread and alpha on the canvas; echoes under the Cyber sonar never
brighter than their bracket; waking by pointer, wheel, touch and drag
but not by keys, app camera moves or stale input; the rest below the
disc; and tighter asserts. CURRENT-STATE notes that terrain above
2.5 km still cuts through the disc and ring.
2026-11-03 22:50:32 +05:30

80 lines
2.6 KiB
JavaScript

import * as Cesium from 'cesium';
export function createState({ services }) {
const state = {};
state.DIRECTION_SCRATCH = Array.from(
{ length: 3 },
() => new Cesium.Cartesian3(),
);
state.SUBJECT_CARTOGRAPHIC_SCRATCH = new Cesium.Cartographic();
state.TARGET_CARTOGRAPHIC_SCRATCH = Array.from(
{ length: 3 },
() => new Cesium.Cartographic(),
);
Object.assign(state, {
viewer: null,
dataManager: null,
enabled: false,
// Set once a refresh observes the subject gone from a still-reporting source.
// The Contact readout turns this into its CONTACT LOST hold state.
subjectMissing: false,
// The dedicated Context chooser exposes a passive shell. Operational source
// layers activate only after Contacts is explicitly selected.
passive: true,
ownedDependencies: new Set(),
subject: null,
results: null,
visual: null,
/** Whether layer views replace the panel's initial markup yet. */
panelShown: false,
/** The published panel view, its signature and its subscribers. */
panelView: null,
panelSignature: '',
panelListeners: new Set(),
/** Result renders, published or not; tests observe repaint storms. */
panelRenders: 0,
subjectListener: null,
contextListener: null,
clearListener: null,
subjectClearListener: null,
runtimeListenersAttached: false,
preRenderRemover: null,
/** The published direction overlay, its signature and its subscribers. */
directionView: null,
directionSignature: '',
directionListeners: new Set(),
/** Each marker's last stable screen angle. */
directionAngles: [0, 0, 0],
navigationHistory: [],
navigationVisited: new Set(),
navigationIndex: -1,
suppressedHistoryKey: null,
pendingSelectionKey: null,
lastSubjectRefreshMs: 0,
/** Camera-pose signature observed on the previous rendered frame. */
lastCameraPoseSig: '',
/** When the pose signature last changed bins (hysteresis anchor). */
lastCameraPoseChangeMs: 0,
/** When the viewer last handled the camera (pointer, wheel, touch, key). */
lastCameraInputMs: Number.NEGATIVE_INFINITY,
/** Element the camera-input listeners are attached to. */
cameraInputTarget: null,
/** Whether the view currently counts as moving (hysteretic, not per-frame). */
cameraMoving: false,
lastEvaluatedPosition: null,
sourceRevision: '',
directionFrame: null,
lastDirectionUpdateMs: 0,
activationId: 0,
autoFocusAttempted: false,
autoFocusRetryPending: false,
pageTimer: null,
cohortPages: new Map(),
});
return state;
}