From f06c50409ae74df06b1d4a23ac393676310feb3b Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 13 Oct 2026 07:31:29 +0200 Subject: [PATCH] emergency: select routes that pass near the start city --- vendor/clouds3d.js | 67 +++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 7fb270c..6a42635 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -3549,15 +3549,35 @@ function emergencyCollectCandidates(net, netKey, startCity, minKm, maxKm, nearLi var lastX = net.pts[last], lastZ = net.pts[last + 1], lastD = net.pts[last + 2]; var len = Math.max(0, lastD - firstD); var lenKm = len * KM_PER_TILE; - var dStart = Math.hypot(firstX - startCity.x, firstZ - startCity.z); - var dEnd = Math.hypot(lastX - startCity.x, lastZ - startCity.z); - var near = Math.min(dStart, dEnd); - if (near <= nearLimit && lenKm >= minKm && lenKm <= maxKm) { - out.push({ - net: net, netKey: netKey, ci: ci, len: len, lenKm: lenKm, near: near, - score: Math.abs(lenKm - targetKm) + near * 18, - reverse: dEnd < dStart - }); + if (lenKm >= minKm && lenKm <= maxKm) { + var near = Math.hypot(firstX - startCity.x, firstZ - startCity.z); + var startD = 0; + var dEnd = Math.hypot(lastX - startCity.x, lastZ - startCity.z); + if (dEnd < near) { + near = dEnd; + startD = len; + } + // A long road can pass close to the start city even when both + // of its endpoints are far away. Sample the chain coarsely. + if (near > nearLimit && n > 4) { + var step = Math.max(1, Math.floor(n / 16)); + for (var q = step; q < n - step; q += step) { + var off = k + q * 3; + var dd = Math.hypot(net.pts[off] - startCity.x, net.pts[off + 1] - startCity.z); + if (dd < near) { + near = dd; + startD = Math.max(0, Math.min(len, net.pts[off + 2] - firstD)); + } + } + } + if (near <= nearLimit) { + out.push({ + net: net, netKey: netKey, ci: ci, len: len, lenKm: lenKm, + near: near, startD: startD, + score: Math.abs(lenKm - targetKm) + near * 18, + reverse: false + }); + } } k += n * 3; } @@ -3620,11 +3640,7 @@ function selectEmergencyRoutes() { if (used[usedKey]) continue; used[usedKey] = true; var route = emergencyChainAt(cand.net, cand.ci); - if (cand.reverse) { - route.reverse(); - var maxD = route.length ? route[0].d : 0; - for (var rp = 0; rp < route.length; rp++) route[rp].d = maxD - route[rp].d; - } else if (route.length) { + if (route.length) { var base = route[0].d; for (var rp2 = 0; rp2 < route.length; rp2++) route[rp2].d -= base; } @@ -3637,12 +3653,20 @@ function selectEmergencyRoutes() { }); continue; } - emergencyRoutes.push({ pts: route, len: len, lenKm: len * KM_PER_TILE, nearKm: cand.near * KM_PER_TILE }); + emergencyRoutes.push({ + pts: route, len: len, lenKm: len * KM_PER_TILE, + nearKm: cand.near * KM_PER_TILE, + startD: Math.max(0, Math.min(len, cand.startD || 0)) + }); } for (var v = 0; v < emergencyRoutes.length; v++) { + var rr = emergencyRoutes[v]; + var spread = rr.len * 0.18; + var d = (rr.startD || 0) + (Math.random() - 0.5) * spread; + d = Math.max(0, Math.min(rr.len, d)); emergencyVehicles.push({ route: v, - d: emergencyRoutes[v].len * (0.05 + 0.85 * Math.random()), + d: d, dir: Math.random() < 0.5 ? 1 : -1, seed: Math.random() }); @@ -3662,12 +3686,19 @@ function selectEmergencyRoutes() { candidateSample: top.slice(0, 5).map(function (c) { return { netKey: c.netKey, ci: c.ci, lenKm: +c.lenKm.toFixed(1), - nearKm: +(c.near * KM_PER_TILE).toFixed(1), reverse: c.reverse + nearKm: +(c.near * KM_PER_TILE).toFixed(1), + startKm: +((c.startD || 0) * KM_PER_TILE).toFixed(1) }; }), routeFailures: routeFailures.slice(0, 5), networks: nets.map(function (n) { return { key: n.key, chains: n.net.cnts.length }; }), - routes: emergencyRoutes.map(function (r) { return { km: +r.lenKm.toFixed(1), nearKm: +r.nearKm.toFixed(1) }; }) + routes: emergencyRoutes.map(function (r) { + return { + km: +r.lenKm.toFixed(1), + nearKm: +r.nearKm.toFixed(1), + startKm: +(r.startD * KM_PER_TILE).toFixed(1) + }; + }) }; }