This commit is contained in:
89
www/map-behavior-zones.js
Normal file
89
www/map-behavior-zones.js
Normal file
@@ -0,0 +1,89 @@
|
||||
(() => {
|
||||
/**
|
||||
* Runtime hooks for Speed / Sound map zones (MiR §4.2.6.8–9).
|
||||
* Vector overlay only — consumed by motion controller / mission runner.
|
||||
*/
|
||||
const TYPES = {
|
||||
speed: "speed",
|
||||
sound: "sound",
|
||||
};
|
||||
|
||||
const BEHAVIOR_TYPES = new Set([TYPES.speed, TYPES.sound]);
|
||||
|
||||
const SPEED_MIN = 0.1;
|
||||
const SPEED_MAX = 1.5;
|
||||
const DEFAULT_SPEED_MPS = 0.8;
|
||||
|
||||
function pointInPolygon(px, py, points) {
|
||||
return window.MapPlannerZones?.pointInPolygon(px, py, points) || false;
|
||||
}
|
||||
|
||||
function clampSpeed(value) {
|
||||
const n = Number(value);
|
||||
if (!Number.isFinite(n)) return DEFAULT_SPEED_MPS;
|
||||
return Math.min(SPEED_MAX, Math.max(SPEED_MIN, n));
|
||||
}
|
||||
|
||||
function isBehaviorZoneType(type) {
|
||||
return BEHAVIOR_TYPES.has(type);
|
||||
}
|
||||
|
||||
function filterBehaviorZones(zones) {
|
||||
return (Array.isArray(zones) ? zones : []).filter((z) => isBehaviorZoneType(z?.type));
|
||||
}
|
||||
|
||||
/** Topmost behavior zones containing image pixel (newest wins for overlaps). */
|
||||
function zonesAtPoint(zones, px, py) {
|
||||
const list = Array.isArray(zones) ? zones : [];
|
||||
const hits = [];
|
||||
for (let i = list.length - 1; i >= 0; i--) {
|
||||
const z = list[i];
|
||||
if (!isBehaviorZoneType(z?.type) || !z.points?.length) continue;
|
||||
if (pointInPolygon(px, py, z.points)) hits.push(z);
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
function getSpeedLimit(zones, px, py) {
|
||||
const hit = zonesAtPoint(zones, px, py).find((z) => z.type === TYPES.speed);
|
||||
if (!hit) return null;
|
||||
return clampSpeed(hit.speed_mps);
|
||||
}
|
||||
|
||||
function getSoundAtPoint(zones, px, py) {
|
||||
const hit = zonesAtPoint(zones, px, py).find((z) => z.type === TYPES.sound);
|
||||
if (!hit?.sound_id) return null;
|
||||
return {
|
||||
sound_id: hit.sound_id,
|
||||
zone_id: hit.id,
|
||||
};
|
||||
}
|
||||
|
||||
function classifyPoint(zones, px, py) {
|
||||
const hits = zonesAtPoint(zones, px, py);
|
||||
const speedZone = hits.find((z) => z.type === TYPES.speed);
|
||||
const soundZone = hits.find((z) => z.type === TYPES.sound);
|
||||
return {
|
||||
speed_mps: speedZone ? clampSpeed(speedZone.speed_mps) : null,
|
||||
sound_id: soundZone?.sound_id || null,
|
||||
speed_zone: speedZone || null,
|
||||
sound_zone: soundZone || null,
|
||||
zones: hits,
|
||||
};
|
||||
}
|
||||
|
||||
window.MapBehaviorZones = {
|
||||
TYPES,
|
||||
BEHAVIOR_TYPES,
|
||||
SPEED_MIN,
|
||||
SPEED_MAX,
|
||||
DEFAULT_SPEED_MPS,
|
||||
clampSpeed,
|
||||
isBehaviorZoneType,
|
||||
filterBehaviorZones,
|
||||
zonesAtPoint,
|
||||
getSpeedLimit,
|
||||
getSoundAtPoint,
|
||||
classifyPoint,
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user