From c8d529392e488d66100dc0e7b2825c3e1be2081f Mon Sep 17 00:00:00 2001 From: Fraxle Date: Mon, 25 May 2026 16:40:12 +0100 Subject: [PATCH] 1.9.1.2 Uniform temperature shades --- assets/js/app.js | 73 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index a1d46fa..1ad65b4 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -570,20 +570,47 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC const fmt = (v, dp = 1) => v == null ? '—' : (showDecimals ? v.toFixed(dp) : String(Math.round(v))); const u = (unit) => showUnits ? unit : ''; // Returns a background tint based on temperature value - all share the same - // Thermal Stress Bands - solid colours matching the legend exactly. - // All band-coloured cells use the full bg/fg from UTCI_BANDS. - const tempBg = (t) => { + // Continuous temperature colour scale — used for all temp columns + // (air, dew, indoor, vehicle, concrete, soil, UTCI, SunSoak…). + // Fully opaque, richly saturated stops so every degree is distinct. + const airTempBg = (t) => { if (t == null) return 'transparent'; - const band = utciCategory(t); - return band ? bandGradient(band.bg, band.darkenAmt) : 'transparent'; + // [temp °C, [r,g,b]] + const stops = [ + [-10, [ 90, 155, 220]], + [ 0, [140, 195, 235]], + [ 10, [155, 215, 195]], + [ 16, [140, 210, 140]], + [ 20, [195, 225, 110]], + [ 24, [240, 225, 80]], + [ 28, [250, 175, 65]], + [ 32, [240, 120, 55]], + [ 36, [220, 70, 50]], + [ 40, [185, 30, 30]], + [ 50, [130, 0, 20]], + ]; + const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v)); + let i = stops.findIndex(s => t < s[0]); + if (i === -1) i = stops.length; + const baseRgb = i === 0 ? stops[0][1] + : i >= stops.length ? stops[stops.length-1][1] + : (() => { + const [t0, c0] = stops[i-1]; + const [t1, c1] = stops[i]; + const x = clamp((t - t0) / (t1 - t0), 0, 1); + const lerp = (a, b) => Math.round(a + (b - a) * x); + return [lerp(c0[0],c1[0]), lerp(c0[1],c1[1]), lerp(c0[2],c1[2])]; + })(); + // Blend base colour 25% toward white before applying the sweep. + const [r,g,b] = baseRgb.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66))); + const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.30)); + const darken = (c) => Math.round(c * 0.93); + const light = `rgb(${lighten(r)},${lighten(g)},${lighten(b)})`; + const mid = `rgb(${r},${g},${b})`; + const dark = `rgb(${darken(r)},${darken(g)},${darken(b)})`; + return `linear-gradient(135deg, ${light} 0%, ${mid} 60%, ${dark} 100%)`; }; - const tempBgStrong = tempBg; - const tempFontColor = (t) => { - if (t == null) return '#1a1200'; - const band = utciCategory(t); - return band ? band.fg : '#1a1200'; - }; - const tempFontColorStrong = tempFontColor; + const airTempFontColor = () => '#1a1a1a'; // Returns any extra band styles (fontWeight, textShadow) for a given temp. const bandExtras = (t) => { const band = utciCategory(t); @@ -638,19 +665,19 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC ${visibleCols.utciP && html` - + ${fmt(r.utciAdj)}${u('°C')} `} ${visibleCols.vehicleT && html` - + ${fmt(r.vehicleT)}${u('°C')} `} ${indoorMode === 'on' && !indoorManaged && html` - + ${fmt(r.indoorT)}${u('°C')} `} ${indoorMode === 'on' && indoorManaged && html` - + ${fmt(r.managedT)}${u('°C')} `} ${visibleCols.burn && html` @@ -658,7 +685,7 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC ${burnLabel(burnMins)} `} ${visibleCols.utci && html` - + ${fmt(r.utci)}${u('°C')} `} ${visibleCols.delta && html` @@ -668,20 +695,20 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC }}> ${delta > 0 ? '+' : ''}${fmt(delta)}${u('°C')} `} - ${visibleCols.tmrt && html`${fmt(r.Tmrt)}${u('°C')}`} + ${visibleCols.tmrt && html`${fmt(r.Tmrt)}${u('°C')}`} ${visibleCols.concreteT && html` - + ${fmt(r.concreteT)}${u('°C')} `} ${visibleCols.soilT && html` - ${fmt(r.soilT0)}${u('°C')}`} + ${fmt(r.soilT0)}${u('°C')}`} ${visibleCols.soilT6 && html` - ${fmt(r.soilT6)}${u('°C')}`} + ${fmt(r.soilT6)}${u('°C')}`} ${visibleCols.soilM && html` ${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'}`} - ${visibleCols.air && html`${fmt(r.Ta)}${u('°C')}`} + ${visibleCols.air && html`${fmt(r.Ta)}${u('°C')}`} ${visibleCols.rh && html`${Math.round(r.RH)}${u('%')}`} - ${visibleCols.dew && html`${fmt(r.dew)}${u('°C')}`} + ${visibleCols.dew && html`${fmt(r.dew)}${u('°C')}`} ${visibleCols.precip && html` 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}>