mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Merge branch 'main' into cctv-stills-aspect
Resolves the scripts/format-scope.json conflict with #1200 by keeping both new CCTV test entries. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
ba5a1e25ee
7 files changed
+131
-8
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
- Color CCTV cameras with a video feed violet. Cameras with an HLS, MP4 or
|
||||||
|
WebM feed were the same cyan as stills, so a DelDOT or Statens vegvesen
|
||||||
|
video camera could only be found by opening it. Stills stay cyan and the
|
||||||
|
active camera stays amber, and focus de-emphasis fades the violet the same
|
||||||
|
way (Danielslay86, #1200).
|
||||||
@@ -4435,7 +4435,7 @@ silently demoting every later lookup for the session.
|
|||||||
ground clamp now lifts the cap _center_ only so the wireframe stays a true pyramid welded to
|
ground clamp now lifts the cap _center_ only so the wireframe stays a true pyramid welded to
|
||||||
the plane (was a flattened fan / the ~47.5 m divergence — RESOLVED), re-selecting the active
|
the plane (was a flattened fan / the ~47.5 m divergence — RESOLVED), re-selecting the active
|
||||||
camera is a no-op (killed a click-flash), and texture swaps gate on canvas content (killed a
|
camera is a no-op (killed a click-flash), and texture swaps gate on canvas content (killed a
|
||||||
periodic white flash). Coverage is now **metro-wide: 250 cameras** (`CCTV_AUSTIN_MAX_SOURCES`
|
periodic white flash). Camera icons are cyan for stills, violet for cameras with a video feed, and amber for the active camera. Coverage is now **metro-wide: 250 cameras** (`CCTV_AUSTIN_MAX_SOURCES`
|
||||||
default 36 → 250, hard bound 300), filtered to `camera_status === TURNED_ON` (~815 live of
|
default 36 → 250, hard bound 300), filtered to `camera_status === TURNED_ON` (~815 live of
|
||||||
1,003 rows). City packs (2026-07-04): Caltrans (districts 4/7/11/3 — SF, LA, San Diego,
|
1,003 rows). City packs (2026-07-04): Caltrans (districts 4/7/11/3 — SF, LA, San Diego,
|
||||||
Sacramento; cap 300) and TfL London JamCams (cap 250) join Austin (cap 250) as keyless default
|
Sacramento; cap 300) and TfL London JamCams (cap 250) join Austin (cap 250) as keyless default
|
||||||
|
|||||||
@@ -558,6 +558,7 @@
|
|||||||
"src/data/cctvTxdotSource.test.mjs",
|
"src/data/cctvTxdotSource.test.mjs",
|
||||||
"src/layers/cctv/source.test.mjs",
|
"src/layers/cctv/source.test.mjs",
|
||||||
"src/layers/cctv/frames.test.mjs",
|
"src/layers/cctv/frames.test.mjs",
|
||||||
|
"src/layers/cctv/rendering.test.mjs",
|
||||||
"src/data/alprCameras.js",
|
"src/data/alprCameras.js",
|
||||||
"src/data/alprCameras.test.mjs",
|
"src/data/alprCameras.test.mjs",
|
||||||
"src/data/dataCredits.test.mjs",
|
"src/data/dataCredits.test.mjs",
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ import {
|
|||||||
CCTV_FOCUS_REQUEST_EVENT,
|
CCTV_FOCUS_REQUEST_EVENT,
|
||||||
activateCctvCameraFromWorldClick,
|
activateCctvCameraFromWorldClick,
|
||||||
} from '../cctvFocusRequest.js';
|
} from '../cctvFocusRequest.js';
|
||||||
|
import {
|
||||||
|
ACTIVE_CAMERA_COLOR,
|
||||||
|
IDLE_CAMERA_COLOR,
|
||||||
|
LIVE_CAMERA_COLOR,
|
||||||
|
} from '../layers/cctv/policy.js';
|
||||||
|
|
||||||
const CCTV_PRESENTATION_SOURCE = fs.readFileSync(
|
const CCTV_PRESENTATION_SOURCE = fs.readFileSync(
|
||||||
path.join(
|
path.join(
|
||||||
@@ -360,6 +365,37 @@ test('default COVERAGE refresh materializes the active and visible camera frustu
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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', () => {
|
test('geometry drain pacing yields to tracked and cockpit camera ownership', () => {
|
||||||
assert.deepEqual(cctvGeometryDrainPacing(), { batchSize: 4, delayMs: 120 });
|
assert.deepEqual(cctvGeometryDrainPacing(), { batchSize: 4, delayMs: 120 });
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
|
|||||||
@@ -413,6 +413,9 @@ export const IDLE_CAMERA_COLOR =
|
|||||||
export const ACTIVE_CAMERA_COLOR =
|
export const ACTIVE_CAMERA_COLOR =
|
||||||
Cesium.Color.fromCssColorString('#ffd97a').withAlpha(0.95);
|
Cesium.Color.fromCssColorString('#ffd97a').withAlpha(0.95);
|
||||||
|
|
||||||
|
export const LIVE_CAMERA_COLOR =
|
||||||
|
Cesium.Color.fromCssColorString('#b48cff').withAlpha(0.88);
|
||||||
|
|
||||||
export const IDLE_COVERAGE_COLOR =
|
export const IDLE_COVERAGE_COLOR =
|
||||||
Cesium.Color.fromCssColorString('#2fe0ff').withAlpha(0.24);
|
Cesium.Color.fromCssColorString('#2fe0ff').withAlpha(0.24);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { isHeadingEstimated } from './headingConfidence.js';
|
|||||||
import {
|
import {
|
||||||
ACTIVE_CAMERA_COLOR,
|
ACTIVE_CAMERA_COLOR,
|
||||||
IDLE_CAMERA_COLOR,
|
IDLE_CAMERA_COLOR,
|
||||||
|
LIVE_CAMERA_COLOR,
|
||||||
ACTIVE_COVERAGE_CENTER,
|
ACTIVE_COVERAGE_CENTER,
|
||||||
IDLE_COVERAGE_CENTER_MUTED,
|
IDLE_COVERAGE_CENTER_MUTED,
|
||||||
ACTIVE_COVERAGE_CENTER_DEPTHFAIL,
|
ACTIVE_COVERAGE_CENTER_DEPTHFAIL,
|
||||||
@@ -18,6 +19,15 @@ export function createRendering({
|
|||||||
parts,
|
parts,
|
||||||
source,
|
source,
|
||||||
}) {
|
}) {
|
||||||
|
/** Icon color: active wins, then live-capable video feeds, then idle stills. */
|
||||||
|
function cameraColorFor(record) {
|
||||||
|
if (record.camera.id === layerState._activeCameraId)
|
||||||
|
return ACTIVE_CAMERA_COLOR;
|
||||||
|
if (parts.model.isVideoFeedType(record.camera.feedType))
|
||||||
|
return LIVE_CAMERA_COLOR;
|
||||||
|
return IDLE_CAMERA_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
focusNowMs,
|
focusNowMs,
|
||||||
getFocusTarget,
|
getFocusTarget,
|
||||||
@@ -58,10 +68,7 @@ export function createRendering({
|
|||||||
),
|
),
|
||||||
cameraDistanceFor: (position) =>
|
cameraDistanceFor: (position) =>
|
||||||
Cesium.Cartesian3.distance(camera.positionWC, position),
|
Cesium.Cartesian3.distance(camera.positionWC, position),
|
||||||
baseColorFor: (record) =>
|
baseColorFor: cameraColorFor,
|
||||||
record.camera.id === layerState._activeCameraId
|
|
||||||
? ACTIVE_CAMERA_COLOR
|
|
||||||
: IDLE_CAMERA_COLOR,
|
|
||||||
});
|
});
|
||||||
layerState._activeFocusStyleCount = result.activeCount;
|
layerState._activeFocusStyleCount = result.activeCount;
|
||||||
}
|
}
|
||||||
@@ -208,9 +215,7 @@ export function createRendering({
|
|||||||
for (const record of layerState._records) {
|
for (const record of layerState._records) {
|
||||||
const isActive = record.camera.id === activeId;
|
const isActive = record.camera.id === activeId;
|
||||||
if (record.billboard) {
|
if (record.billboard) {
|
||||||
record.billboard.color = isActive
|
record.billboard.color = cameraColorFor(record);
|
||||||
? ACTIVE_CAMERA_COLOR
|
|
||||||
: IDLE_CAMERA_COLOR;
|
|
||||||
record.billboard.scale = isActive ? 1.25 : 1.0;
|
record.billboard.scale = isActive ? 1.25 : 1.0;
|
||||||
// disableDepthTestDistance stays POSITIVE_INFINITY for every billboard
|
// disableDepthTestDistance stays POSITIVE_INFINITY for every billboard
|
||||||
// (set at creation) — see the field-test far-zoom submerge fix there.
|
// (set at creation) — see the field-test far-zoom submerge fix there.
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// src/layers/cctv/rendering.test.mjs
|
||||||
|
// Icon colors under focus de-emphasis (#1200): the focus pass fades each icon
|
||||||
|
// from the same base color the coverage refresh assigns, so a video-feed camera
|
||||||
|
// stays violet while it dims instead of falling back to the idle cyan.
|
||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { createRendering } from './rendering.js';
|
||||||
|
import {
|
||||||
|
ACTIVE_CAMERA_COLOR,
|
||||||
|
IDLE_CAMERA_COLOR,
|
||||||
|
LIVE_CAMERA_COLOR,
|
||||||
|
} from './policy.js';
|
||||||
|
import { isVideoFeedType } from '../../sources/cctvTypes.js';
|
||||||
|
|
||||||
|
const FOCUS_FACTOR = 0.5;
|
||||||
|
|
||||||
|
function assertFadedFrom(actual, base, label) {
|
||||||
|
assert.ok(actual, `${label}: color written`);
|
||||||
|
assert.deepEqual(
|
||||||
|
[actual.red, actual.green, actual.blue],
|
||||||
|
[base.red, base.green, base.blue],
|
||||||
|
`${label}: hue`,
|
||||||
|
);
|
||||||
|
assert.equal(actual.alpha, base.alpha * FOCUS_FACTOR, `${label}: alpha`);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('focus de-emphasis fades each icon from its own color', () => {
|
||||||
|
const record = (id, feedType) => ({
|
||||||
|
camera: { id, feedType },
|
||||||
|
// No position, so the pass skips screen projection; the focus stub below
|
||||||
|
// decides the factor on its own.
|
||||||
|
billboard: { show: true },
|
||||||
|
});
|
||||||
|
const idleVideo = record('idle-hls', 'hls');
|
||||||
|
const idleStill = record('idle-image', 'image');
|
||||||
|
const activeVideo = record('active-hls', 'hls');
|
||||||
|
const state = {
|
||||||
|
_enabled: true,
|
||||||
|
_viewer: { scene: {}, camera: {} },
|
||||||
|
_records: [idleVideo, idleStill, activeVideo],
|
||||||
|
_activeCameraId: activeVideo.camera.id,
|
||||||
|
_activeFocusStyleCount: 0,
|
||||||
|
_lastFocusStyleAt: -Infinity,
|
||||||
|
};
|
||||||
|
const focus = {
|
||||||
|
focusNowMs: (nowMs) => nowMs,
|
||||||
|
getFocusTarget: () => ({}),
|
||||||
|
focusPassIsNeeded: () => true,
|
||||||
|
advanceSpriteFocus: () => ({
|
||||||
|
factor: FOCUS_FACTOR,
|
||||||
|
active: true,
|
||||||
|
transitioning: false,
|
||||||
|
}),
|
||||||
|
focusAlphaNeedsWrite: () => true,
|
||||||
|
};
|
||||||
|
const { refreshCctvFocusStyles } = createRendering({
|
||||||
|
state,
|
||||||
|
services: { focus },
|
||||||
|
parts: { model: { isVideoFeedType } },
|
||||||
|
source: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshCctvFocusStyles(1000);
|
||||||
|
|
||||||
|
assertFadedFrom(idleVideo.billboard.color, LIVE_CAMERA_COLOR, 'idle video');
|
||||||
|
assertFadedFrom(idleStill.billboard.color, IDLE_CAMERA_COLOR, 'idle still');
|
||||||
|
assertFadedFrom(
|
||||||
|
activeVideo.billboard.color,
|
||||||
|
ACTIVE_CAMERA_COLOR,
|
||||||
|
'active video',
|
||||||
|
);
|
||||||
|
assert.equal(state._activeFocusStyleCount, 3);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user