mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
151 lines
6.1 KiB
JavaScript
151 lines
6.1 KiB
JavaScript
/**
|
||
* @file Preset-aware traffic dot styling — pure lookup tables mapping the
|
||
* active post-FX style (StyleManager preset name) + congestion bucket to a
|
||
* dot treatment the shaders cannot destroy.
|
||
*
|
||
* Why: traffic dots are in-scene PointPrimitives, so they pass THROUGH the
|
||
* post-FX shaders (unlike the detection/FIRMS canvases composited above
|
||
* them). NVG and FLIR reduce the scene to luminance (hue is discarded), and
|
||
* the shipped bucket palette's Rec.601 luma ordering is free 0.58 / slow
|
||
* 0.72 / jam 0.49 — under NVG a jam renders DIMMER than free flow. CRT
|
||
* keeps hue but its 5-px pixelation + dithering shred 4–6 px dots.
|
||
*
|
||
* Encoding per profile (owner verdict 2026-07-23 round 2: "just bright
|
||
* dots" — a luminance RAMP failed in the field because dim free-flow dots
|
||
* read as dark holes on NVG-bright roads; classification is the detection
|
||
* brackets' job via `trafficBucketTier`, presence is the dots' job):
|
||
* - `mono` (surveillance/NVG, thermal/FLIR, noir): every colored dot is a
|
||
* bright white core with a thin black halo (local contrast survives any
|
||
* luma mapping, including Ironbow where white stays the hottest end);
|
||
* jam keeps a size bump so queues read as fat beads.
|
||
* - `crt` (retro): saturated primaries that survive 10-level posterization
|
||
* plus an all-bucket size boost to out-shout the pixel grid.
|
||
* - `normal` (normal/anime/snow/unknown): every lookup returns null/0 —
|
||
* the shipped palette and sizing apply untouched.
|
||
*
|
||
* Sim/uncovered dots (bucket null/'sim') are NEVER restyled under any
|
||
* style: the keyless simulation path stays byte-identical (qa-traffic
|
||
* gate v), and white is already luminance-legible everywhere.
|
||
*
|
||
* Cesium-free so the tables are unit-testable; traffic.js maps tuples to
|
||
* Cesium colors at spawn/restyle time (see `trafficFlowStyle.js` pattern).
|
||
*
|
||
* @module data/trafficPresetStyle
|
||
*/
|
||
|
||
/** @const {Object<string,'mono'|'crt'>} Style name → non-normal profile. */
|
||
const PROFILE_BY_STYLE = {
|
||
surveillance: 'mono', // NVG — P43 phosphor × luma
|
||
thermal: 'mono', // FLIR — grayscale/ironbow × luma
|
||
noir: 'mono', // full desaturation
|
||
retro: 'crt', // CRT — hue survives, small dots don't
|
||
};
|
||
|
||
/**
|
||
* Per-profile bucket treatments: rgba ([r,g,b] 0–255 + alpha 0–1) and the
|
||
* pixel-size delta added ON TOP of the shipped sizing (which already gives
|
||
* jam +1). Mono luma: jam 1.00 / slow 0.70 / free 0.36.
|
||
* @const {Object<string, Object<string, {rgba:number[], sizeDelta:number}>>}
|
||
*/
|
||
const DOT_STYLE = {
|
||
mono: {
|
||
jam: {
|
||
rgba: [255, 255, 255, 0.95],
|
||
sizeDelta: 3,
|
||
outline: { rgba: [0, 0, 0, 0.9], width: 2 },
|
||
},
|
||
slow: {
|
||
rgba: [255, 255, 255, 0.9],
|
||
sizeDelta: 1,
|
||
outline: { rgba: [0, 0, 0, 0.85], width: 1 },
|
||
},
|
||
free: {
|
||
rgba: [255, 255, 255, 0.85],
|
||
sizeDelta: 0,
|
||
outline: { rgba: [0, 0, 0, 0.8], width: 1 },
|
||
},
|
||
},
|
||
crt: {
|
||
jam: {
|
||
rgba: [255, 59, 48, 0.95],
|
||
sizeDelta: 3,
|
||
outline: { rgba: [0, 0, 0, 0.9], width: 2 },
|
||
},
|
||
slow: {
|
||
rgba: [255, 179, 0, 0.92],
|
||
sizeDelta: 2,
|
||
outline: { rgba: [0, 0, 0, 0.85], width: 1 },
|
||
},
|
||
free: { rgba: [0, 255, 102, 0.9], sizeDelta: 1, outline: null },
|
||
},
|
||
};
|
||
|
||
/**
|
||
* Classify a StyleManager preset name into a traffic styling profile.
|
||
* @param {string|null|undefined} styleName - Active style (e.g. 'surveillance').
|
||
* @returns {'normal'|'mono'|'crt'} Styling profile; unknown → 'normal'.
|
||
*/
|
||
export function trafficStyleProfile(styleName) {
|
||
return PROFILE_BY_STYLE[styleName] || 'normal';
|
||
}
|
||
|
||
/**
|
||
* Preset dot color for a congestion bucket, or null to keep the shipped
|
||
* FLOW_BUCKET_COLORS value. Null/'sim' buckets always return null.
|
||
* @param {string|null|undefined} styleName - Active style name.
|
||
* @param {'free'|'slow'|'jam'|'sim'|null|undefined} bucket - Flow bucket.
|
||
* @returns {number[]|null} [r,g,b,a] with rgb 0–255, alpha 0–1, or null.
|
||
*/
|
||
export function presetDotRgba(styleName, bucket) {
|
||
const entry = DOT_STYLE[trafficStyleProfile(styleName)]?.[bucket];
|
||
return entry ? entry.rgba : null;
|
||
}
|
||
|
||
/**
|
||
* Pixel-size delta a preset adds on top of the shipped dot sizing.
|
||
* @param {string|null|undefined} styleName - Active style name.
|
||
* @param {'free'|'slow'|'jam'|'sim'|null|undefined} bucket - Flow bucket.
|
||
* @returns {number} Pixels to add (0 under the normal profile / sim dots).
|
||
*/
|
||
export function presetSizeDelta(styleName, bucket) {
|
||
const entry = DOT_STYLE[trafficStyleProfile(styleName)]?.[bucket];
|
||
return entry ? entry.sizeDelta : 0;
|
||
}
|
||
|
||
/**
|
||
* Dark halo for a preset dot, or null for none. Why: NVG auto-gain pushes
|
||
* the whole scene near saturation, so a bright dot has almost no contrast
|
||
* against a bright road — a dark outline restores LOCAL contrast, which
|
||
* every luma-mapping shader preserves. Null under the normal profile and
|
||
* for sim dots (shipped dots never draw outlines).
|
||
* @param {string|null|undefined} styleName - Active style name.
|
||
* @param {'free'|'slow'|'jam'|'sim'|null|undefined} bucket - Flow bucket.
|
||
* @returns {{rgba:number[], width:number}|null} Outline spec or null.
|
||
*/
|
||
export function presetDotOutline(styleName, bucket) {
|
||
const entry = DOT_STYLE[trafficStyleProfile(styleName)]?.[bucket];
|
||
return entry?.outline || null;
|
||
}
|
||
|
||
/**
|
||
* Detection-overlay tier key for a traffic contact's flow bucket. The
|
||
* detection canvas composites ABOVE the post-FX chain, so tier colors are
|
||
* literal screen RGB in every preset — the brackets carry the congestion
|
||
* classification the in-scene dots can no longer encode under mono
|
||
* presets. Per-theme colors live in detection.js THEME_MAP `tiers`
|
||
* (veh_jam / veh_slow / veh_free / veh_nodata).
|
||
*
|
||
* Callers pass 'sim' for LIVE-mode roads TomTom has no data for; in
|
||
* keyless mode they must pass null so contacts keep the stock 'vehicle'
|
||
* tier — the keyless experience stays byte-identical.
|
||
*
|
||
* @param {'free'|'slow'|'jam'|'sim'|null|undefined} bucket - Flow bucket.
|
||
* @returns {string|null} Tier key, or null (no override → stock 'vehicle').
|
||
*/
|
||
export function trafficBucketTier(bucket) {
|
||
if (bucket === 'free' || bucket === 'slow' || bucket === 'jam')
|
||
return `veh_${bucket}`;
|
||
if (bucket === 'sim') return 'veh_nodata';
|
||
return null;
|
||
}
|