Let a host application place live contacts

createApplicationCatalog takes an optional placement provider. Its
layer(kind) hands Flights, Military Flights, AIS Vessels, Transit and Rail
a place(id, wallMs), and optionally observe(id, fix) and forget(id). While
live, each layer tells it every fix it accepts and draws a contact where it
places it: position, height (the layer's own when none is given), course
and speed. A null answer, a provider that throws, a kind it leaves out, or
no provider at all keeps the layer's own motion unchanged, and following
the world clock the layers never ask it. A transit vehicle or train it
holds still lets the render loop idle and is asked again each second;
vessels ask for frames only while it places one, and stop when the layer
turns off.

Tests show the layers draw and reconcile exactly as before without a
provider and with one that places nothing, draw where a provider places
them, and idle while it holds them still.
This commit is contained in:
Sameh Khamis committed 2026-11-14 15:07:25 -08:00
1 parent e53cfef1d1
commit 6f830a2e70
46 files changed
+1417 -34

No files matched your search

+19
View File
@@ -217,6 +217,25 @@ test('a jump forgets how contacts moved; clearing drops every contact', () => {
assert.ok(calls.includes('model:abc123'));
});
test('leaving the live edge forgets every contact a host placement was told of', () => {
const forgotten = [];
const flightState = {
records: records(),
_positionHistory: new Map([
['abc123', []],
['def456', []],
]),
_displayCourse: new Map(),
_groundSnap: { clear() {} },
_displayFloorState: new Map(),
};
const parts = { tracking: { _clearTrail() {} } };
const services = { placement: { forget: (id) => forgotten.push(id) } };
createClockFollow({ flightState, services, parts }).resetMotion();
assert.deepEqual(forgotten, ['abc123', 'def456']);
assert.equal(flightState._positionHistory.size, 0);
});
test('a snapshot at a time the source drops for a newer one sets no error', async () => {
const probe = ingestion({
async getSnapshot() {