From 7cdaa6044b1c5c6781a501292e44a3823f1a709f Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 24 Oct 2026 23:36:30 -0700 Subject: [PATCH] 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); +}); +