Project today's end-of-day sleep total on the Sleep trend chart
A dashed tail continues today's line from now to midnight by adding the increments the N-day average curve makes over the same stretch, so the projection follows the usual daily rhythm instead of extrapolating the current rate (which overshoots right after a long night). The legend shows the projected total; with no history there's no average and no projection.
This commit is contained in:
+29
-2
@@ -1318,7 +1318,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
return { today, yesterday, avg, avgDays };
|
||||
// Where today is likely to end up: continue today's line by adding what
|
||||
// the average day typically adds between now and midnight. That respects
|
||||
// the time-of-day rhythm (night sleep, nap clusters), unlike a linear
|
||||
// rate extrapolation, which overshoots wildly just after a long night.
|
||||
// No history → no average → no projection.
|
||||
let projected = null;
|
||||
if (avg) {
|
||||
const nowPt = today[today.length - 1];
|
||||
const avgAt = (x) => {
|
||||
const lo = Math.floor(x);
|
||||
const hi = Math.min(24, lo + 1);
|
||||
return avg[lo].y + (avg[hi].y - avg[lo].y) * (x - lo);
|
||||
};
|
||||
projected = [{ x: nowPt.x, y: nowPt.y }];
|
||||
for (let h = Math.ceil(nowPt.x); h <= 24; h++) {
|
||||
if (h <= nowPt.x) continue; // now landing exactly on an hour boundary
|
||||
projected.push({ x: h, y: nowPt.y + avgAt(h) - avgAt(nowPt.x) });
|
||||
}
|
||||
}
|
||||
|
||||
return { today, yesterday, avg, avgDays, projected };
|
||||
}
|
||||
|
||||
function drawSleepTrendChart(curves) {
|
||||
@@ -1330,9 +1350,10 @@
|
||||
const innerH = H - MT - MB;
|
||||
|
||||
const series = [
|
||||
// Reference lines first so today draws on top of them.
|
||||
// Reference lines first so today (and its projected tail) draw on top.
|
||||
{ pts: curves.avg, cls: "trend-avg", label: `${curves.avgDays}-day average` },
|
||||
{ pts: curves.yesterday, cls: "trend-yesterday", label: "Yesterday" },
|
||||
{ pts: curves.projected, cls: "trend-projected", label: "Projected end of day" },
|
||||
{ pts: curves.today, cls: "trend-today", label: "Today" },
|
||||
].filter(s => s.pts && s.pts.length > 1);
|
||||
|
||||
@@ -1377,9 +1398,15 @@
|
||||
const yLegend = document.getElementById("legend-trend-yesterday");
|
||||
const aLegend = document.getElementById("legend-trend-avg");
|
||||
const aText = document.getElementById("legend-trend-avg-text");
|
||||
const pLegend = document.getElementById("legend-trend-projected");
|
||||
const pText = document.getElementById("legend-trend-projected-text");
|
||||
if (yLegend) yLegend.hidden = !curves.yesterday;
|
||||
if (aLegend) aLegend.hidden = !curves.avg;
|
||||
if (aText) aText.textContent = `${curves.avgDays}-day avg`;
|
||||
if (pLegend) pLegend.hidden = !curves.projected;
|
||||
if (pText && curves.projected) {
|
||||
pText.textContent = `Projected ~${curves.projected[curves.projected.length - 1].y.toFixed(1)}h`;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- weight ----------
|
||||
|
||||
Reference in New Issue
Block a user