diff --git a/src/app.js b/src/app.js index bc4d68a..9aec655 100644 --- a/src/app.js +++ b/src/app.js @@ -1379,14 +1379,33 @@ target ? target.hi : 0, ...series.flatMap(s => s.pts.map(p => p.y)), ); - const { yMax, steps: ySteps } = niceAxisSleepHours(rawMax); + const { yMax } = niceAxisSleepHours(rawMax); + // The interesting detail sits near the goal, in the upper teens of hours, + // so the y scale is piecewise: everything up to BREAK shares a compressed + // slice of the height and the hours above it get all the rest. Linear + // again while the axis is still too short for a split to mean anything. + const BREAK = 10, LOW_FRAC = 0.26; + const split = yMax > BREAK + 2; + const frac = (v) => !split + ? v / yMax + : v <= BREAK + ? (v / BREAK) * LOW_FRAC + : LOW_FRAC + ((v - BREAK) / (yMax - BREAK)) * (1 - LOW_FRAC); const xOf = (hour) => ML + (hour / 24) * innerW; - const yOf = (v) => MT + innerH * (1 - v / yMax); + const yOf = (v) => MT + innerH * (1 - frac(v)); - // Label every 1h gridline; smaller text once the axis is dense so the - // labels stay apart. - const yLabelCls = ySteps > 16 ? "y-dense" : ""; + // Gridlines: every 1h in the stretched region above the break, every 2h + // in the compressed region below it (1h everywhere when linear). + const yTicks = []; + if (split) { + for (let v = 0; v < BREAK; v += 2) yTicks.push(v); + for (let v = BREAK; v <= yMax; v++) yTicks.push(v); + } else { + for (let v = 0; v <= yMax; v++) yTicks.push(v); + } + // Smaller text once the axis is dense so the labels stay apart. + const yLabelCls = yTicks.length > 16 ? "y-dense" : ""; const parts = []; // Age-based goal band, behind everything: today's projection should land @@ -1400,12 +1419,10 @@ `
Hours slept so far at each point of the day, against yesterday and the average over the picked chart window. The dashed tail continues today's line the way the average day usually plays out.
+Hours slept so far at each point of the day, against yesterday and the average over the picked chart window. The dashed tail continues today's line the way the average day usually plays out. The axis is stretched above 10h to give the hours around the goal more room.