mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-10 13:22:18 +00:00
Keep aircraft, installation and viewshed state off Cesium objects
The aircraft layers' model spec key, IR boost and belly offset and the billboards' large-icon flag move into aircraftVisuals, one store shared by flights, military and local ADS-B. Installation markers' last drawn height and selection move into installationMarkers. The CCTV viewshed QA tag is now the viewshedCameraId record field, which qa-cctv-v2 reads through entityRecords. Test fixtures drop a placement flag nothing reads.
This commit is contained in:
1 parent
42b17ffab6
commit
f86dcc9179
11 files changed
+84
-40
No files matched your search
@@ -1315,6 +1315,7 @@
|
||||
"src/layers/flights/policy.js",
|
||||
"src/layers/flights/queries.js",
|
||||
"src/layers/flights/rendering.js",
|
||||
"src/layers/aircraft/visualState.js",
|
||||
"src/layers/flights/state.js",
|
||||
"src/layers/flights/testing.js",
|
||||
"src/layers/flights/tracking.js",
|
||||
@@ -1374,6 +1375,7 @@
|
||||
"src/layers/military/policy.js",
|
||||
"src/layers/military/queries.js",
|
||||
"src/layers/military/rendering.js",
|
||||
"src/layers/aircraft/visualState.js",
|
||||
"src/layers/military/state.js",
|
||||
"src/layers/military/testing.js",
|
||||
"src/layers/military/tracking.js",
|
||||
@@ -1639,6 +1641,7 @@
|
||||
"src/layers/installations/model.js",
|
||||
"src/layers/installations/policy.js",
|
||||
"src/layers/installations/rendering.js",
|
||||
"src/layers/installations/markerState.js",
|
||||
"src/layers/installations/selection.js",
|
||||
"src/layers/installations/source.js",
|
||||
"src/layers/installations/state.js",
|
||||
@@ -1834,6 +1837,7 @@
|
||||
"src/layers/cctv/controls.js",
|
||||
"src/layers/cctv/frames.js",
|
||||
"src/layers/cctv/geometry.js",
|
||||
"src/data/entityRecords.js",
|
||||
"src/layers/cctv/geometryQueue.js",
|
||||
"src/layers/cctv/ground.js",
|
||||
"src/layers/cctv/headingConfidence.js",
|
||||
@@ -2529,6 +2533,7 @@
|
||||
"src/layers/flights/policy.js",
|
||||
"src/layers/flights/queries.js",
|
||||
"src/layers/flights/rendering.js",
|
||||
"src/layers/aircraft/visualState.js",
|
||||
"src/layers/flights/state.js",
|
||||
"src/layers/flights/testing.js",
|
||||
"src/layers/flights/tracking.js",
|
||||
@@ -2539,6 +2544,7 @@
|
||||
"src/layers/installations/model.js",
|
||||
"src/layers/installations/policy.js",
|
||||
"src/layers/installations/rendering.js",
|
||||
"src/layers/installations/markerState.js",
|
||||
"src/layers/installations/selection.js",
|
||||
"src/layers/installations/source.js",
|
||||
"src/layers/installations/state.js",
|
||||
@@ -3435,6 +3441,7 @@
|
||||
"src/layers/flights/policy.js",
|
||||
"src/layers/flights/queries.js",
|
||||
"src/layers/flights/rendering.js",
|
||||
"src/layers/aircraft/visualState.js",
|
||||
"src/layers/flights/state.js",
|
||||
"src/layers/flights/testing.js",
|
||||
"src/layers/flights/tracking.js",
|
||||
@@ -3445,6 +3452,7 @@
|
||||
"src/layers/installations/model.js",
|
||||
"src/layers/installations/policy.js",
|
||||
"src/layers/installations/rendering.js",
|
||||
"src/layers/installations/markerState.js",
|
||||
"src/layers/installations/selection.js",
|
||||
"src/layers/installations/source.js",
|
||||
"src/layers/installations/state.js",
|
||||
|
||||
@@ -1669,7 +1669,7 @@ async function main() {
|
||||
const prims = window.__godsEyeView.viewer.scene.primitives;
|
||||
let n = 0;
|
||||
for (let i = 0; i < prims.length; i++) {
|
||||
if (prims.get(i) && prims.get(i)._gevViewshed) n += 1;
|
||||
if (window.__godsEyeView.entityRecords.read(prims.get(i))?.viewshedCameraId) n += 1;
|
||||
}
|
||||
return n;
|
||||
});
|
||||
@@ -1729,7 +1729,7 @@ async function main() {
|
||||
const prims = window.__godsEyeView.viewer.scene.primitives;
|
||||
window.__qaViewshedRefs = [];
|
||||
for (let i = 0; i < prims.length; i++) {
|
||||
if (prims.get(i) && prims.get(i)._gevViewshed)
|
||||
if (window.__godsEyeView.entityRecords.read(prims.get(i))?.viewshedCameraId)
|
||||
window.__qaViewshedRefs.push(prims.get(i));
|
||||
}
|
||||
});
|
||||
@@ -1738,7 +1738,7 @@ async function main() {
|
||||
const prims = window.__godsEyeView.viewer.scene.primitives;
|
||||
const now = [];
|
||||
for (let i = 0; i < prims.length; i++) {
|
||||
if (prims.get(i) && prims.get(i)._gevViewshed) now.push(prims.get(i));
|
||||
if (window.__godsEyeView.entityRecords.read(prims.get(i))?.viewshedCameraId) now.push(prims.get(i));
|
||||
}
|
||||
const before = window.__qaViewshedRefs || [];
|
||||
const sameCount = now.length === before.length;
|
||||
|
||||
@@ -88,7 +88,7 @@ for (const layer of LAYERS) {
|
||||
// `_iconKind` is identity for every unconverted contact (see
|
||||
// tr3bRegistry.test.mjs) — it only swaps the glyph for a contact the
|
||||
// operator explicitly converted into a TR-3B.
|
||||
assert.match(source, /bb\.image\s*=\s*aircraftIcon\(\s*_iconKind\(\s*icao24,\s*meta\?\.klass,?\s*\)(,\s*bb\._gevIconLarge\s*\?\s*TRACKED_ICON_PX\s*:\s*undefined)?,?\s*\)/,
|
||||
assert.match(source, /bb\.image\s*=\s*aircraftIcon\(\s*_iconKind\(\s*icao24,\s*meta\?\.klass,?\s*\)(,\s*aircraftVisuals\.read\(bb\)\?\.iconLarge\s*\?\s*TRACKED_ICON_PX\s*:\s*undefined)?,?\s*\)/,
|
||||
'near contacts and model fallbacks retain the class-derived aircraft silhouette');
|
||||
assert.match(source, /bb\.rotation\s*=\s*0;/,
|
||||
'far dots are reset to a rotation-free presentation');
|
||||
|
||||
@@ -97,7 +97,7 @@ for (const fixture of LAYERS) {
|
||||
// The handoff state: the fleet tick hid the sprite and showed the model.
|
||||
fixture.seed({
|
||||
billboardShow: false,
|
||||
models: [[ICAO, { ready: true, show: true, _gevPlacementReady: true }]],
|
||||
models: [[ICAO, { ready: true, show: true }]],
|
||||
});
|
||||
assert.ok(
|
||||
nearbyIcaos(fixture.layer).includes(ICAO),
|
||||
@@ -136,7 +136,6 @@ for (const fixture of LAYERS) {
|
||||
ready: false,
|
||||
show: true,
|
||||
scale: 0,
|
||||
_gevPlacementReady: false,
|
||||
}]],
|
||||
});
|
||||
assert.ok(
|
||||
@@ -160,7 +159,6 @@ for (const fixture of LAYERS) {
|
||||
models: [[ICAO, {
|
||||
ready: true,
|
||||
show: true,
|
||||
_gevPlacementReady: true,
|
||||
modelMatrix,
|
||||
computedScale: COMPUTED_SCALE,
|
||||
}]],
|
||||
@@ -191,7 +189,6 @@ for (const fixture of LAYERS) {
|
||||
ready: false,
|
||||
show: true,
|
||||
scale: 0,
|
||||
_gevPlacementReady: false,
|
||||
modelMatrix: Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY),
|
||||
}]],
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createEntityStore } from '../../data/entityRecords.js';
|
||||
|
||||
/**
|
||||
* What the aircraft layers (flights, military, local ADS-B) remember about the
|
||||
* models and billboards they draw, kept beside them rather than on them.
|
||||
* - `specKey`: the model spec a model was loaded for; a class change reloads.
|
||||
* - `irBoost`: the IR boost a model was loaded with; a flip reloads.
|
||||
* - `bellyM`: metres from the model origin to its belly, to sit it on the ground.
|
||||
* - `iconLarge`: whether a billboard shows the large icon raster.
|
||||
*/
|
||||
export const aircraftVisuals = createEntityStore();
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { createFrustumVolumePrimitive } from '../../data/cctvViewshed.js';
|
||||
import { entityRecord } from '../../data/entityRecords.js';
|
||||
import {
|
||||
FRUSTUM_GROUND_CLEARANCE_M,
|
||||
PROBE_MIN_RANGE_M,
|
||||
@@ -564,8 +565,8 @@ export function createGeometry({ state: layerState, services, parts, source }) {
|
||||
record.frustumPositions,
|
||||
color,
|
||||
);
|
||||
// QA tag: the harness counts viewshed volumes by this marker.
|
||||
primitive._gevViewshed = record.camera.id;
|
||||
// QA tag: the harness counts viewshed volumes by this record field.
|
||||
entityRecord(primitive).viewshedCameraId = record.camera.id;
|
||||
record.viewshedPrimitive =
|
||||
layerState._viewer.scene.primitives.add(primitive);
|
||||
record.viewshedActiveTint = !!isActive;
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
COURSE_MAX_DPS,
|
||||
} from './policy.js';
|
||||
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
|
||||
import { aircraftVisuals } from '../aircraft/visualState.js';
|
||||
|
||||
export function createRendering({
|
||||
flightState,
|
||||
@@ -162,7 +163,7 @@ export function createRendering({
|
||||
const meta = flightState.records.data.get(icao24);
|
||||
bb.image = aircraftIcon(
|
||||
_iconKind(icao24, meta?.klass),
|
||||
bb._gevIconLarge ? TRACKED_ICON_PX : undefined,
|
||||
aircraftVisuals.read(bb)?.iconLarge ? TRACKED_ICON_PX : undefined,
|
||||
);
|
||||
bb.width = icao24 === flightState._trackedIcao ? 24 : 20;
|
||||
bb.height = icao24 === flightState._trackedIcao ? 24 : 20;
|
||||
@@ -432,7 +433,7 @@ export function createRendering({
|
||||
const key = _specKeyFor(flightState.records.data.get(icao24)?.klass);
|
||||
const current = flightState._models.get(icao24);
|
||||
if (
|
||||
(current && current._gevSpecKey !== key) ||
|
||||
(current && aircraftVisuals.read(current)?.specKey !== key) ||
|
||||
(!current && flightState._modelPending.has(icao24))
|
||||
) {
|
||||
const bb = flightState._billboards.get(icao24);
|
||||
@@ -442,7 +443,7 @@ export function createRendering({
|
||||
if (
|
||||
icao24 === flightState._trackedIcao &&
|
||||
flightState._trackedModel &&
|
||||
flightState._trackedModel._gevSpecKey !== key
|
||||
aircraftVisuals.read(flightState._trackedModel)?.specKey !== key
|
||||
) {
|
||||
_releaseTrackedModel();
|
||||
}
|
||||
@@ -465,7 +466,11 @@ export function createRendering({
|
||||
const batch = flightState._irReloadQueue.splice(0, IR_RELOAD_BATCH);
|
||||
for (const icao of batch) {
|
||||
const model = flightState._models.get(icao);
|
||||
if (!model || model._gevIrBoost === flightState._irBoost) continue; // already right state
|
||||
if (
|
||||
!model ||
|
||||
aircraftVisuals.read(model)?.irBoost === flightState._irBoost
|
||||
)
|
||||
continue; // already right state
|
||||
const bb = flightState._billboards.get(icao);
|
||||
if (bb && icao !== flightState._trackedIcao) bb.show = true;
|
||||
_releaseModel(icao);
|
||||
@@ -571,8 +576,9 @@ export function createRendering({
|
||||
// Keep the pick identity explicit on the resolved primitive. This also
|
||||
// protects injected/custom loaders that do not copy the creation option.
|
||||
model.id = icao24;
|
||||
model._gevSpecKey = specKey; // class-change sync compares against this
|
||||
model._gevIrBoost = loadIrBoost; // boost-flip reload queue compares against this
|
||||
const visual = aircraftVisuals.write(model);
|
||||
visual.specKey = specKey; // class-change sync compares against this
|
||||
visual.irBoost = loadIrBoost; // boost-flip reload queue compares against this
|
||||
// Admitted, not yet the visual. Cesium's default is show=true, which would let
|
||||
// an unplaced primitive claim ownership from the billboard for the frames
|
||||
// between admission and the next fleet tick (and draw at the identity matrix,
|
||||
@@ -764,7 +770,7 @@ export function createRendering({
|
||||
// Assign after resolution as well as in the creation options so the
|
||||
// standalone primitive always exposes the tracked aircraft pick id.
|
||||
m.id = flightState._trackedIcao;
|
||||
m._gevSpecKey = trackedKey; // class-change sync compares against this
|
||||
aircraftVisuals.write(m).specKey = trackedKey; // class-change sync compares against this
|
||||
m.show = false; // admitted, not yet the visual — the driver shows it once placed
|
||||
// Seed the world transform before the primitive enters the scene. A model
|
||||
// can become ready+shown between render phases; leaving Cesium's identity
|
||||
@@ -1075,9 +1081,10 @@ export function createRendering({
|
||||
(bb.scale || 1) *
|
||||
distanceScale *
|
||||
(globalThis.devicePixelRatio || 1);
|
||||
const wantLarge = bb._gevIconLarge ? glyphDevPx > 56 : glyphDevPx > 76;
|
||||
if (wantLarge !== !!bb._gevIconLarge) {
|
||||
bb._gevIconLarge = wantLarge;
|
||||
const visual = aircraftVisuals.write(bb);
|
||||
const wantLarge = visual.iconLarge ? glyphDevPx > 56 : glyphDevPx > 76;
|
||||
if (wantLarge !== !!visual.iconLarge) {
|
||||
visual.iconLarge = wantLarge;
|
||||
bb.image = aircraftIcon(
|
||||
_iconKind(icao24, info?.klass),
|
||||
wantLarge ? TRACKED_ICON_PX : undefined,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createEntityStore } from '../../data/entityRecords.js';
|
||||
|
||||
/**
|
||||
* What the installations layer last drew for a retained marker entity, so an
|
||||
* unchanged record leaves it alone: `heightM` (surface height) and `selected`.
|
||||
*/
|
||||
export const installationMarkers = createEntityStore();
|
||||
@@ -3,6 +3,7 @@ import * as Cesium from 'cesium';
|
||||
import { t } from '../../i18n/index.js';
|
||||
import { LAYER_ID, MAX_RENDERED, COLOR_BY_CLASS } from './policy.js';
|
||||
import { entityRecord } from '../../data/entityRecords.js';
|
||||
import { installationMarkers } from './markerState.js';
|
||||
|
||||
export function createRendering({
|
||||
state: layerState,
|
||||
@@ -219,13 +220,14 @@ export function createRendering({
|
||||
: null;
|
||||
if (retained) {
|
||||
// Same geometry: update the marker in place (selection, floor height).
|
||||
if (retained.gevInstallationHeightM !== surfaceHeightM) {
|
||||
retained.gevInstallationHeightM = surfaceHeightM;
|
||||
const marker = installationMarkers.write(retained);
|
||||
if (marker.heightM !== surfaceHeightM) {
|
||||
marker.heightM = surfaceHeightM;
|
||||
retained.position = displayPosition;
|
||||
entityRecord(retained).displayPosition = () => displayPosition;
|
||||
}
|
||||
if (retained.gevInstallationSelected !== selectedRecord) {
|
||||
retained.gevInstallationSelected = selectedRecord;
|
||||
if (marker.selected !== selectedRecord) {
|
||||
marker.selected = selectedRecord;
|
||||
retained.point.pixelSize = selectedRecord ? 13 : 9;
|
||||
retained.point.color = selectedRecord ? Cesium.Color.WHITE : color;
|
||||
}
|
||||
@@ -268,8 +270,10 @@ export function createRendering({
|
||||
outline.installationId = record.id;
|
||||
entityIds.push(outline.id);
|
||||
}
|
||||
entity.gevInstallationHeightM = surfaceHeightM;
|
||||
entity.gevInstallationSelected = selectedRecord;
|
||||
Object.assign(installationMarkers.write(entity), {
|
||||
heightM: surfaceHeightM,
|
||||
selected: selectedRecord,
|
||||
});
|
||||
entityRecord(entity).trackedId = `installations:${record.id}`;
|
||||
entityRecord(entity).displayPosition = () => displayPosition;
|
||||
entityRecord(entity).labelModel = {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
MODEL_PROX_KEEP_M,
|
||||
} from '../flights/policy.js';
|
||||
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
|
||||
import { aircraftVisuals } from '../aircraft/visualState.js';
|
||||
|
||||
const specKey = (spec) => `${spec.url}@${spec.scale}`;
|
||||
|
||||
@@ -78,7 +79,7 @@ export function createLocalAdsbModels({
|
||||
const spec = civilAircraftModelSpec(marker.klass);
|
||||
const key = specKey(spec);
|
||||
const current = models.get(id);
|
||||
if (current && current._gevSpecKey === key) return;
|
||||
if (current && aircraftVisuals.read(current)?.specKey === key) return;
|
||||
if (current) release(id);
|
||||
if (pending.has(id) || models.size + pending.size >= cap) return;
|
||||
const loadGeneration = generation;
|
||||
@@ -113,8 +114,9 @@ export function createLocalAdsbModels({
|
||||
return;
|
||||
}
|
||||
model.id = id;
|
||||
model._gevSpecKey = key;
|
||||
model._gevBellyM = spec.bellyM;
|
||||
const visual = aircraftVisuals.write(model);
|
||||
visual.specKey = key;
|
||||
visual.bellyM = spec.bellyM;
|
||||
// Admitted, not yet the visual: placement turns it on.
|
||||
model.show = false;
|
||||
collection.add(model);
|
||||
@@ -182,7 +184,7 @@ export function createLocalAdsbModels({
|
||||
return Cesium.Cartesian3.fromRadians(
|
||||
carto.longitude,
|
||||
carto.latitude,
|
||||
height + (model?._gevBellyM || 0),
|
||||
height + (aircraftVisuals.read(model)?.bellyM || 0),
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
scratchGround,
|
||||
);
|
||||
|
||||
@@ -54,6 +54,7 @@ import {
|
||||
COURSE_MAX_DPS,
|
||||
} from './policy.js';
|
||||
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
|
||||
import { aircraftVisuals } from '../aircraft/visualState.js';
|
||||
|
||||
export function createRendering({
|
||||
flightState,
|
||||
@@ -170,7 +171,7 @@ export function createRendering({
|
||||
const meta = flightState.records.data.get(icao24);
|
||||
bb.image = aircraftIcon(
|
||||
_iconKind(icao24, meta?.klass),
|
||||
bb._gevIconLarge ? TRACKED_ICON_PX : undefined,
|
||||
aircraftVisuals.read(bb)?.iconLarge ? TRACKED_ICON_PX : undefined,
|
||||
);
|
||||
bb.width = icao24 === flightState._trackedIcao ? 24 : 20;
|
||||
bb.height = icao24 === flightState._trackedIcao ? 24 : 20;
|
||||
@@ -400,7 +401,7 @@ export function createRendering({
|
||||
const key = _specKeyFor(flightState.records.data.get(icao24)?.klass);
|
||||
const current = flightState._models.get(icao24);
|
||||
if (
|
||||
(current && current._gevSpecKey !== key) ||
|
||||
(current && aircraftVisuals.read(current)?.specKey !== key) ||
|
||||
(!current && flightState._modelPending.has(icao24))
|
||||
) {
|
||||
const bb = flightState._billboards.get(icao24);
|
||||
@@ -410,7 +411,7 @@ export function createRendering({
|
||||
if (
|
||||
icao24 === flightState._trackedIcao &&
|
||||
flightState._trackedModel &&
|
||||
flightState._trackedModel._gevSpecKey !== key
|
||||
aircraftVisuals.read(flightState._trackedModel)?.specKey !== key
|
||||
) {
|
||||
_releaseTrackedModel();
|
||||
}
|
||||
@@ -433,7 +434,11 @@ export function createRendering({
|
||||
const batch = flightState._irReloadQueue.splice(0, IR_RELOAD_BATCH);
|
||||
for (const icao of batch) {
|
||||
const model = flightState._models.get(icao);
|
||||
if (!model || model._gevIrBoost === flightState._irBoost) continue; // already right state
|
||||
if (
|
||||
!model ||
|
||||
aircraftVisuals.read(model)?.irBoost === flightState._irBoost
|
||||
)
|
||||
continue; // already right state
|
||||
const bb = flightState._billboards.get(icao);
|
||||
if (bb && icao !== flightState._trackedIcao) bb.show = true;
|
||||
_releaseModel(icao);
|
||||
@@ -536,8 +541,9 @@ export function createRendering({
|
||||
// Keep the pick identity explicit on the resolved primitive. This also
|
||||
// protects injected/custom loaders that do not copy the creation option.
|
||||
model.id = icao24;
|
||||
model._gevSpecKey = specKey; // class-change sync compares against this
|
||||
model._gevIrBoost = loadIrBoost; // boost-flip reload queue compares against this
|
||||
const visual = aircraftVisuals.write(model);
|
||||
visual.specKey = specKey; // class-change sync compares against this
|
||||
visual.irBoost = loadIrBoost; // boost-flip reload queue compares against this
|
||||
// Admitted, not yet the visual. Cesium's default is show=true, which would let
|
||||
// an unplaced primitive claim ownership from the billboard for the frames
|
||||
// between admission and the next fleet tick (and draw at the identity matrix,
|
||||
@@ -725,7 +731,7 @@ export function createRendering({
|
||||
// Assign after resolution as well as in the creation options so the
|
||||
// standalone primitive always exposes the tracked aircraft pick id.
|
||||
m.id = flightState._trackedIcao;
|
||||
m._gevSpecKey = trackedKey; // class-change sync compares against this
|
||||
aircraftVisuals.write(m).specKey = trackedKey; // class-change sync compares against this
|
||||
m.show = false; // admitted, not yet the visual — the driver shows it once placed
|
||||
// Seed the world transform before the primitive enters the scene. A model
|
||||
// can become ready+shown between render phases; leaving Cesium's identity
|
||||
@@ -1005,9 +1011,10 @@ export function createRendering({
|
||||
(bb.scale || 1) *
|
||||
distanceScale *
|
||||
(globalThis.devicePixelRatio || 1);
|
||||
const wantLarge = bb._gevIconLarge ? glyphDevPx > 56 : glyphDevPx > 76;
|
||||
if (wantLarge !== !!bb._gevIconLarge) {
|
||||
bb._gevIconLarge = wantLarge;
|
||||
const visual = aircraftVisuals.write(bb);
|
||||
const wantLarge = visual.iconLarge ? glyphDevPx > 56 : glyphDevPx > 76;
|
||||
if (wantLarge !== !!visual.iconLarge) {
|
||||
visual.iconLarge = wantLarge;
|
||||
bb.image = aircraftIcon(
|
||||
_iconKind(icao24, flightState.records.data.get(icao24)?.klass),
|
||||
wantLarge ? TRACKED_ICON_PX : undefined,
|
||||
|
||||
Reference in new issue
Block a user