From 7cdaa6044b1c5c6781a501292e44a3823f1a709f Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 24 Oct 2026 23:36:30 -0700 Subject: [PATCH 1/3] perf(flights): cache unchanged detection label text --- src/layers/flights/queries.js | 15 +++++- src/layers/flights/queries.test.mjs | 82 +++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/layers/flights/queries.test.mjs diff --git a/src/layers/flights/queries.js b/src/layers/flights/queries.js index 371b3a854..5e72e50b4 100644 --- a/src/layers/flights/queries.js +++ b/src/layers/flights/queries.js @@ -491,8 +491,19 @@ export function createQueries({ object.position = pos; object.skipLabel = isTracked; // Card text only — declutter/cohort identity is `object.sourceId` (icao24). - const id = _contactLabel(icao24, info); - if (object.id !== id) object.id = id; + const callsign = info?.callsign; + const registration = info?.registration; + // Records and enrichment may change in place. Cache by field values, + // while keeping the live visual anchor above independent of text. + if ( + object.id === undefined || + object._labelCallsign !== callsign || + object._labelRegistration !== registration + ) { + object.id = _contactLabel(icao24, info); + object._labelCallsign = callsign; + object._labelRegistration = registration; + } const altitude = info?.altitude; if (object._altitude !== altitude) { object._altitude = altitude; diff --git a/src/layers/flights/queries.test.mjs b/src/layers/flights/queries.test.mjs new file mode 100644 index 000000000..a5f25c829 --- /dev/null +++ b/src/layers/flights/queries.test.mjs @@ -0,0 +1,82 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import * as Cesium from 'cesium'; +import { createQueries } from './queries.js'; +import { createRendering } from './rendering.js'; + +function fixture() { + const info = { + callsign: ' TEST123 ', + registration: ' N123 ', + altitude: 1000, + }; + const bb = { show: true, position: new Cesium.Cartesian3(1, 2, 3) }; + const flightState = { + feed: {}, + _billboardCollection: { show: true }, + _billboards: new Map([['abc123', bb]]), + _models: new Map(), + _detectionObjects: new Map(), + records: { data: new Map([['abc123', info]]) }, + }; + let formats = 0; + const services = { + aircraftPresentation: {}, + militaryRegistry: {}, + camera: {}, + focus: {}, + recession: {}, + labels: { + formatFlightLevel: (altitude) => { + formats++; + return String(altitude); + }, + }, + }; + const parts = { + testing: {}, + tracking: { _trackedModelRegimeActive: () => true }, + motion: { _trackedVisualCached: () => null }, + }; + parts.rendering = createRendering({ flightState, services, parts }); + const queries = createQueries({ flightState, services, parts }).methods; + return { flightState, info, bb, parts, queries, formats: () => formats }; +} + +test('candidate text follows in-place enrichment without freezing identity or positions', () => { + const f = fixture(); + const [first] = f.queries.getDetectableObjects(); + assert.equal(first.id, 'TEST123'); + assert.equal(first.sourceId, 'abc123'); + f.bb.position = new Cesium.Cartesian3(4, 5, 6); + assert.equal(f.queries.getDetectableObjects()[0], first); + assert.equal(first.position, f.bb.position); + assert.equal(f.formats(), 1); + f.info.callsign = ' NEW456 '; + assert.equal(f.queries.getDetectableObjects()[0].id, 'NEW456'); + f.info.callsign = ' '; + assert.equal(f.queries.getDetectableObjects()[0].id, 'N123'); + f.info.registration = ' N456 '; + f.info.altitude = 2000; + assert.equal(f.queries.getDetectableObjects()[0].id, 'N456'); + assert.equal(first.metric, '2000'); + assert.equal(f.formats(), 2); + f.flightState.records.data.set('abc123', { ...f.info }); + assert.equal(f.queries.getDetectableObjects()[0], first); + f.flightState.records.data.delete('abc123'); + assert.equal(f.queries.getDetectableObjects()[0].id, 'abc123'); + assert.equal(first.sourceId, 'abc123'); +}); + +test('unchanged candidate text is normalized once across paints', (t) => { + const f = fixture(); + const trim = String.prototype.trim; + let calls = 0; + t.mock.method(String.prototype, 'trim', function () { + if (String(this) === ' TEST123 ') calls++; + return trim.call(this); + }); + for (let i = 0; i < 20; i++) f.queries.getDetectableObjects(); + assert.equal(calls, 1); +}); + From 45a093fb42e94ed7ad56cc40017b4f33b2fb4d67 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 24 Oct 2026 23:36:30 -0700 Subject: [PATCH 2/3] perf(flights): reuse resolved models in detection queries --- docs/CURRENT-STATE.md | 5 +++ src/layers/flights/queries.js | 4 +- src/layers/flights/queries.test.mjs | 67 +++++++++++++++++++++++++++++ src/layers/flights/rendering.js | 7 ++- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 6f0f0bc85..3f94a456a 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -1,5 +1,10 @@ # God's Eye View Current State +Aircraft detection candidates reuse normalized display text until callsign or +registration changes, including in-place enrichment. Each candidate resolves its +fleet model once per collection. Positions, model ownership and tracked-aircraft +anchors remain live on every collection; contact admission is unchanged. + Civil aircraft share one delayed render timestamp per fleet update. Extrapolated fixes reuse their local coordinate frame until their coordinates change, including in-place ground-height corrections. Update cadence, contact counts, interpolation, diff --git a/src/layers/flights/queries.js b/src/layers/flights/queries.js index 5e72e50b4..89b91f125 100644 --- a/src/layers/flights/queries.js +++ b/src/layers/flights/queries.js @@ -452,8 +452,8 @@ export function createQueries({ const isTracked = icao24 === flightState._trackedIcao; // Keep planes rendered as a 3D model (billboard hidden) so the detection box // doesn't vanish on the 2D→3D handoff; bb.position stays current while hidden. - const model = flightState._models.get(icao24); - const modelOwnsVisual = parts.rendering._modelOwnsVisual(icao24); + const model = flightState._models.get(icao24) ?? null; + const modelOwnsVisual = parts.rendering._modelOwnsVisual(icao24, model); if (!isTracked && !bb.show && !modelOwnsVisual) continue; const info = flightState.records.data.get(icao24); let object = flightState._detectionObjects.get(icao24); diff --git a/src/layers/flights/queries.test.mjs b/src/layers/flights/queries.test.mjs index a5f25c829..13957d6cc 100644 --- a/src/layers/flights/queries.test.mjs +++ b/src/layers/flights/queries.test.mjs @@ -80,3 +80,70 @@ test('unchanged candidate text is normalized once across paints', (t) => { assert.equal(calls, 1); }); +test('candidate collection resolves models once and keeps handoffs live', (t) => { + const f = fixture(); + const get = t.mock.method(f.flightState._models, 'get'); + const [candidate] = f.queries.getDetectableObjects(); + assert.equal(get.mock.callCount(), 1); + const model = { + ready: false, + show: true, + modelMatrix: Cesium.Matrix4.fromTranslation( + new Cesium.Cartesian3(10, 20, 30), + ), + }; + f.parts.rendering._modelSpec = () => ({ + visualCenterNative: Cesium.Cartesian3.ZERO, + scale: 1, + }); + f.flightState._models.set('abc123', model); + f.bb.show = false; + assert.deepEqual(f.queries.getDetectableObjects(), []); + model.ready = true; + assert.equal(f.queries.getDetectableObjects()[0], candidate); + assert.deepEqual(candidate.position, new Cesium.Cartesian3(10, 20, 30)); + Cesium.Matrix4.setTranslation( + model.modelMatrix, + new Cesium.Cartesian3(40, 50, 60), + model.modelMatrix, + ); + f.queries.getDetectableObjects(); + assert.deepEqual(candidate.position, new Cesium.Cartesian3(40, 50, 60)); + model.show = false; + assert.deepEqual(f.queries.getDetectableObjects(), []); + f.bb.show = true; + assert.equal(f.queries.getDetectableObjects()[0].position, f.bb.position); + assert.equal(get.mock.callCount(), 6); + model.show = true; + assert.equal(f.parts.rendering._modelOwnsVisual('abc123'), true); + assert.equal(get.mock.callCount(), 7); +}); + +test('tracked ownership and live anchors remain independent of fleet models', () => { + const f = fixture(); + f.flightState._trackedIcao = 'abc123'; + f.flightState._trackedModel = { ready: true, show: true }; + f.parts.rendering._modelSpec = () => ({}); + let anchor = new Cesium.Cartesian3(10, 20, 30); + f.parts.motion._trackedVisualCached = () => anchor; + const [candidate] = f.queries.getDetectableObjects(); + assert.equal(candidate.position, anchor); + assert.equal(candidate.skipLabel, true); + assert.equal(f.parts.rendering._modelOwnsVisual('abc123', null), true); + anchor = new Cesium.Cartesian3(40, 50, 60); + assert.equal(f.queries.getDetectableObjects()[0].position, anchor); + for (const tracked of [ + null, + { ready: false, show: true }, + { ready: true, show: false }, + ]) { + f.flightState._trackedModel = tracked; + assert.equal( + f.parts.rendering._modelOwnsVisual('abc123', { ready: true, show: true }), + false, + ); + } + f.flightState._trackedModel = { ready: true, show: true }; + f.parts.tracking._trackedModelRegimeActive = () => false; + assert.equal(f.parts.rendering._modelOwnsVisual('abc123', null), false); +}); diff --git a/src/layers/flights/rendering.js b/src/layers/flights/rendering.js index d0074c9c9..8aff72e38 100644 --- a/src/layers/flights/rendering.js +++ b/src/layers/flights/rendering.js @@ -383,17 +383,20 @@ export function createRendering({ * null or still-loading tracked model from claiming a visual it is not * drawing yet. * @param {string} icao24 + * @param {Cesium.Model|null} [model] Resolved fleet model; null means absent. * @returns {boolean} */ - function _modelOwnsVisual(icao24) { + function _modelOwnsVisual(icao24, model) { if (icao24 === flightState._trackedIcao) { return ( parts.tracking._trackedModelRegimeActive() && _modelIsRendering(flightState._trackedModel) ); } - return _modelIsRendering(flightState._models.get(icao24)); + return _modelIsRendering( + model === undefined ? flightState._models.get(icao24) : model, + ); } /** A model is the visual only once it actually draws — loaded AND shown. From d53d68b7cfc6ab9727daeaf3af6cfd896dfa4bf4 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 24 Oct 2026 23:36:58 -0700 Subject: [PATCH 3/3] docs: record aircraft candidate processing improvement --- changelog.d/1379-aircraft-label-candidates.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/1379-aircraft-label-candidates.md diff --git a/changelog.d/1379-aircraft-label-candidates.md b/changelog.d/1379-aircraft-label-candidates.md new file mode 100644 index 000000000..f51ff101d --- /dev/null +++ b/changelog.d/1379-aircraft-label-candidates.md @@ -0,0 +1,4 @@ +- Reduce repeated work in aircraft detection candidates by caching unchanged + callsign/registration text and reusing resolved fleet models. Metadata updates, + live positions and tracked-aircraft/model handoffs remain immediate + (samehkhamis, #1379).