(() => { /** * 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, }; })();