diff --git a/background.html b/background.html index ec6f974..abdfb92 100644 --- a/background.html +++ b/background.html @@ -803,6 +803,20 @@ window.addEventListener('resize', scheduleFitInfoPanel); backgroundColor: hexToInt(ASH.void), speed: 2.5 }, + rain: { + skyColor: hexToInt(ASH.steel), + cloudColor: hexToInt(ASH.panel), + lightColor: hexToInt(ASH.bone), + backgroundColor: hexToInt(ASH.void), + speed: 1.8 + }, + snow: { + skyColor: hexToInt(ASH.blue), + cloudColor: hexToInt(ASH.chalk), + lightColor: hexToInt(ASH.chalkLight), + backgroundColor: hexToInt(ASH.steel), + speed: 0.7 + }, night: { skyColor: hexToInt(ASH.void), cloudColor: hexToInt(ASH.raised), @@ -1444,6 +1458,22 @@ window.weatherThemes = { backgroundColor: hexToInt(ASH.panel), speed: 2.5 }, + rain: { + // Wet grey overcast, faster cloud motion + skyColor: hexToInt(ASH.steel), + cloudColor: hexToInt(ASH.stone), + lightColor: hexToInt(ASH.bone), + backgroundColor: hexToInt(ASH.panel), + speed: 1.8 + }, + snow: { + // Cold pale overcast, slow drifting cloud field + skyColor: hexToInt(ASH.blue), + cloudColor: hexToInt(ASH.boneLight), + lightColor: hexToInt(ASH.chalk), + backgroundColor: hexToInt(ASH.steel), + speed: 0.7 + }, night: { // Very dark sky, moon-lit clouds skyColor: hexToInt(ASH.void), @@ -1458,7 +1488,8 @@ window.weatherThemes = { if (code === 0) return 'sunny'; if (code >= 1 && code <= 3) return 'cloudy'; if (code >= 45 && code <= 48) return 'cloudy'; - if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82) || (code >= 71 && code <= 77) || (code >= 85 && code <= 86)) return 'stormy'; + if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82)) return 'rain'; + if ((code >= 71 && code <= 77) || (code >= 85 && code <= 86)) return 'snow'; if (code >= 95) return 'stormy'; return 'cloudy'; } @@ -1497,19 +1528,64 @@ window.weatherThemes = { // Cycles through a list of weather states automatically. Useful // for A/B testing the scene against every theme without // waiting on the open-meteo API. - var simWeathers = ['sunny', 'cloudy', 'stormy', 'night']; + var SIM_CFG = window.CONFIG || {}; + var simWeathers = (Array.isArray(SIM_CFG.weatherSimStates) && SIM_CFG.weatherSimStates.length) + ? SIM_CFG.weatherSimStates.slice() + : ['sunny', 'cloudy', 'rain', 'snow', 'stormy', 'night']; var simIndex = 0; var simTimer = null; var __weatherSimActive = false; + function weatherSimProfileFor(name) { + var profiles = SIM_CFG.weatherSimProfile || {}; + var p = profiles[name] || {}; + var defaults = { + sunny: { code: 0, tempC: 18, humidity: 45, precipMm: 0.0, snowMm: 0.0, windKmh: 8, windDirDeg: 240 }, + cloudy: { code: 3, tempC: 14, humidity: 70, precipMm: 0.0, snowMm: 0.0, windKmh: 14, windDirDeg: 260 }, + rain: { code: 61, tempC: 10, humidity: 92, precipMm: 1.2, snowMm: 0.0, windKmh: 22, windDirDeg: 270 }, + snow: { code: 73, tempC: -2, humidity: 85, precipMm: 0.4, snowMm: 0.8, windKmh: 12, windDirDeg: 300 }, + stormy: { code: 95, tempC: 16, humidity: 88, precipMm: 2.5, snowMm: 0.0, windKmh: 35, windDirDeg: 250 }, + night: { code: 0, tempC: 8, humidity: 65, precipMm: 0.0, snowMm: 0.0, windKmh: 6, windDirDeg: 220 } + }; + var d = defaults[name] || defaults.cloudy; + return { + code: Number(p.code !== undefined ? p.code : d.code), + tempC: Number(p.tempC !== undefined ? p.tempC : d.tempC), + humidity: Number(p.humidity !== undefined ? p.humidity : d.humidity), + precipMm: Number(p.precipMm !== undefined ? p.precipMm : d.precipMm), + snowMm: Number(p.snowMm !== undefined ? p.snowMm : d.snowMm), + windKmh: Number(p.windKmh !== undefined ? p.windKmh : d.windKmh), + windDirDeg: Number(p.windDirDeg !== undefined ? p.windDirDeg : d.windDirDeg) + }; + } + + function applyWeatherSimState(name) { + var p = weatherSimProfileFor(name); + currentWeatherCode = p.code; + window.__currentWeather = { + code: p.code, + tempC: p.tempC, + humidity: p.humidity, + precipMm: p.precipMm, + snowMm: p.snowMm, + windKmh: p.windKmh, + windDirDeg: p.windDirDeg + }; + window.__currentWind = { + speedKmh: p.windKmh, + dirDeg: p.windDirDeg + }; + setWeatherTheme(name); + } + function startWeatherSim(intervalMs) { stopWeatherSim(); var step = intervalMs || (window.CONFIG && window.CONFIG.weatherSimInterval) || 30000; simIndex = 0; - setWeatherTheme(simWeathers[simIndex]); + applyWeatherSimState(simWeathers[simIndex]); simTimer = setInterval(function() { simIndex = (simIndex + 1) % simWeathers.length; - setWeatherTheme(simWeathers[simIndex]); + applyWeatherSimState(simWeathers[simIndex]); }, step); __weatherSimActive = true; } @@ -2229,6 +2305,33 @@ window.weatherThemes = { var now = sceneNow(); // virtual calendar: follows the same // clock the sky and the clock hands use var current = weatherData.current; + if (__weatherSimActive) { + var simState = currentWeatherThemeName || simWeathers[simIndex]; + var simProf = weatherSimProfileFor(simState); + current = { + temperature_2m: simProf.tempC, + relative_humidity_2m: simProf.humidity, + weather_code: simProf.code, + wind_speed_10m: simProf.windKmh, + wind_direction_10m: simProf.windDirDeg, + precipitation: simProf.precipMm, + snowfall: simProf.snowMm + }; + currentWeatherCode = simProf.code; + window.__currentWeather = { + code: simProf.code, + tempC: simProf.tempC, + humidity: simProf.humidity, + precipMm: simProf.precipMm, + snowMm: simProf.snowMm, + windKmh: simProf.windKmh, + windDirDeg: simProf.windDirDeg + }; + window.__currentWind = { + speedKmh: simProf.windKmh, + dirDeg: simProf.windDirDeg + }; + } var hourly = weatherData.hourly; var daily = weatherData.daily; @@ -2253,7 +2356,7 @@ window.weatherThemes = { // button has manual control) var curTemp = current.temperature_2m; var curCode = current.weather_code; - if (SIM_ACTIVE) { + if (SIM_ACTIVE && !__weatherSimActive) { var curIdx = findHourlyIndex(eff, hourly.time); curTemp = hourly.temperature_2m[curIdx]; curCode = hourly.weather_code[curIdx]; @@ -2285,7 +2388,7 @@ window.weatherThemes = { : Math.round(curTemp) + '\u00b0C'; var curWindSpeed = current.wind_speed_10m; var curWindDir = current.wind_direction_10m; - if (SIM_ACTIVE && hourly.wind_speed_10m && hourly.wind_direction_10m) { + if (SIM_ACTIVE && !__weatherSimActive && hourly.wind_speed_10m && hourly.wind_direction_10m) { var curIdx = findHourlyIndex(eff, hourly.time); curWindSpeed = hourly.wind_speed_10m[curIdx]; curWindDir = hourly.wind_direction_10m[curIdx]; diff --git a/config.js b/config.js index 58b1e66..7bbb885 100644 --- a/config.js +++ b/config.js @@ -101,9 +101,22 @@ window.CONFIG = { // false = live Open-Meteo conditions (also used for the theme choice). // true = cycle sunny -> cloudy -> stormy -> night, useful to compare // all weather looks without waiting on the API. - weatherSim: true, // test mode: cycles sunny/cloudy/stormy/night + weatherSim: true, // test mode: cycles configured weather states weatherSimInterval: 15000, // ms per weather step when weatherSim is true // weatherSim: false, weatherSimInterval: 30000 + // Synthetic weather states used by weatherSim. Each state sets theme, + // Open-Meteo-like weather_code, temperature, humidity, precipitation and wind. + weatherSimStates: ['sunny', 'cloudy', 'rain', 'snow', 'stormy', 'night'], + weatherSimProfile: { + sunny: { code: 0, tempC: 18, humidity: 45, precipMm: 0.0, snowMm: 0.0, windKmh: 8, windDirDeg: 240 }, + cloudy: { code: 3, tempC: 14, humidity: 70, precipMm: 0.0, snowMm: 0.0, windKmh: 14, windDirDeg: 260 }, + rain: { code: 61, tempC: 10, humidity: 92, precipMm: 1.2, snowMm: 0.0, windKmh: 22, windDirDeg: 270 }, + snow: { code: 73, tempC: -2, humidity: 85, precipMm: 0.4, snowMm: 0.8, windKmh: 12, windDirDeg: 300 }, + stormy: { code: 95, tempC: 16, humidity: 88, precipMm: 2.5, snowMm: 0.0, windKmh: 35, windDirDeg: 250 }, + night: { code: 0, tempC: 8, humidity: 65, precipMm: 0.0, snowMm: 0.0, windKmh: 6, windDirDeg: 220 } + // weatherSimStates: ['sunny', 'rain', 'stormy'] + // weatherSimProfile: { rain: { code: 80, tempC: 9, precipMm: 2.0, windKmh: 28 } } + }, // ------------------------------------------------------------------ // Weather data @@ -183,7 +196,7 @@ window.CONFIG = { // only ever reports sunny/cloudy/stormy, so the deck keeps that // coverage through the night as well - 'night' is used by the weather // simulation (weatherSim: true) alone. - density: { sunny: 0.25, cloudy: 0.50, stormy: 0.80 }, + density: { sunny: 0.25, cloudy: 0.50, rain: 0.75, snow: 0.65, stormy: 0.80 }, // 'night' intentionally has no own density: it follows the last // non-night weather density (sunny/cloudy/stormy). // Cloud motion from Open-Meteo wind. Open-Meteo reports wind FROM @@ -197,7 +210,7 @@ window.CONFIG = { // wind: { speedScale: 0 } // static clouds // wind: { speedScale: 140, maxSpeed: 0.12 } // very visible motion } - // density: { sunny: 0.05, cloudy: 0.9, stormy: 1.0 } + // density: { sunny: 0.05, cloudy: 0.9, rain: 0.95, snow: 0.85, stormy: 1.0 } }, // ------------------------------------------------------------------ @@ -538,7 +551,7 @@ window.CONFIG = { // time, 1440 = one simulated day per minute. Clock hands, digital readout, // weather and sky all use the same clock. Hash overrides: #at=... #speed=... day: { - startAt: '2026-10-12T18:30', // test clock: evening, then accelerated + startAt: '2026-01-15T18:30', // test clock: winter evening, then accelerated speed: 1440 // one simulated day per minute // startAt: null, speed: 1 // real time }, diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index aa3ff87..1b0e245 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -5973,9 +5973,11 @@ function hex(rgb) { var WEATHER_DENSITY = { sunny: 0.25, cloudy: 0.50, - // rain and storm both map to 'stormy' (open-meteo codes 51-67, 71-77, - // 80-86, 95+). Full 1.0 packed the deck so hard that the march showed - // its own layering, so the top end is capped below overcast. + rain: 0.75, + snow: 0.65, + // Thunder/rain/storm codes are split into rain/snow/stormy themes. + // Full 1.0 packed the deck so hard that the march showed its own + // layering, so the top end is capped below overcast. stormy: 0.80 }; var LAST_NON_NIGHT_CLOUD_DENSITY = WEATHER_DENSITY.cloudy;