Files
gods-eye-view/src/layers/cctv/index.js
T
Sameh Khamis 479286a6a6 CCTV opens on the camera nearest the viewer, not the first in the catalog
Turning CCTV on picked the first catalog record, so at King's Cross it
showed a Sydney camera. enable() now picks the camera nearest the viewer.
Until someone chooses a camera, each settled move re-picks the nearest,
so a share link whose flight lands after CCTV turned on still opens
nearby.
2026-11-07 03:52:38 -08:00

138 lines
6.2 KiB
JavaScript

import { createCalibration } from './calibration.js';
import { createTesting } from './testing.js';
import { createProjection } from './projection.js';
import { createCards } from './cards.js';
import { createModel } from './model.js';
import { createGround } from './ground.js';
import { createGeometry } from './geometry.js';
import { createCatalog } from './catalog.js';
import { createFrames } from './frames.js';
import { createRendering } from './rendering.js';
import { createGeometryQueue } from './geometryQueue.js';
import { createSelection } from './selection.js';
import { createHover } from './hover.js';
import { createNavigation } from './navigation.js';
import { createPresentation } from './presentation.js';
import { createLifecycle } from './lifecycle.js';
import { createHealth } from './health.js';
import { createControls } from './controls.js';
import { createIngestion } from './ingestion.js';
import { createState } from './state.js';
/** Construct one layer with its own scene state and supplied application services. */
export function createCctvLayer({ services, source }) {
if (
!['getCatalog', 'getHealth', 'getFrameUrl', 'getMediaUrl'].every(
(key) => typeof source?.[key] === 'function',
)
)
throw new TypeError('A camera source is required');
const state = createState({ services });
const parts = {};
const context = { state, services, parts, source };
parts.calibration = createCalibration(context);
parts.testing = createTesting(context);
parts.projection = createProjection(context);
parts.cards = createCards(context);
parts.model = createModel(context);
parts.ground = createGround(context);
parts.geometry = createGeometry(context);
parts.catalog = createCatalog(context);
parts.frames = createFrames(context);
parts.rendering = createRendering(context);
parts.geometryQueue = createGeometryQueue(context);
parts.selection = createSelection(context);
parts.hover = createHover(context);
parts.navigation = createNavigation(context);
parts.presentation = createPresentation(context);
parts.lifecycle = createLifecycle(context);
parts.health = createHealth(context);
parts.controls = createControls(context);
parts.ingestion = createIngestion(context);
return Object.assign(
{},
parts.controls.methods,
parts.lifecycle.methods,
parts.ingestion?.methods,
{
calibrationPatchMovesAnchor:
parts.calibration.calibrationPatchMovesAnchor,
migrateRangeScaleForFloor: parts.calibration.migrateRangeScaleForFloor,
_pushAmbientCardEntriesForTest:
parts.testing._pushAmbientCardEntriesForTest,
_setCctvOverlayHostForTest: parts.testing._setCctvOverlayHostForTest,
createCctvProjectionOverlayEntry:
parts.projection.createCctvProjectionOverlayEntry,
getActiveVideoElement: parts.projection.getActiveVideoElement,
setCctvCardPresentationOptions:
parts.cards.setCctvCardPresentationOptions,
surfaceRegimeKey: parts.ground.surfaceRegimeKey,
normalizeCoverageMode: parts.model.normalizeCoverageMode,
readCalibrationStoreV2: parts.calibration.readCalibrationStoreV2,
writeCalibrationStoreV2: parts.calibration.writeCalibrationStoreV2,
deriveCalBadge: parts.calibration.deriveCalBadge,
computeFrustumGeometry: parts.geometry.computeFrustumGeometry,
activationProbeClampRange: parts.geometry.activationProbeClampRange,
frameSignatureFromPixels: parts.frames.frameSignatureFromPixels,
_createCctvProjectionPlaneForTest:
parts.testing._createCctvProjectionPlaneForTest,
_updateCctvProjectionPlaneForTest:
parts.testing._updateCctvProjectionPlaneForTest,
applyCctvFocusDeemphasis: parts.rendering.applyCctvFocusDeemphasis,
createGeometryProgressNotifier:
parts.geometryQueue.createGeometryProgressNotifier,
processCctvGeometryQueueBatch:
parts.geometryQueue.processCctvGeometryQueueBatch,
cctvGeometryDrainPacing: parts.geometryQueue.cctvGeometryDrainPacing,
processCctvGeometryDrainBatch:
parts.geometryQueue.processCctvGeometryDrainBatch,
prioritizeActiveCctvGeometryRecord:
parts.geometryQueue.prioritizeActiveCctvGeometryRecord,
processGeometryBatch: parts.geometryQueue.processGeometryBatch,
hideCctvRecordVisuals: parts.rendering.hideCctvRecordVisuals,
refreshCoverageStyles: parts.rendering.refreshCoverageStyles,
refreshHorizonCulling: parts.rendering.refreshHorizonCulling,
_viewRecordIdsForTest: () =>
state._viewRecords.map((record) => record.camera.id),
clearProbeClampOnDeactivation:
parts.geometry.clearProbeClampOnDeactivation,
cctvRecordNeedsActivation: parts.model.cctvRecordNeedsActivation,
bindCctvWorldClickGesture: parts.selection.bindCctvWorldClickGesture,
setActiveCamera: parts.selection.setActiveCamera,
deactivateActiveCamera: parts.selection.deactivateActiveCamera,
cctvEmptyClickDeselects: parts.selection.cctvEmptyClickDeselects,
materializeCctvCoverageEntities:
parts.geometry.materializeCctvCoverageEntities,
materializeCctvActiveCoverageEntities:
parts.geometry.materializeCctvActiveCoverageEntities,
materializeCctvVisibleCoverageEntities:
parts.geometry.materializeCctvVisibleCoverageEntities,
_extractPickedCameraIdForTest:
parts.testing._extractPickedCameraIdForTest,
_setCctvCoverageStateForTest: parts.testing._setCctvCoverageStateForTest,
focusCctvRecord: parts.navigation.focusCctvRecord,
maybeAutoHop: parts.navigation.maybeAutoHop,
openingCameraId: parts.navigation.openingCameraId,
reopenNearestCamera: parts.navigation.reopenNearestCamera,
cctvCycleIndex: parts.navigation.cctvCycleIndex,
},
);
}
export {
CCTV_CALIBRATION_STORAGE_KEY_V1,
CCTV_CALIBRATION_STORAGE_KEY_V2,
FRUSTUM_GROUND_CLEARANCE_M,
CCTV_FOCUS_RESULT,
CCTV_PROJECTION_OVERLAY_SOURCE_ID,
CCTV_PROJECTION_OVERLAY_SOURCE_OPTIONS,
} from './policy.js';
export { createCctvSource } from './source.js';
export { CCTV_AMBIENT_CARD_MAX } from '../../data/cctvLod.js';
export {
CCTV_OVERLAY_SOURCE_ID,
createCctvThumbnailOverlayEntry,
createFrameSlot,
} from '../../data/cctvCards.js';