From e3250a34da4bbb95d137664bb0fb12f11e87fe09 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Mon, 12 Oct 2026 21:54:43 +0200 Subject: [PATCH] cars: add time-based traffic profile with weekend and Sunday peaks --- config.js | 23 +++++++++++++- vendor/clouds3d.js | 76 +++++++++++++++++++++++++++++++++------------- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/config.js b/config.js index 50ca2a9..cccefaf 100644 --- a/config.js +++ b/config.js @@ -317,7 +317,28 @@ window.CONFIG = { spreadFloor: 0.30, // share that far side roads keep speed: 1.0, // flow speed multiplier density: 1.0, // vehicle density multiplier - brightness: 1.3 // vehicle light intensity + brightness: 1.3, // vehicle light intensity + // Time-based traffic profile. Values are multipliers around the + // configured density. Times are local scene-clock hours. + timeProfile: { + enabled: true, + weekday: 1.0, // Mon-Fri base traffic + saturday: 0.65, // weekend reduction + sunday: 0.55, // weekend reduction + morningPeak: 7.7, // rush-hour morning peak + morningWidth: 1.2, // gaussian sigma in hours + morningGain: 0.55, // +55 % at the peak + eveningPeak: 17.5, // rush-hour evening peak + eveningWidth: 1.4, + eveningGain: 0.75, // +75 % at the peak + fridayEveningGain: 0.35, + sundayEveningPeak: 18.5, + sundayEveningWidth: 1.8, + sundayEveningGain: 0.85, // commuter return traffic + nightReduction: 0.70 // deep-night traffic reduced to 30 % + // timeProfile: { enabled: false } + // timeProfile: { morningGain: 0.8, eveningGain: 1.0, nightReduction: 0.85 } + } // speed: 2.0, density: 0.6, brightness: 1.0 }, diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index d30777b..aa3ff87 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -3359,6 +3359,58 @@ var CAR_LANE_PX = CAR_TUN.lanePx || 2.6; var CAR_BRIGHT = CAR_TUN.brightness || 1.3; var CAR_SPEED = CAR_TUN.speed || 1; var CAR_DENS = CAR_TUN.density || 1; +var CAR_TIME_CFG = CAR_TUN.timeProfile || {}; +var CAR_TIME_ON = CAR_TIME_CFG.enabled !== false; +function carSmoothstep(a, b, x) { + var t = Math.max(0, Math.min(1, (x - a) / Math.max(1e-6, b - a))); + return t * t * (3 - 2 * t); +} +function carGaussian(hh, center, width) { + var d = hh - center; + if (d > 12) d -= 24; + if (d < -12) d += 24; + var w = Math.max(0.25, width); + return Math.exp(-(d * d) / (2 * w * w)); +} +function carWeekendFactor(dow, hh) { + var sat = (typeof CAR_TIME_CFG.saturday === 'number') ? CAR_TIME_CFG.saturday : 0.65; + var sun = (typeof CAR_TIME_CFG.sunday === 'number') ? CAR_TIME_CFG.sunday : 0.55; + var work = (typeof CAR_TIME_CFG.weekday === 'number') ? CAR_TIME_CFG.weekday : 1.0; + function wOf(d) { return d === 0 ? sun : (d === 6 ? sat : work); } + var f = wOf(dow); + if (hh >= 23) f = f + (wOf((dow + 1) % 7) - f) * (hh - 23); + else if (hh < 1) f = wOf((dow + 6) % 7) + (f - wOf((dow + 6) % 7)) * hh; + return Math.max(0.15, Math.min(1.5, f)); +} +function carRushFactor(hh, dow) { + if (!CAR_TIME_ON || typeof hh !== 'number') return 1; + var morningPeak = (typeof CAR_TIME_CFG.morningPeak === 'number') ? CAR_TIME_CFG.morningPeak : 7.7; + var morningWidth = (typeof CAR_TIME_CFG.morningWidth === 'number') ? CAR_TIME_CFG.morningWidth : 1.2; + var morningGain = (typeof CAR_TIME_CFG.morningGain === 'number') ? CAR_TIME_CFG.morningGain : 0.55; + var eveningPeak = (typeof CAR_TIME_CFG.eveningPeak === 'number') ? CAR_TIME_CFG.eveningPeak : 17.5; + var eveningWidth = (typeof CAR_TIME_CFG.eveningWidth === 'number') ? CAR_TIME_CFG.eveningWidth : 1.4; + var eveningGain = (typeof CAR_TIME_CFG.eveningGain === 'number') ? CAR_TIME_CFG.eveningGain : 0.75; + var nightReduction = (typeof CAR_TIME_CFG.nightReduction === 'number') ? CAR_TIME_CFG.nightReduction : 0.70; + var fridayGain = (typeof CAR_TIME_CFG.fridayEveningGain === 'number') ? CAR_TIME_CFG.fridayEveningGain : 0.35; + var sundayEveningPeak = (typeof CAR_TIME_CFG.sundayEveningPeak === 'number') ? CAR_TIME_CFG.sundayEveningPeak : 18.5; + var sundayEveningWidth = (typeof CAR_TIME_CFG.sundayEveningWidth === 'number') ? CAR_TIME_CFG.sundayEveningWidth : 1.8; + var sundayEveningGain = (typeof CAR_TIME_CFG.sundayEveningGain === 'number') ? CAR_TIME_CFG.sundayEveningGain : 0.85; + + var rush = 1; + rush += morningGain * carGaussian(hh, morningPeak, morningWidth); + rush += eveningGain * carGaussian(hh, eveningPeak, eveningWidth); + + if (dow === 5) rush += fridayGain * carGaussian(hh, 18.0, 1.6); + if (dow === 0) rush += sundayEveningGain * carGaussian(hh, sundayEveningPeak, sundayEveningWidth); + + var nightEdge = -1; + if (hh >= 22) nightEdge = carSmoothstep(22, 24, hh); + else if (hh < 5) nightEdge = 1 - carSmoothstep(3, 5, hh); + if (nightEdge >= 0) rush *= 1 - nightReduction * nightEdge; + + if (typeof dow === 'number') rush *= carWeekendFactor(dow, hh); + return Math.max(0.12, Math.min(2.2, rush)); +} // Gaussian spread of traffic around the motorway network: sigma (spreadKm) // and the floor that distant side roads keep (spreadFloor). var CAR_SIGMA = carMetres((CAR_TUN.spreadKm || 25) * 1000); @@ -6590,30 +6642,12 @@ window.clouds3dRender = function(target) { var carPxScale = Math.max(0.45, Math.min(1.25, gl.drawingBufferHeight / 1080)); var tCurve = Math.max(0, Math.min(1, (carNight - 0.10) / 0.40)); tCurve = tCurve * tCurve * (3 - 2 * tCurve); // smoothstep - // rush-hour shaping: busy evening/early morning, quiet 23:00-04:00 + // Time-based traffic profile: weekday rush peaks, quiet deep night, + // reduced weekend traffic, and a Sunday-evening commuter bump. var rush = 1; var tDay = window.__currentDayProgress; if (typeof tDay === 'number') { - var hh = tDay * 24; - var nightPos = hh >= 23 ? (hh - 23) / 5 : (hh < 4 ? (hh + 1) / 5 : -1); - if (nightPos >= 0) { - var edge = Math.min(1, Math.min(nightPos, 1 - nightPos) * 5); - edge = edge * edge * (3 - 2 * edge); - rush = 1 - 0.65 * edge; // dips to 0.35 - } - // weekend factor (window.__flightDow: 0=Sun..6=Sat), blended - // smoothly over the hour around midnight so nothing pops - var dow = window.__flightDow; - if (typeof dow === 'number') { - var wOf = function (d) { return d === 0 ? 0.55 : (d === 6 ? 0.65 : 1); }; - var fNow = wOf(dow); - if (hh >= 23) { - fNow = fNow + (wOf((dow + 1) % 7) - fNow) * (hh - 23); - } else if (hh < 1) { - fNow = wOf((dow + 6) % 7) + (fNow - wOf((dow + 6) % 7)) * hh; - } - rush *= fNow; - } + rush = carRushFactor(tDay * 24, window.__flightDow); } gl.useProgram(carProg); gl.uniformMatrix4fv(carUniforms.proj, false, proj);