// Hand-drawn SVG helpers. Output is built from numbers only, so it is safe as innerHTML. // Bigger chart for the end-of-term review: weekly samples over the whole term, // a gridline per year and per quarter of the value range. export function lineChart(values, { w = 260, h = 96, min = 0, max = 100, yearEvery = 52 } = {}) { const pad = 3; const n = values.length; const span = max - min || 1; const x = (i) => pad + (n > 1 ? (i / (n - 1)) * (w - 2 * pad) : 0); const y = (v) => h - pad - Math.min(1, Math.max(0, (v - min) / span)) * (h - 2 * pad); const grid = []; for (let i = yearEvery; i < n; i += yearEvery) grid.push(``); for (const f of [0.25, 0.5, 0.75]) grid.push(``); const pts = values.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" "); return ``; } // Small line chart on graph paper. `values` are plotted evenly left to right. export function sparkline(values, { w = 84, h = 24, min = 0, max = 100 } = {}) { const grid = []; for (let x = 12; x < w; x += 12) grid.push(``); for (const f of [0.25, 0.5, 0.75]) grid.push(``); let line = ""; const n = values.length; if (n > 1) { const span = max - min || 1; const pts = values.map((v, i) => { const x = 2 + (i / (n - 1)) * (w - 4); const y = h - 2 - Math.min(1, Math.max(0, (v - min) / span)) * (h - 4); return [x.toFixed(1), y.toFixed(1)]; }); const last = pts[n - 1]; line = `` + ``; } return ``; }