| 59 | } |
| 60 | |
| 61 | function paint(ctx, w, h) { |
| 62 | // Cream paper background + vignette. |
| 63 | ctx.fillStyle = theme.widget_base; |
| 64 | ctx.fillRect(0, 0, w, h); |
| 65 | |
| 66 | // Header. |
| 67 | ctx.fillStyle = theme.widget_text; |
| 68 | ctx.font = "bold 11px sans-serif"; |
| 69 | ctx.textAlign = "start"; |
| 70 | ctx.fillText("HEATMAP", 14, 18); |
| 71 | ctx.fillStyle = theme.placeholder_text; |
| 72 | ctx.font = "10px sans-serif"; |
| 73 | ctx.textAlign = "end"; |
| 74 | ctx.fillText(datasets.length + " channels / viridis", w - 14, 18); |
| 75 | ctx.fillStyle = theme.widget_border; |
| 76 | ctx.fillRect(14, 22, w - 28, 1); |
| 77 | |
| 78 | if (rows.length === 0 || datasets.length === 0) return; |
| 79 | |
| 80 | // Plot card frames the heatmap. Card padding leaves room for the |
| 81 | // colour scale on the right. |
| 82 | const padX = 14; |
| 83 | const padTop = 30; |
| 84 | const padBot = 30; |
| 85 | const scaleW = 22; |
| 86 | const scaleGap = 8; |
| 87 | const cardX = padX; |
| 88 | const cardY = padTop; |
| 89 | const cardW = w - padX * 2; |
| 90 | const cardH = h - padTop - padBot; |
| 91 | |
| 92 | ctx.fillStyle = theme.widget_border; |
| 93 | ctx.fillRect(cardX + 1, cardY + 2, cardW, cardH); |
| 94 | ctx.fillStyle = theme.widget_base; |
| 95 | ctx.fillRect(cardX, cardY, cardW, cardH); |
| 96 | |
| 97 | const plotX = cardX + 1; |
| 98 | const plotY = cardY + 1; |
| 99 | const plotW = cardW - scaleW - scaleGap - 2; |
| 100 | const plotH = cardH - 2; |
| 101 | |
| 102 | const cols = datasets.length; |
| 103 | const cellW = plotW / HISTORY; |
| 104 | const cellH = plotH / cols; |
| 105 | |
| 106 | for (let r = 0; r < rows.length; ++r) { |
| 107 | const row = rows[r]; |
| 108 | const x = plotX + r * cellW; |
| 109 | for (let c = 0; c < cols; ++c) { |
| 110 | ctx.fillStyle = viridis(row[c]); |
| 111 | ctx.fillRect(x, plotY + c * cellH, cellW + 1, cellH + 1); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // Card border on top. |
| 116 | ctx.strokeStyle = theme.widget_border; |
| 117 | ctx.lineWidth = 1; |
| 118 | ctx.strokeRect(cardX + 0.5, cardY + 0.5, cardW - 1, cardH - 1); |