// src/data/cctv.test.mjs — CCTV v2 pure frustum geometry (computeFrustumGeometry). // // Locks the §2a math of docs/plans/2026-07-03-cctv-v2-design.md: // - the far-cap (monitor plane) corners lie ON the plane through capCenter // perpendicular to the frustum view axis (ε < 0.5 m) — this is the geometric // invariant that welds the wireframe corner rays to the plane entity; // - vFov = 2·atan(tan(hFov/2) / (16/9)) (same 16:9 derivation the projection // frame used); // - far-cap center + corners clamp to ≥ groundAlt + 2 m so a fabricated pitch // (Austin's -24°) never buries the plane in the tiles (§6 risk); // - the activation obstruction probe's range clamp (§9.1) shortens the // effective range, never lengthens it. // // computeFrustumGeometry is PURE (no viewer, no scene queries) so it runs under // plain node:test. import { test } from 'node:test'; // Preserve structural pins across component qualification and formatter wrapping. function componentFunctionSource(fn) { return fn .toString() .replace(/\blayerState\./g, '') .replace(/\bparts\.\w+\./g, '') .replace(/,\s*(?=\))/g, '') .replace(/\s+/g, ' ') .replace(/\(\s+/g, '('); } import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import * as Cesium from 'cesium'; import cctvLayer, { CCTV_PROJECTION_OVERLAY_SOURCE_OPTIONS, _createCctvProjectionPlaneForTest, _extractPickedCameraIdForTest, _updateCctvProjectionPlaneForTest, _setCctvCoverageStateForTest, _pushAmbientCardEntriesForTest, _setCctvOverlayHostForTest, activationProbeClampRange, bindCctvWorldClickGesture, clearProbeClampOnDeactivation, computeFrustumGeometry, cctvCycleIndex, cctvEmptyClickDeselects, cctvRecordNeedsActivation, deactivateActiveCamera, CCTV_FOCUS_RESULT, FRUSTUM_GROUND_CLEARANCE_M, CCTV_CALIBRATION_STORAGE_KEY_V2, CCTV_CALIBRATION_STORAGE_KEY_V1, readCalibrationStoreV2, writeCalibrationStoreV2, deriveCalBadge, surfaceRegimeKey, calibrationPatchMovesAnchor, migrateRangeScaleForFloor, cctvGeometryDrainPacing, createGeometryProgressNotifier, normalizeCoverageMode, frameSignatureFromPixels, focusCctvRecord, hideCctvRecordVisuals, materializeCctvActiveCoverageEntities, materializeCctvVisibleCoverageEntities, maybeAutoHop, prioritizeActiveCctvGeometryRecord, processCctvGeometryDrainBatch, processCctvGeometryQueueBatch, processGeometryBatch, refreshCoverageStyles, setCctvCardPresentationOptions, setActiveCamera, } from './cctv.js'; import { CCTV_ACTIVATION_RESULT, CCTV_FOCUS_REQUEST_EVENT, activateCctvCameraFromWorldClick, } from '../cctvFocusRequest.js'; import { ACTIVE_CAMERA_COLOR, IDLE_CAMERA_COLOR, LIVE_CAMERA_COLOR, } from '../layers/cctv/policy.js'; const CCTV_PRESENTATION_SOURCE = fs.readFileSync( path.join( path.dirname(fileURLToPath(import.meta.url)), '..', 'ui', 'cctvPresentation.js', ), 'utf8', ); const ASPECT = 16 / 9; const toRad = (deg) => (deg * Math.PI) / 180; const GESTURE_TYPES = { LEFT_DOWN: 'left-down', MOUSE_MOVE: 'mouse-move', LEFT_UP: 'left-up', LEFT_CLICK: 'left-click', }; function makeGestureHandler() { const actions = new Map(); return { setInputAction(callback, type) { actions.set(type, callback); }, fire(type, event) { actions.get(type)?.(event); }, }; } // Same spherical earth radius projectPoint uses (R = 6371 km) so the // lat/lon→metres conversion in the assertions matches the module's math. const M_PER_DEG = (Math.PI / 180) * 6371000; /** Local ENU metres of point b relative to point a ({lat, lon, alt}). */ function enu(a, b) { return { e: (b.lon - a.lon) * M_PER_DEG * Math.cos(toRad(a.lat)), n: (b.lat - a.lat) * M_PER_DEG, u: b.alt - a.alt, }; } function dot(v, w) { return v.e * w.e + v.n * w.n + v.u * w.u; } function sub(v, w) { return { e: v.e - w.e, n: v.n - w.n, u: v.u - w.u }; } function mag(v) { return Math.hypot(v.e, v.n, v.u); } /** Unit view-axis direction for a heading/pitch pose, in ENU. */ function viewDir(headingDeg, pitchDeg) { const h = toRad(headingDeg); const p = toRad(pitchDeg); return { e: Math.sin(h) * Math.cos(p), n: Math.cos(h) * Math.cos(p), u: Math.sin(p), }; } // A pose whose far cap sits well above ground (no clamping) so the raw plane // math is observable: tall mount, shallow pitch. const UNCLAMPED_CAMERA = { lat: 30.2672, lon: -97.7431, headingDeg: 41, pitchDeg: -5, fovDeg: 56, rangeM: 210, mountHeightM: 100, }; const UNCLAMPED_GROUND = 0; // Austin's fabricated prior personality (design §1a): pitch -24° at 210 m puts // the unclamped cap ~85 m below the mount — underground vs groundAlt 150. const AUSTIN_FABRICATED_CAMERA = { lat: 30.2672, lon: -97.7431, headingDeg: 41, pitchDeg: -24, fovDeg: 56, rangeM: 210, mountHeightM: 10, }; const AUSTIN_GROUND = 150; test('geometry drain coalesces 40 progress ticks and always publishes the final state', () => { let nowMs = 0; const state = { loaded: 0, total: 40, loading: true }; const coalesced = []; const unthrottled = []; let progressInvocations = 0; const notifier = createGeometryProgressNotifier( () => coalesced.push({ ...state }), { now: () => nowMs, intervalMs: 300, batchLimit: 10 }, ); const queue = Array.from({ length: state.total }, (_, index) => index + 1); while (queue.length) { processCctvGeometryQueueBatch({ queue, batchSize: 1, visit: (loaded) => { state.loaded = loaded; nowMs += 35; unthrottled.push({ ...state }); }, progress: () => notifier.progress(), complete: () => { progressInvocations = coalesced.length; state.loading = false; notifier.finish(); }, }); } assert.ok( progressInvocations >= 4 && progressInvocations <= 6, `expected about 4-6 progress callbacks, got ${progressInvocations}`, ); assert.notEqual(progressInvocations, 40); assert.equal( coalesced.length, progressInvocations + 1, 'queue completion must add one unconditional final notification', ); assert.deepEqual(coalesced.at(-1), { ...unthrottled.at(-1), loading: false }); const productionDrain = processGeometryBatch.toString(); assert.match(productionDrain, /processCctvGeometryDrainBatch/); assert.match(productionDrain, /_geoProgressNotifier\?\.progress\(\)/); assert.match(productionDrain, /_geoProgressNotifier\?\.finish\(\)/); assert.doesNotMatch( productionDrain, /if \(_geoLoading\) notifyListeners\(\)/, ); }); test('lazy coverage inserts nothing at catalog init, then materializes only eligible records', () => { const records = Array.from({ length: 100 }, (_, index) => ({ camera: { id: `cam-${index}` }, coverageEntities: [], })); let insertCount = 0; const build = (record) => Array.from({ length: 5 }, (_, entityIndex) => { insertCount += 1; return { id: `${record.camera.id}-${entityIndex}`, show: false }; }); assert.equal( insertCount, 0, 'building a 100-camera catalog must insert zero coverage entities', ); assert.doesNotMatch( componentFunctionSource(cctvLayer.init), /buildCoverageEntities|ensure(?:Active|Visible)CoverageEntities/, ); const activated = materializeCctvActiveCoverageEntities(records[42], build); assert.equal(activated.length, 5); assert.equal(insertCount, 5, 'activation builds only the active camera set'); assert.equal( records.filter((record) => record.coverageEntities.length > 0).length, 1, ); assert.match( setActiveCamera.toString(), /ensureActiveCoverageEntities\(record\)/, ); const visibleIds = new Set([ ...records.slice(0, 14).map((record) => record.camera.id), records[42].camera.id, ]); const coverageOn = materializeCctvVisibleCoverageEntities( records, visibleIds, build, ); assert.equal(coverageOn.length, 14 * 5); assert.equal( insertCount, 15 * 5, 'COVERAGE ON builds only remaining visible/eligible sets', ); assert.equal( records.filter((record) => record.coverageEntities.length > 0).length, 15, ); assert.match( componentFunctionSource(refreshCoverageStyles), /ensureVisibleCoverageEntities\(_records, coverageVisible\)/, ); assert.equal( materializeCctvVisibleCoverageEntities(records, visibleIds, build).length, 0, 'materialization is idempotent', ); }); test('default COVERAGE refresh materializes the active and visible camera frustums', () => { const records = Array.from({ length: 20 }, (_, index) => ({ camera: { ...UNCLAMPED_CAMERA, id: `cam-${index}`, lon: UNCLAMPED_CAMERA.lon + index * 0.002, groundElevationM: 0, }, coverageEntities: [], projection: null, })); const inserted = []; const viewer = { entities: { add(entity) { inserted.push(entity); return entity; }, }, }; const activeRecord = records.at(-1); try { _setCctvCoverageStateForTest({ viewer, records, activeCameraId: activeRecord.camera.id, }); refreshCoverageStyles(); const materialized = records.filter( (record) => record.coverageEntities.length > 0, ); assert.equal( inserted.length, 14 * 5, 'default COVERAGE ON builds the 14-camera visible cohort', ); assert.equal(materialized.length, 14); assert.equal( activeRecord.coverageEntities.length, 5, 'the enable-time active camera is materialized', ); assert.ok( materialized.some((record) => record !== activeRecord), 'visible neighbors are materialized alongside the active camera', ); const projectionOnly = { ...records[0], camera: { ...records[0].camera, id: 'projection-only' }, coverageEntities: [], projection: { planeEntity: { show: false } }, }; _setCctvCoverageStateForTest({ viewer, records: [projectionOnly], activeCameraId: projectionOnly.camera.id, coverageMode: 'off', showProjection: true, }); refreshCoverageStyles(); assert.equal(projectionOnly.coverageEntities.length, 5); assert.ok( projectionOnly.coverageEntities.every((entity) => entity.show), 'COVERAGE OFF keeps the active projection frustum materialized and visible', ); } finally { _setCctvCoverageStateForTest({ enabled: false }); } }); test('camera icons are violet for video feeds, cyan for stills, amber when active', () => { const record = (id, feedType) => ({ camera: { ...UNCLAMPED_CAMERA, id, feedType, groundElevationM: 0 }, billboard: {}, coverageEntities: [], projection: null, }); const idleVideo = record('idle-hls', 'hls'); const idleStill = record('idle-image', 'image'); const activeVideo = record('active-hls', 'hls'); const viewer = { entities: { add: (entity) => entity } }; try { // Disabled keeps the active video camera from opening a real projection; // icon colors are assigned either way. _setCctvCoverageStateForTest({ viewer, records: [idleVideo, idleStill, activeVideo], activeCameraId: activeVideo.camera.id, enabled: false, coverageMode: 'off', }); refreshCoverageStyles(); assert.equal(idleVideo.billboard.color, LIVE_CAMERA_COLOR); assert.equal(idleStill.billboard.color, IDLE_CAMERA_COLOR); assert.equal(activeVideo.billboard.color, ACTIVE_CAMERA_COLOR); } finally { _setCctvCoverageStateForTest({ enabled: false }); } }); test('geometry drain pacing yields to tracked and cockpit camera ownership', () => { assert.deepEqual(cctvGeometryDrainPacing(), { batchSize: 4, delayMs: 120 }); assert.deepEqual( cctvGeometryDrainPacing({ trackedEntity: { id: 'flight-1' } }), { batchSize: 2, delayMs: 250 }, ); assert.deepEqual(cctvGeometryDrainPacing({ cockpitActive: true }), { batchSize: 2, delayMs: 250, }); const active = { id: 'active' }; const queue = [{ id: 'near' }, { id: 'far' }, active]; assert.equal(prioritizeActiveCctvGeometryRecord(queue, active), true); assert.equal(queue[0], active); }); test('geometry drain rechecks pacing when tracking releases between batches', () => { let trackedEntity = { id: 'flight-1' }; const queue = Array.from({ length: 10 }, (_, index) => index + 1); const visited = []; const runBatch = () => processCctvGeometryDrainBatch({ queue, readOwnership: () => ({ trackedEntity, cockpitActive: false }), visit: (record) => visited.push(record), progress: () => {}, complete: () => {}, }); const trackedBatch = runBatch(); assert.deepEqual(trackedBatch, { hasMore: true, batchSize: 2, delayMs: 250 }); assert.deepEqual(visited, [1, 2]); trackedEntity = null; const untrackedBatch = runBatch(); assert.deepEqual(untrackedBatch, { hasMore: true, batchSize: 4, delayMs: 120, }); assert.deepEqual(visited, [1, 2, 3, 4, 5, 6]); assert.match( processGeometryBatch.toString(), /processCctvGeometryDrainBatch/, ); }); test('vFov formula: vFov = 2·atan(tan(hFov/2) / (16/9)); halfW/halfH follow', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); const expectedVFovRad = 2 * Math.atan(Math.tan(toRad(56) / 2) / ASPECT); assert.ok( Math.abs(toRad(g.vFovDeg) - expectedVFovRad) < 1e-9, `vFovDeg=${g.vFovDeg}`, ); assert.ok( Math.abs(g.halfW - 210 * Math.tan(toRad(56) / 2)) < 1e-6, `halfW=${g.halfW}`, ); assert.ok( Math.abs(g.halfH - 210 * Math.tan(expectedVFovRad / 2)) < 1e-6, `halfH=${g.halfH}`, ); }); test('mount altitude = groundAlt + mountHeightM', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); assert.equal(g.mount.alt, 100); const g2 = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND); assert.equal(g2.mount.alt, 160); }); test('cap center sits rangeM along the view axis from the mount (ε < 0.5 m)', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); const d = viewDir(41, -5); const cap = enu(g.mount, g.capCenter); const expected = { e: d.e * 210, n: d.n * 210, u: d.u * 210 }; assert.ok( mag(sub(cap, expected)) < 0.5, `cap offset error ${mag(sub(cap, expected))} m`, ); }); test('corner/plane coincidence: all 4 corners lie on the far-cap plane (ε < 0.5 m)', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); const d = viewDir(41, -5); const capEnu = enu(g.mount, g.capCenter); for (const key of ['tl', 'tr', 'br', 'bl']) { const cornerEnu = enu(g.mount, g.corners[key]); const offAxis = Math.abs(dot(sub(cornerEnu, capEnu), d)); assert.ok( offAxis < 0.5, `${key} is ${offAxis.toFixed(3)} m off the cap plane`, ); // Each corner is the half-diagonal away from the cap center. const diag = Math.hypot(g.halfW, g.halfH); const dist = mag(sub(cornerEnu, capEnu)); assert.ok( Math.abs(dist - diag) < 0.5, `${key} corner-to-center ${dist} vs diag ${diag}`, ); } }); test('cap rectangle spans 2·halfW × 2·halfH (ε < 0.5 m)', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); const m = g.mount; const width = mag(sub(enu(m, g.corners.tr), enu(m, g.corners.tl))); const height = mag(sub(enu(m, g.corners.tl), enu(m, g.corners.bl))); assert.ok( Math.abs(width - 2 * g.halfW) < 0.5, `top width ${width} vs ${2 * g.halfW}`, ); assert.ok( Math.abs(height - 2 * g.halfH) < 0.5, `left height ${height} vs ${2 * g.halfH}`, ); // Bottom edge too — the rectangle is a rectangle, not a trapezoid. const widthBottom = mag(sub(enu(m, g.corners.br), enu(m, g.corners.bl))); assert.ok( Math.abs(widthBottom - 2 * g.halfW) < 0.5, `bottom width ${widthBottom}`, ); }); test('unclamped pose: corners keep their true plane altitudes (no clamp applied)', () => { const g = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND); const floor = UNCLAMPED_GROUND + FRUSTUM_GROUND_CLEARANCE_M; for (const key of ['tl', 'tr', 'br', 'bl']) { assert.ok( g.corners[key].alt > floor + 1, `${key} alt ${g.corners[key].alt}`, ); } assert.ok( g.corners.tl.alt > g.corners.bl.alt, 'top corners sit above bottom corners', ); }); test('ground clearance lifts the whole rectangle rigidly until its lowest support clears the floor', () => { const g = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND); const floor = AUSTIN_GROUND + FRUSTUM_GROUND_CLEARANCE_M; const upVert = Math.cos(toRad(-24)) * g.halfH; // The bottom row is the limiting support on flat ground: it lands exactly on // the floor and the rest of the rectangle rides up with it, rigidly. assert.ok(g.liftM > 0, 'a downward pitch needs a lift'); assert.equal( g.liftLimitedBy[0], 'b', `limited by a bottom support, got ${g.liftLimitedBy}`, ); for (const key of ['bl', 'br']) { assert.ok( Math.abs(g.corners[key].alt - floor) < 1e-6, `${key} alt ${g.corners[key].alt} vs floor ${floor}`, ); } assert.ok( Math.abs(g.capCenter.alt - (floor + upVert)) < 1e-6, 'cap center sits one half-height above the floor', ); for (const key of ['tl', 'tr']) { assert.ok( Math.abs(g.corners[key].alt - (floor + 2 * upVert)) < 1e-6, `${key} alt ${g.corners[key].alt}`, ); } // Nothing on the rectangle is below the floor any more. for (const key of ['tl', 'tr', 'bl', 'br']) assert.ok(g.corners[key].alt >= floor - 1e-6, key); }); test('measured ground under a far support raises the plane further, still rigidly', () => { const flat = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND); const rise = 30; const lifted = computeFrustumGeometry( AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND, null, { br: AUSTIN_GROUND + rise, }, ); assert.ok( Math.abs(lifted.liftM - (flat.liftM + rise)) < 1e-6, 'lift grows by exactly the rise', ); assert.equal(lifted.liftLimitedBy, 'br'); assert.equal(lifted.footprintMeasured, true); for (const key of ['tl', 'tr', 'bl', 'br']) { assert.ok( Math.abs(lifted.corners[key].alt - flat.corners[key].alt - rise) < 1e-6, key, ); } assert.ok( Math.abs(lifted.mount.alt - flat.mount.alt) < 1e-6, 'the mount does not move', ); }); test('clamped pose keeps corner/plane coincidence and the rigid 2·halfW × 2·halfH span', () => { // The wireframe corner rays must terminate exactly on the monitor plane's // corners AT THE DEFAULT AUSTIN POSE — this is the case that diverged by // ~47.5 m under per-corner clamping (owner field test 2026-07-04). const g = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND); const d = viewDir(41, -24); const capEnu = enu(g.mount, g.capCenter); const diag = Math.hypot(g.halfW, g.halfH); for (const key of ['tl', 'tr', 'br', 'bl']) { const cornerEnu = enu(g.mount, g.corners[key]); const offAxis = Math.abs(dot(sub(cornerEnu, capEnu), d)); assert.ok( offAxis < 0.5, `${key} is ${offAxis.toFixed(3)} m off the cap plane`, ); const dist = mag(sub(cornerEnu, capEnu)); assert.ok( Math.abs(dist - diag) < 0.5, `${key} corner-to-center ${dist} vs diag ${diag}`, ); } const width = mag( sub(enu(g.mount, g.corners.tr), enu(g.mount, g.corners.tl)), ); const height = mag( sub(enu(g.mount, g.corners.tl), enu(g.mount, g.corners.bl)), ); assert.ok( Math.abs(width - 2 * g.halfW) < 0.5, `width ${width} vs ${2 * g.halfW}`, ); assert.ok( Math.abs(height - 2 * g.halfH) < 0.5, `height ${height} vs ${2 * g.halfH}`, ); }); test('range override (activation probe clamp) shortens but never lengthens the range', () => { const clamped = computeFrustumGeometry( UNCLAMPED_CAMERA, UNCLAMPED_GROUND, 100, ); assert.equal(clamped.rangeM, 100); // halfW scales with the effective range. assert.ok(Math.abs(clamped.halfW - 100 * Math.tan(toRad(56) / 2)) < 1e-6); const longer = computeFrustumGeometry( UNCLAMPED_CAMERA, UNCLAMPED_GROUND, 5000, ); assert.equal( longer.rangeM, 210, 'an override beyond the pose range is ignored', ); const none = computeFrustumGeometry(UNCLAMPED_CAMERA, UNCLAMPED_GROUND, null); assert.equal(none.rangeM, 210); }); test('activation obstruction clamp uses the field-derived 12 m floor', () => { assert.equal(activationProbeClampRange(210, 8), 12); assert.equal(activationProbeClampRange(210, 150), 146); assert.equal(activationProbeClampRange(210, 210), null); assert.equal(activationProbeClampRange(210, 250), null); }); test('deactivation clears the probe clamp and restores nominal frustum geometry', () => { const record = { camera: { ...UNCLAMPED_CAMERA }, probeClampRangeM: 105, }; let rewrittenRange = null; assert.equal( clearProbeClampOnDeactivation(record, (deactivated) => { rewrittenRange = computeFrustumGeometry( deactivated.camera, UNCLAMPED_GROUND, deactivated.probeClampRangeM, ).rangeM; }), true, ); assert.equal(record.probeClampRangeM, null); assert.equal(rewrittenRange, 210); }); test('CCTV disable hide sweep hides record visuals and destroys viewsheds without restyling', () => { const viewshedA = { id: 'viewshed-a' }; const viewshedB = { id: 'viewshed-b' }; const records = [ { camera: { id: 'a' }, activationDone: true, probeClampRangeM: 105, billboard: { color: 'keep-color', scale: 1.25 }, coverageEntities: [{ show: true }, { show: true }], projection: { planeEntity: { show: true } }, viewshedPrimitive: viewshedA, }, { camera: { id: 'b' }, activationDone: true, probeClampRangeM: 146, billboard: { color: 'keep-color', scale: 1 }, projection: null, viewshedPrimitive: viewshedB, }, ]; const destroyed = []; hideCctvRecordVisuals( records, (record) => { destroyed.push(record.camera.id); record.viewshedPrimitive = null; }, 'a', ); assert.deepEqual( records.flatMap((record) => (record.coverageEntities || []).map((entity) => entity.show), ), [false, false], ); assert.equal(records[0].projection.planeEntity.show, false); assert.deepEqual(destroyed, ['a', 'b']); assert.equal(records[0].viewshedPrimitive, null); assert.equal(records[1].viewshedPrimitive, null); assert.deepEqual( records.map((record) => record.probeClampRangeM), [null, null], ); assert.deepEqual( records.map((record) => record.activationDone), [false, true], ); assert.deepEqual( records.map((record) => record.billboard), [ { color: 'keep-color', scale: 1.25 }, { color: 'keep-color', scale: 1 }, ], ); }); test('real active monitor plane owns one protected host label and no native label entity', () => { const calls = []; const overlayHost = { setEntries: (...args) => calls.push(['entries', ...args]), setVisible: (...args) => calls.push(['visible', ...args]), clearSource: (...args) => calls.push(['clear', ...args]), }; const viewer = { entities: new Cesium.EntityCollection() }; const record = { camera: { ...UNCLAMPED_CAMERA, id: 'runtime-projection', name: 'Congress & 6th Monitor', groundElevationM: UNCLAMPED_GROUND, }, coverageEntities: [], projection: null, }; _setCctvOverlayHostForTest(overlayHost); try { const runtime = _createCctvProjectionPlaneForTest(viewer, record); _setCctvCoverageStateForTest({ viewer, records: [record], activeCameraId: record.camera.id, enabled: true, coverageMode: 'off', showProjection: true, }); refreshCoverageStyles(); assert.ok( runtime.planeEntity, 'runtime guard requires a real monitor-plane entity', ); assert.equal(runtime.planeEntity.label, undefined); assert.ok( runtime.planeEntity.plane, 'monitor plane geometry remains native', ); assert.ok( viewer.entities.values.every((entity) => entity.label === undefined), ); const publication = calls.find( ([type, sourceId]) => type === 'entries' && sourceId === 'cctv-projection', ); assert.ok( publication, 'active projection must publish its associated host label', ); assert.deepEqual(publication[3], CCTV_PROJECTION_OVERLAY_SOURCE_OPTIONS); const entry = publication[2][0]; assert.equal(entry.title, 'Congress & 6th Monitor'); assert.equal(entry.position(), runtime.labelPosition); assert.equal(entry.protected, true); assert.equal(entry.paintLane, 'selected'); assert.equal(runtime.planeEntity.show, true); const cachedPosition = runtime.labelPosition; record.frustumPositions.label = Cesium.Cartesian3.add( record.frustumPositions.label, new Cesium.Cartesian3(3, -2, 1), new Cesium.Cartesian3(), ); _updateCctvProjectionPlaneForTest(record); assert.equal( entry.position(), cachedPosition, 'host getter retains the authoritative cache', ); assert.ok( Cesium.Cartesian3.equals(cachedPosition, record.frustumPositions.label), 'plane geometry updates rewrite that shared host-label cache', ); _setCctvCoverageStateForTest({ viewer, records: [record], activeCameraId: null, enabled: false, coverageMode: 'off', showProjection: false, }); refreshCoverageStyles(); assert.equal(runtime.planeEntity.show, false); assert.deepEqual(calls.slice(-2), [ ['clear', 'cctv-projection'], ['visible', 'cctv-projection', false], ]); } finally { _setCctvCoverageStateForTest({ enabled: false }); _setCctvOverlayHostForTest(); } }); test('CCTV disable→enable defers the active-camera re-probe until its next activation', () => { const record = { camera: { id: 'active', rangeM: 210 }, activationDone: true, probeClampRangeM: 105, coverageEntities: [], }; let probeCalls = 0; const probe = () => { probeCalls += 1; record.probeClampRangeM = activationProbeClampRange( record.camera.rangeM, 150, ); }; const setActiveCamera = () => { if (!cctvRecordNeedsActivation('active', 'active', record)) return; probe(); record.activationDone = true; }; hideCctvRecordVisuals([record], () => {}, 'active'); assert.equal(record.probeClampRangeM, null); assert.equal(record.activationDone, false); assert.equal(cctvRecordNeedsActivation('active', 'active', record), true); assert.doesNotMatch( componentFunctionSource(cctvLayer.enable), /setActiveCamera|runActivationObstructionProbe|pickFromRay/, 'enable must restore nominal visuals without entering the activation probe path', ); assert.equal(probeCalls, 0, 'enable performs zero obstruction probes'); setActiveCamera(); assert.equal(probeCalls, 1); assert.equal(record.probeClampRangeM, 146); assert.equal(cctvRecordNeedsActivation('active', 'active', record), false); setActiveCamera(); assert.equal( probeCalls, 1, 're-selecting the activated camera remains a no-op', ); }); test('CCTV focus distinguishes tracking ownership from a missing active camera', () => { let flyCalls = 0; const viewer = { trackedEntity: { id: 'tracked-flight' }, camera: { flyToBoundingSphere() { flyCalls += 1; }, }, }; const record = { camera: { rangeM: 210, headingDeg: 41 }, position: { x: 1, y: 2, z: 3 }, }; const originalDebug = console.debug; console.debug = () => {}; try { assert.equal( focusCctvRecord(viewer, record, 1.9), CCTV_FOCUS_RESULT.TRACKING_HOLDS_VIEW, ); } finally { console.debug = originalDebug; } assert.equal(flyCalls, 0); assert.equal( focusCctvRecord(viewer, null, 1.9), CCTV_FOCUS_RESULT.NO_ACTIVE_CAMERA, ); }); test('CCTV focus reports when the camera flight starts', () => { let flyCalls = 0; const viewer = { trackedEntity: null, camera: { flyToBoundingSphere() { flyCalls += 1; }, }, }; const record = { camera: { rangeM: 210, headingDeg: 41 }, position: { x: 1, y: 2, z: 3 }, }; assert.equal(focusCctvRecord(viewer, record, 1.9), CCTV_FOCUS_RESULT.FOCUSED); assert.equal(flyCalls, 1); }); test('CCTV focus refuses camera flights while cockpit owns the view', () => { const originalDocument = globalThis.document; let flyCalls = 0; const viewer = { trackedEntity: null, camera: { flyToBoundingSphere() { flyCalls += 1; }, }, }; const record = { camera: { rangeM: 210, headingDeg: 41 }, position: { x: 1, y: 2, z: 3 }, }; const originalDebug = console.debug; console.debug = () => {}; globalThis.document = { body: { classList: { contains: (name) => name === 'cockpit-mode' } }, }; try { assert.equal( focusCctvRecord(viewer, record, 1.9), CCTV_FOCUS_RESULT.COCKPIT_ACTIVE, ); } finally { console.debug = originalDebug; globalThis.document = originalDocument; } assert.equal(flyCalls, 0); }); test('CCTV repeated in-world clicks dispatch focus only for the one real activation', () => { const target = new EventTarget(); const activated = []; const requests = []; const results = [ CCTV_ACTIVATION_RESULT.ACTIVATED, CCTV_ACTIVATION_RESULT.UNCHANGED, CCTV_ACTIVATION_RESULT.UNCHANGED, ]; target.addEventListener(CCTV_FOCUS_REQUEST_EVENT, (event) => requests.push(event.detail), ); const activate = (cameraId) => { activated.push(cameraId); return results.shift(); }; assert.deepEqual( [ activateCctvCameraFromWorldClick('atx-cam-3', activate, target), activateCctvCameraFromWorldClick('atx-cam-3', activate, target), activateCctvCameraFromWorldClick('atx-cam-3', activate, target), ], [true, false, false], ); assert.deepEqual(activated, ['atx-cam-3', 'atx-cam-3', 'atx-cam-3']); assert.deepEqual(requests, [{ cameraId: 'atx-cam-3' }]); assert.match( componentFunctionSource(cctvLayer.init), /bindCctvWorldClickGesture\(_clickHandler/, ); assert.match( componentFunctionSource(cctvLayer.init), /_cctvOverlayHost\.hitTest/, ); assert.match( componentFunctionSource(cctvLayer.init), /sourceId: CCTV_OVERLAY_SOURCE_ID/, ); assert.match( componentFunctionSource(cctvLayer.init), /activateCctvCameraFromWorldClick\(cameraId, setActiveCamera\)/, ); assert.match( componentFunctionSource(cctvLayer.init), /activateCctvCameraFromWorldClick\(cardId, setActiveCamera\)/, ); }); // ─── Empty-space deselection and stable null-active state ────────────────── function makeDeselectViewer() { return { entities: new Cesium.EntityCollection(), isDestroyed: () => false, trackedEntity: { id: 'sibling-track-owner' }, camera: { positionWC: Cesium.Cartesian3.fromDegrees(-97.7431, 30.2672, 4_000), positionCartographic: Cesium.Cartographic.fromDegrees( -97.7431, 30.2672, 4_000, ), heading: 0.4, pitch: -0.7, roll: 0.1, transform: Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY), flyToBoundingSphere() { this.flyCalls = (this.flyCalls || 0) + 1; }, }, scene: { canvas: { clientWidth: 1200, clientHeight: 800 }, cartesianToCanvasCoordinates: () => undefined, primitives: { add: (primitive) => primitive, remove: () => true }, requestRender() {}, }, }; } function makeDeselectRecord(id, index = 0) { return { camera: { ...UNCLAMPED_CAMERA, id, name: `Camera ${id}`, city: 'Austin', lon: UNCLAMPED_CAMERA.lon + index * 0.002, groundElevationM: UNCLAMPED_GROUND, }, coverageEntities: [], projection: null, activationDone: true, }; } test('CCTV empty-click gate excludes every identified scene object, ADJUST, and null-active clicks', () => { assert.equal( cctvEmptyClickDeselects(null, { activeCameraId: 'cam-1' }), true, ); assert.equal( cctvEmptyClickDeselects(null, { activeCameraId: 'cam-1', calibrationMode: true, }), false, ); assert.equal(cctvEmptyClickDeselects(null, { activeCameraId: null }), false); assert.equal( cctvEmptyClickDeselects( { id: 'registered-sibling' }, { activeCameraId: 'cam-1', }, ), false, ); assert.equal( cctvEmptyClickDeselects( { id: { id: 'unregistered-local-entity' } }, { activeCameraId: 'cam-1', }, ), false, ); assert.equal( cctvEmptyClickDeselects( { primitive: { id: 'unregistered-primitive' } }, { activeCameraId: 'cam-1', }, ), false, ); assert.equal( cctvEmptyClickDeselects( { id: '' }, { activeCameraId: 'cam-1', }, ), false, 'an empty-string scene ID is still identified rather than empty space', ); assert.equal( cctvEmptyClickDeselects( { primitive: {} }, { activeCameraId: 'cam-1', }, ), true, 'an ID-less surface pick remains true empty space', ); const initSource = componentFunctionSource(cctvLayer.init); assert.ok( initSource.indexOf('if (pickedId !== null) return') < initSource.indexOf('_cctvOverlayHost.hitTest'), 'every identified sibling must win before an overlapping CCTV card hit test', ); assert.match(initSource, /activeCameraId: _activeCameraId/); assert.match(initSource, /calibrationMode: _calibrationMode/); }); test('CCTV camera extraction requires layer ownership, not a colliding sibling ID', () => { const billboard = { id: 'shared-id' }; const ownedCoverageEntity = { id: 'cctv-shared-id-cap', properties: { cctvCameraId: 'shared-id' }, }; const record = { ...makeDeselectRecord('shared-id'), billboard, coverageEntities: [ownedCoverageEntity], }; _setCctvCoverageStateForTest({ records: [record], activeCameraId: 'shared-id', }); try { assert.equal( _extractPickedCameraIdForTest({ id: 'shared-id', primitive: billboard }), 'shared-id', ); assert.equal( _extractPickedCameraIdForTest({ id: 'shared-id', primitive: { id: 'shared-id' }, }), null, 'a sibling primitive with the same canonical ID cannot activate CCTV', ); assert.equal( _extractPickedCameraIdForTest({ id: { id: 'shared-id' } }), null, 'a sibling Entity with the same canonical ID cannot activate CCTV', ); assert.equal( _extractPickedCameraIdForTest({ id: ownedCoverageEntity }), 'shared-id', 'an exact stored CCTV coverage Entity retains activation ownership', ); assert.equal( _extractPickedCameraIdForTest({ id: { id: 'sibling-entity', properties: { cctvCameraId: 'shared-id' }, }, }), null, 'a sibling cannot impersonate CCTV by copying its property shape', ); } finally { _setCctvCoverageStateForTest({ enabled: false }); } }); test('CCTV deselect publishes one null state and leaves the complete camera pose unchanged', () => { const viewer = makeDeselectViewer(); const record = { ...makeDeselectRecord('deselect-me'), probeClampRangeM: 150, }; const publications = []; _setCctvOverlayHostForTest({ setEntries() {}, setVisible() {}, clearSource() {}, hitTest: () => null, }); try { const runtime = _createCctvProjectionPlaneForTest(viewer, record); _setCctvCoverageStateForTest({ viewer, records: [record], activeCameraId: record.camera.id, enabled: true, coverageMode: 'off', showProjection: true, }); refreshCoverageStyles(); const unsubscribe = cctvLayer.subscribe((state) => publications.push(state), ); const baselinePublications = publications.length; const pose = { positionWC: Cesium.Cartesian3.clone(viewer.camera.positionWC), positionCartographic: Cesium.Cartographic.clone( viewer.camera.positionCartographic, ), heading: viewer.camera.heading, pitch: viewer.camera.pitch, roll: viewer.camera.roll, transform: Cesium.Matrix4.clone(viewer.camera.transform), trackedEntity: viewer.trackedEntity, }; assert.equal(deactivateActiveCamera(), true); assert.equal( deactivateActiveCamera(), false, 'repeat deselect is idempotent', ); assert.equal(publications.length, baselinePublications + 1); assert.equal(publications.at(-1).activeCameraId, null); assert.equal(publications.at(-1).activeCamera, null); assert.equal(publications.at(-1).enabled, true); assert.equal(runtime.planeEntity.show, false); assert.equal(record.probeClampRangeM, null); assert.equal(record.activationDone, false); assert.deepEqual(viewer.camera.positionWC, pose.positionWC); assert.deepEqual( viewer.camera.positionCartographic, pose.positionCartographic, ); assert.equal(viewer.camera.heading, pose.heading); assert.equal(viewer.camera.pitch, pose.pitch); assert.equal(viewer.camera.roll, pose.roll); assert.deepEqual(viewer.camera.transform, pose.transform); assert.equal(viewer.trackedEntity, pose.trackedEntity); assert.equal(viewer.camera.flyCalls || 0, 0); unsubscribe(); } finally { _setCctvOverlayHostForTest(); _setCctvCoverageStateForTest({ enabled: false }); } }); test('CCTV null-active coverage, auto-hop, cycling, and panel targets stay honest', () => { const viewer = makeDeselectViewer(); const records = Array.from({ length: 3 }, (_, index) => makeDeselectRecord(`cam-${index}`, index), ); _setCctvOverlayHostForTest({ setEntries() {}, setVisible() {}, clearSource() {}, }); try { _setCctvCoverageStateForTest({ viewer, records, activeCameraId: records[0].camera.id, enabled: true, coverageMode: 'on', }); cctvLayer.setParams({ autoHop: true, autoHopSec: 8 }); assert.equal(deactivateActiveCamera(), true); refreshCoverageStyles(); assert.equal(cctvLayer.getUIState().activeCameraId, null); assert.ok( records.every((record) => record.coverageEntities.every((entity) => !entity.show), ), ); maybeAutoHop(1_000_000); assert.equal( cctvLayer.getUIState().activeCameraId, null, 'elapsed auto-hop stays suspended', ); assert.equal(cctvCycleIndex(-1, 1, records.length), 0); assert.equal(cctvCycleIndex(-1, -1, records.length), records.length - 1); assert.equal(cctvCycleIndex(2, 1, records.length), 0); assert.equal(cctvCycleIndex(0, -1, records.length), records.length - 1); const renderer = CCTV_PRESENTATION_SOURCE.match( /_renderCctvState\(state\) \{[\s\S]*?\n\}\n/, ); assert.ok(renderer, '_renderCctvState is missing'); assert.match( renderer[0], // Nothing active clears the pick; only the outside-view note may show. /else if \(!activeId\)[\s\S]*?selectedIndex =[^;]*\? 0 : -1;/, ); assert.match( renderer[0], /_cctvFocusBtn\.disabled\s*=\s*!enabled\s*\|\|\s*catalogCount === 0\s*\|\|\s*!activeId/, ); } finally { cctvLayer.setParams({ autoHop: false }); _setCctvOverlayHostForTest(); _setCctvCoverageStateForTest({ enabled: false }); } }); test('CCTV disable clears and hides its shared-host source through the real layer lifecycle', () => { const calls = []; _setCctvOverlayHostForTest({ clearSource(sourceId) { calls.push(['clear', sourceId]); }, setVisible(sourceId, visible) { calls.push(['visible', sourceId, visible]); }, }); try { _setCctvCoverageStateForTest({ enabled: true }); cctvLayer.disable(); assert.deepEqual(calls, [ ['clear', 'cctv'], ['visible', 'cctv', false], ['clear', 'cctv-projection'], ['visible', 'cctv-projection', false], ]); } finally { _setCctvOverlayHostForTest(); _setCctvCoverageStateForTest({ enabled: false }); } }); test('pristine module default publishes no active-camera card (shipped behavior needs no setter call)', () => { // Deliberately never calls setCardPresentationOptions: this test must run // before any test that does, so a flipped `_activeCameraCardEnabled` // default (the P4 round-1 product inversion) fails here even though every // explicit-option test still passes. const publications = []; const activeRecord = { camera: { id: 'default-active-camera', name: 'DEFAULT ACTIVE' }, position: { x: 1, y: 2, z: 3 }, }; _setCctvOverlayHostForTest({ setEntries(sourceId, entries) { publications.push({ sourceId, entries }); }, }); try { _setCctvCoverageStateForTest({ records: [activeRecord], activeCameraId: activeRecord.camera.id, enabled: true, }); _pushAmbientCardEntriesForTest(); assert.deepEqual( publications.at(-1), { sourceId: 'cctv', entries: [] }, 'active camera must not publish a host card under the untouched default', ); } finally { _setCctvOverlayHostForTest(); _setCctvCoverageStateForTest({ enabled: false }); } }); test('active CCTV camera is absent from host by default and protected only when opted in', () => { const publications = []; const activeRecord = { camera: { id: 'active-camera', name: 'ACTIVE CAMERA' }, position: { x: 1, y: 2, z: 3 }, }; _setCctvOverlayHostForTest({ setEntries(sourceId, entries) { publications.push({ sourceId, entries }); }, }); try { _setCctvCoverageStateForTest({ records: [activeRecord], activeCameraId: activeRecord.camera.id, enabled: true, }); assert.deepEqual( cctvLayer.setCardPresentationOptions({ activeCameraCardEnabled: false }), { activeCameraCardEnabled: false }, ); assert.deepEqual(publications.at(-1), { sourceId: 'cctv', entries: [] }); assert.deepEqual( cctvLayer.setCardPresentationOptions({ activeCameraCardEnabled: true }), { activeCameraCardEnabled: true }, ); const activeEntry = publications .at(-1) .entries.find(({ id }) => id === activeRecord.camera.id); assert.ok( activeEntry, 'opt-in republishes the active camera into the host', ); assert.equal(activeEntry.active, true); assert.equal(activeEntry.protected, true); } finally { setCctvCardPresentationOptions({ activeCameraCardEnabled: false }); _setCctvOverlayHostForTest(); _setCctvCoverageStateForTest({ enabled: false }); } }); test('CCTV drag-then-release over a camera is inert, while a clean tap activates and dispatches', () => { let timeMs = 0; let activationCalls = 0; const handler = makeGestureHandler(); const target = new EventTarget(); const requests = []; target.addEventListener(CCTV_FOCUS_REQUEST_EVENT, (event) => requests.push(event.detail), ); bindCctvWorldClickGesture( handler, () => { activateCctvCameraFromWorldClick( 'atx-cam-3', () => { activationCalls += 1; return CCTV_ACTIVATION_RESULT.ACTIVATED; }, target, ); }, { now: () => timeMs, eventTypes: GESTURE_TYPES, }, ); handler.fire(GESTURE_TYPES.LEFT_DOWN, { position: { x: 10, y: 10 } }); timeMs = 20; handler.fire(GESTURE_TYPES.MOUSE_MOVE, { endPosition: { x: 14, y: 10 } }); timeMs = 40; handler.fire(GESTURE_TYPES.MOUSE_MOVE, { endPosition: { x: 10, y: 10 } }); timeMs = 60; handler.fire(GESTURE_TYPES.LEFT_UP, { position: { x: 10, y: 10 } }); handler.fire(GESTURE_TYPES.LEFT_CLICK, { position: { x: 10, y: 10 } }); assert.equal(activationCalls, 0); assert.deepEqual(requests, []); timeMs = 100; handler.fire(GESTURE_TYPES.LEFT_DOWN, { position: { x: 10, y: 10 } }); timeMs = 180; handler.fire(GESTURE_TYPES.LEFT_UP, { position: { x: 11, y: 11 } }); handler.fire(GESTURE_TYPES.LEFT_CLICK, { position: { x: 11, y: 11 } }); assert.equal(activationCalls, 1); assert.deepEqual(requests, [{ cameraId: 'atx-cam-3' }]); }); test('CCTV auto-hop remains activation-only and never dispatches a focus request', () => { assert.doesNotMatch( maybeAutoHop.toString(), /activateCctvCameraFromWorldClick|gev:cctv-request-focus|dispatchEvent/, ); }); test('heading wrap: heading 350° produces a symmetric cap (left/right corners equidistant)', () => { const camera = { ...UNCLAMPED_CAMERA, headingDeg: 350 }; const g = computeFrustumGeometry(camera, UNCLAMPED_GROUND); const m = g.mount; const dTL = mag(enu(m, g.corners.tl)); const dTR = mag(enu(m, g.corners.tr)); assert.ok(Math.abs(dTL - dTR) < 0.5, `tl ${dTL} vs tr ${dTR}`); }); // --------------------------------------------------------------------------- // Task 5 — calibration v2 store + CAL badge (design §3b/§3c as amended by the // LOCKED owner decisions §9.2/§9.3: wipe-clean v2 store, panel-only badge). // --------------------------------------------------------------------------- /** Minimal in-memory localStorage stand-in for pure store-IO unit tests. */ function fakeStorage(seed = {}) { const map = new Map(Object.entries(seed)); return { getItem: (key) => (map.has(key) ? map.get(key) : null), setItem: (key, value) => { map.set(key, String(value)); }, removeItem: (key) => { map.delete(key); }, _dump: () => Object.fromEntries(map.entries()), }; } test('calibration v2 round-trip: writeCalibrationStoreV2 → readCalibrationStoreV2 preserves values/source/savedAt', () => { const storage = fakeStorage(); const savedAt = 1782800000000; const entry = new Map([ [ 'austin-42', { values: { offsetNorthM: 12.5, offsetEastM: -3.0, headingDeg: 41.0, pitchDeg: 4.0, fovDeg: -8.0, rangeScale: 1.35, heightM: 6.0, }, source: 'manual', savedAt, }, ], ]); writeCalibrationStoreV2(entry, storage); const raw = JSON.parse(storage.getItem(CCTV_CALIBRATION_STORAGE_KEY_V2)); assert.equal(raw['austin-42'].source, 'manual'); assert.equal(raw['austin-42'].savedAt, savedAt); assert.equal(raw['austin-42'].values.offsetNorthM, 12.5); const restored = readCalibrationStoreV2(storage); assert.ok(restored instanceof Map); const cam = restored.get('austin-42'); assert.equal(cam.source, 'manual'); assert.equal(cam.savedAt, savedAt); assert.deepEqual(cam.values, entry.get('austin-42').values); }); test('calibration v2: removing an entry (reset) then re-writing produces an empty store', () => { const storage = fakeStorage(); const entry = new Map([ [ 'sf-market-5th', { values: { offsetNorthM: 5, offsetEastM: 0, headingDeg: 0, pitchDeg: 0, fovDeg: 0, rangeScale: 1, heightM: 0, }, source: 'manual', savedAt: 1000, }, ], ]); writeCalibrationStoreV2(entry, storage); assert.ok(readCalibrationStoreV2(storage).has('sf-market-5th')); // Reset removes the entry from the map, then persists the now-empty map — // this is the shape setParams({calibration:{reset:true}}) drives. entry.delete('sf-market-5th'); writeCalibrationStoreV2(entry, storage); const restored = readCalibrationStoreV2(storage); assert.equal( restored.size, 0, 'reset entry must be gone, base pose restored', ); }); test('calibration v2: malformed/partial entries are dropped defensively', () => { const storage = fakeStorage({ [CCTV_CALIBRATION_STORAGE_KEY_V2]: JSON.stringify({ 'ok-cam': { values: { offsetNorthM: 1, offsetEastM: 2, headingDeg: 3, pitchDeg: 4, fovDeg: 5, rangeScale: 1.1, heightM: 6, }, source: 'manual', savedAt: 42, }, 'no-values': { source: 'manual', savedAt: 42 }, junk: 'not-an-object', }), }); const restored = readCalibrationStoreV2(storage); assert.ok(restored.has('ok-cam')); assert.equal(restored.get('ok-cam').values.offsetNorthM, 1); }); test('calibration v2: a corrupt v1 key never leaks into the v2 store (v1 is dead data, never read)', () => { const storage = fakeStorage({ [CCTV_CALIBRATION_STORAGE_KEY_V1]: JSON.stringify({ 'austin-42': { offsetNorthM: 999, offsetEastM: 999, headingDeg: 999, pitchDeg: 0, fovDeg: 0, rangeScale: 1, heightM: 0, }, }), }); // v2 key is untouched/empty — v1's presence must have zero effect. const restored = readCalibrationStoreV2(storage); assert.equal( restored.size, 0, 'v2 store must start empty — no legacy import (owner decision #3, §9.3)', ); assert.ok(!restored.has('austin-42')); }); test('deriveCalBadge: CALIBRATED when the camera carries a manual v2 calibration', () => { const camera = { calSource: 'manual', poseSource: 'curated' }; // Manual calibration wins over curated — a human explicitly tuned this pose. assert.equal(deriveCalBadge(camera), 'calibrated'); }); test('deriveCalBadge: CURATED for a hand-authored catalog prior with no manual save', () => { const camera = { calSource: null, poseSource: 'curated' }; assert.equal(deriveCalBadge(camera), 'curated'); }); test('deriveCalBadge: RAW PRIOR for everything else (all Austin Open Data today)', () => { const camera = { calSource: null, poseSource: null }; assert.equal(deriveCalBadge(camera), 'raw-prior'); assert.equal(deriveCalBadge({}), 'raw-prior'); }); // --------------------------------------------------------------------------- // Task 5 (height-datum fix): regime-aware ground resolution pure helpers. // docs/superpowers/specs/2026-07-05-entity-height-datum-design.md §2. // --------------------------------------------------------------------------- test('surfaceRegimeKey: globe hidden (photoreal) → google-3d; globe visible → terrain-globe', () => { assert.equal(surfaceRegimeKey(false), 'google-3d'); assert.equal(surfaceRegimeKey(true), 'terrain-globe'); }); test('surfaceRegimeKey: unknown globe state (no viewer / no scene) defaults to terrain-globe (never samples)', () => { // Only an explicit globe.show === false means the visible surface is the // Google tileset. undefined/null (torn-down viewer) must fall to the // regime that takes ZERO scene queries. assert.equal(surfaceRegimeKey(undefined), 'terrain-globe'); assert.equal(surfaceRegimeKey(null), 'terrain-globe'); }); test('calibrationPatchMovesAnchor: only north/east translation re-resolves ground', () => { assert.equal(calibrationPatchMovesAnchor({ offsetNorthM: 10 }), true); assert.equal(calibrationPatchMovesAnchor({ offsetEastM: -5 }), true); assert.equal(calibrationPatchMovesAnchor({ headingDeg: 20 }), false); assert.equal(calibrationPatchMovesAnchor({ pitchDeg: -2, fovDeg: 5 }), false); assert.equal( calibrationPatchMovesAnchor({ rangeScale: 1.2, heightM: 8 }), false, ); assert.equal(calibrationPatchMovesAnchor(null), false); }); // Coverage tri-state (viewshed design §3b): normalizeCoverageMode // --------------------------------------------------------------------------- test('normalizeCoverageMode: passes through the three valid modes', () => { assert.equal(normalizeCoverageMode('off', 'on'), 'off'); assert.equal(normalizeCoverageMode('on', 'off'), 'on'); assert.equal(normalizeCoverageMode('viewshed', 'off'), 'viewshed'); }); test('normalizeCoverageMode: boolean back-compat (showCoverage semantics)', () => { assert.equal(normalizeCoverageMode(true, 'off'), 'on'); assert.equal(normalizeCoverageMode(false, 'viewshed'), 'off'); }); test('normalizeCoverageMode: garbage keeps the current mode', () => { assert.equal(normalizeCoverageMode('sideways', 'viewshed'), 'viewshed'); assert.equal(normalizeCoverageMode(undefined, 'on'), 'on'); assert.equal(normalizeCoverageMode(null, 'off'), 'off'); assert.equal(normalizeCoverageMode(3, 'on'), 'on'); }); // Unchanged-frame signature (white-flash fix, owner field test 2026-07-30) // --------------------------------------------------------------------------- /** Builds an RGBA buffer from [r,g,b] triples. */ function rgba(triples) { const out = new Uint8ClampedArray(triples.length * 4); triples.forEach(([r, g, b], i) => { out[i * 4] = r; out[i * 4 + 1] = g; out[i * 4 + 2] = b; out[i * 4 + 3] = 255; }); return out; } test('frameSignatureFromPixels: identical pixels hash identically', () => { const a = rgba([ [1, 2, 3], [250, 128, 7], [0, 0, 0], ]); const b = rgba([ [1, 2, 3], [250, 128, 7], [0, 0, 0], ]); assert.equal(frameSignatureFromPixels(a), frameSignatureFromPixels(b)); }); test('frameSignatureFromPixels: a single changed channel changes the hash', () => { const base = rgba([ [10, 20, 30], [40, 50, 60], ]); const tweak = rgba([ [10, 20, 31], [40, 50, 60], ]); assert.notEqual( frameSignatureFromPixels(base), frameSignatureFromPixels(tweak), ); }); test('frameSignatureFromPixels: order matters (a swap is not a collision)', () => { const ab = rgba([ [1, 2, 3], [9, 8, 7], ]); const ba = rgba([ [9, 8, 7], [1, 2, 3], ]); assert.notEqual(frameSignatureFromPixels(ab), frameSignatureFromPixels(ba)); }); test('frameSignatureFromPixels: alpha is deliberately ignored', () => { const opaque = new Uint8ClampedArray([5, 6, 7, 255]); const clear = new Uint8ClampedArray([5, 6, 7, 0]); assert.equal( frameSignatureFromPixels(opaque), frameSignatureFromPixels(clear), ); }); test('frameSignatureFromPixels: returns an unsigned 32-bit value', () => { const sig = frameSignatureFromPixels( rgba([ [255, 255, 255], [0, 0, 0], ]), ); assert.ok( Number.isInteger(sig) && sig >= 0 && sig <= 0xffffffff, `got ${sig}`, ); }); test('frameSignatureFromPixels: empty or junk input yields null (always redraw)', () => { assert.equal(frameSignatureFromPixels(new Uint8ClampedArray(0)), null); assert.equal(frameSignatureFromPixels(null), null); assert.equal(frameSignatureFromPixels(undefined), null); assert.equal(frameSignatureFromPixels({}), null); }); test('a saved rangeScale keeps its effective range when the catalog range floor drops', () => { const saved = { offsetNorthM: 0, offsetEastM: 0, headingDeg: 0, pitchDeg: 0, fovDeg: 0, rangeScale: 1.5, heightM: 0, }; // A 145 m pack range used to be inflated to 220 m: 220 × 1.5 = 330 m. The // migrated multiplier reproduces that on the honest 145 m base. const migrated = migrateRangeScaleForFloor(saved, 145); assert.ok( Math.abs(145 * migrated.rangeScale - 330) < 1, `${145 * migrated.rangeScale}`, ); // A pack range at or above the old floor was never inflated: unchanged. assert.equal(migrateRangeScaleForFloor(saved, 250).rangeScale, 1.5); assert.equal(migrateRangeScaleForFloor(saved, 220).rangeScale, 1.5); // The 3× ceiling still applies. assert.equal( migrateRangeScaleForFloor({ ...saved, rangeScale: 3 }, 145).rangeScale, 3, ); }); test('the footprint lift is capped so a tower under the far edge cannot launch the plane', () => { // Rises are placed under a BOTTOM support: the top row already sits one // plane-height higher, so a rise there is absorbed by the tilt first. const flat = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND); const tower = computeFrustumGeometry( AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND, null, { br: AUSTIN_GROUND + 240, }, ); assert.equal(tower.liftCapped, true); assert.ok( Math.abs(tower.liftM - (flat.liftM + 60)) < 1e-6, `lift ${tower.liftM} vs ${flat.liftM}`, ); const hill = computeFrustumGeometry( AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND, null, { br: AUSTIN_GROUND + 30, }, ); assert.equal(hill.liftCapped, false); assert.ok(Math.abs(hill.liftM - (flat.liftM + 30)) < 1e-6); });