From 4ac48b188a846d0969bb8775ecef7cdc17fe2c92 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 13 Oct 2026 07:14:19 +0200 Subject: [PATCH] car: cap road chain stitching to avoid Set overflow --- vendor/clouds3d.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index bca09a3..70bf798 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -4485,6 +4485,22 @@ function carMergeChains(polys, cfg) { eps.push(r); } } + // Safety valve: dense residential/tertiary cells can create far more + // endpoints than the join search can safely hold. In that case keep the + // roads as individual chains instead of trying to stitch everything. + var JOIN_ENDPOINT_LIMIT = 180000; + if (eps.length > JOIN_ENDPOINT_LIMIT) { + var isoOut = [], isoMax = 0; + for (i = 0; i < n; i++) { + if (polys[i] && polys[i].length > 1) { + isoOut.push(polys[i]); + var isoSpan = Math.max(polys[i][0].d, polys[i][polys[i].length - 1].d); + if (isoSpan > isoMax) isoMax = isoSpan; + } + } + return { out: isoOut, n: n, joins: 0, + maxChainKm: n ? isoMax * cfg.KM_PER_TILE : 0 }; + } // canonical numeric key for an endpoint pair (way index + end side) function pairKey(A, B) { var x = A.w * 2 + A.e, y = B.w * 2 + B.e; @@ -4510,25 +4526,36 @@ function carMergeChains(polys, cfg) { var T = TIERS[t]; var cands = candsByTier[t]; var seenPair = new Set(); - for (i = 0; i < eps.length; i++) { + var seenFull = false; + var SEEN_PAIR_LIMIT = 2000000; + var CAND_LIMIT = 1200000; + for (i = 0; i < eps.length && !seenFull; i++) { if ((i & 8191) === 8191) geoProgress('join tier ' + t, i, eps.length); var A2 = eps[i]; var gx = Math.floor(A2.x / NEAR), gz = Math.floor(A2.z / NEAR); - for (var ox = -1; ox <= 1; ox++) { - for (var oz = -1; oz <= 1; oz++) { + for (var ox = -1; ox <= 1 && !seenFull; ox++) { + for (var oz = -1; oz <= 1 && !seenFull; oz++) { var bucket = grid.get((gx + ox) + '_' + (gz + oz)); if (!bucket) continue; - for (j = 0; j < bucket.length; j++) { + for (j = 0; j < bucket.length && !seenFull; j++) { var B2 = bucket[j]; if (B2.w === A2.w) continue; var pk2 = pairKey(A2, B2); if (seenPair.has(pk2)) continue; + if (seenPair.size >= SEEN_PAIR_LIMIT) { + seenFull = true; + break; + } seenPair.add(pk2); var gm = geomFor(A2, B2); if (gm.dot <= T.dot) continue; if (gm.lat > T.lat) continue; // opposite carriageway: reject if (gm.along < T.alongLo || gm.along > T.alongHi) continue; cands.push({ a: A2, b: B2, s: gm.s }); + if (cands.length >= CAND_LIMIT) { + seenFull = true; + break; + } } } }