From b1f78f2cddbcf1e137acb13c31b46e06d3012c58 Mon Sep 17 00:00:00 2001
From: Paul Oliver <contact@pauloliver.dev>
Date: Sat, 11 Apr 2026 14:07:37 +0200
Subject: Adds heatmaps

---
 data/vue/Plot.vue | 104 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 65 insertions(+), 39 deletions(-)

(limited to 'data/vue/Plot.vue')

diff --git a/data/vue/Plot.vue b/data/vue/Plot.vue
index 3c08d73..5bbc89a 100644
--- a/data/vue/Plot.vue
+++ b/data/vue/Plot.vue
@@ -11,7 +11,7 @@
 <script setup>
 import { defineProps, inject, onMounted, ref, useTemplateRef, watch } from 'vue'
 
-const props = defineProps({ section: String, name: String })
+const props = defineProps({ is_heatmap: Boolean, section: String, name: String })
 
 const maximized = ref(false)
 
@@ -19,8 +19,12 @@ const plot_ref = useTemplateRef('plot_ref')
 const plot_container = useTemplateRef('plot_container')
 
 const plots = inject('plots')
+const heatmaps = inject('heatmaps')
 const entries = inject('entries')
 const x_axis = inject('x_axis')
+const hm_left = inject('hm_left')
+const hm_px_count = inject('hm_px_count')
+const hm_px_pow = inject('hm_px_pow')
 const data = inject('data')
 
 const plot_toggle_maximize = () => {
@@ -34,32 +38,32 @@ const prevent_plotly_buttons_tab_focus = () => {
   focusableElements.forEach(elem => elem.setAttribute('tabindex', '-1'))
 }
 
-onMounted(() => {
-  const plot_config = plots.value[props.section][props.name]
+const heatmap_init = [{ colorscale: 'Electric', type: 'heatmap', x: [], y: [], z: [] }]
 
-  switch (plot_config.type) {
-  case 'lines':
-    var data_defs = { mode: 'lines', line: { width: 1 }}
-    break
-  case 'stack':
-    var data_defs = { mode: 'lines', line: { width: 1 }, stackgroup: 'stackgroup' }
-    break
-  case 'stack_percent':
-    var data_defs = { mode: 'lines', line: { width: 1 }, stackgroup: 'stackgroup', groupnorm: 'percent' }
-    break
-  }
+const plot_init = () => {
+    const plot_config = plots.value[props.section][props.name]
+    const plot_defs = { mode: 'lines', line: { width: 1 }, x: [], y: [] }
+
+    switch (plot_config.type) {
+    case 'lines':
+      return Array.from(plot_config.cols, col => ({ ...plot_defs, name: col }))
+    case 'stack':
+      return Array.from(plot_config.cols, col => ({ ...plot_defs, stackgroup: 'sg', name: col }))
+    case 'stack_percent':
+      return Array.from(plot_config.cols, col => ({ ...plot_defs, stackgroup: 'sg', groupnorm: 'percent', name: col }))
+    }
+}
 
-  const columns = plot_config.cols
-  const data = Array.from(columns, column => ({ ...data_defs, x: [], y: [], name: column }))
-
-  Plotly.newPlot(plot_ref.value, data, {
-    legend: { font: { color: '#586e75', family: 'monospace' }, maxheight: 100, orientation: 'h' },
-    margin: { b: 32, l: 32, r: 32, t: 32 },
-    paper_bgcolor: '#002b36',
-    plot_bgcolor: '#002b36',
-    title: { font: { color: '#586e75' }, text: props.name, x: 0, xref: 'paper' },
-    xaxis: { gridcolor: '#073642', tickfont: { color: '#586e75' }, zerolinecolor: '#586e75' },
-    yaxis: { gridcolor: '#073642', tickfont: { color: '#586e75' }, zerolinecolor: '#586e75' },
+onMounted(() => {
+  Plotly.newPlot(plot_ref.value, props.is_heatmap ? heatmap_init : plot_init(), {
+    font: { color: 'gray', family: 'monospace' },
+    legend: { maxheight: 100, orientation: 'h' },
+    margin: { b: 48, l: 48, r: 48, t: 48 },
+    paper_bgcolor: 'black',
+    plot_bgcolor: 'black',
+    title: { font: { size: 16 }, text: props.name, x: 0, xref: 'paper' },
+    xaxis: { gridcolor: '#111', tickfont: { color: 'gray' }, zerolinecolor: 'gray' },
+    yaxis: { gridcolor: '#111', tickfont: { color: 'gray' }, zerolinecolor: 'gray' },
   }, {
     displayModeBar: true,
     responsive: true,
@@ -68,32 +72,54 @@ onMounted(() => {
   prevent_plotly_buttons_tab_focus()
 })
 
-watch(data, new_data => {
+const update_plot = new_data => {
   const plot_config = plots.value[props.section][props.name]
-  const columns = plot_config.cols
-  const column_count = columns.length
-  const table_data = new_data.values[plot_config.table]
-  const traces = [...Array(column_count).keys()]
-  const xs = Array(column_count).fill(table_data.map(elem => elem[x_axis.value]))
-  const ys = columns.map(column => table_data.map(elem => elem[column]))
+  const cols = plot_config.cols
+  const cols_count = cols.length
+  const table_data = new_data.plot_values[plot_config.table]
+  const traces = [...Array(cols_count).keys()]
+  const xs = Array(cols_count).fill(table_data.map(elem => elem[x_axis.value]))
+  const ys = cols.map(column => table_data.map(elem => elem[column]))
 
   // Clear traces
   if (new_data.redraw) {
     const restyle = {
-      x: Array.from(columns, () => []),
-      y: Array.from(columns, () => []),
+      x: Array.from(cols, () => []),
+      y: Array.from(cols, () => []),
     }
 
     Plotly.restyle(plot_ref.value, restyle)
   }
 
   Plotly.extendTraces(plot_ref.value, { x: xs, y: ys }, traces, entries.value)
-})
+}
+
+const update_heatmap = new_data => {
+  const heatmap_config = heatmaps.value[props.section][props.name]
+  const table_data = new_data.heatmap_values[heatmap_config.table]
+  const ys = [table_data.map(elem => elem[x_axis.value])]
+  const zs = [table_data.map(elem => elem.eva_render.split(' ').map(str => Number('0x' + str)))]
+
+  if (new_data.redraw) {
+    const px_size = Math.pow(2, Number(hm_px_pow.value))
+    const restyle = {
+      x: [Array.from(Array(Number(hm_px_count.value)).keys()).map(i => Number(hm_left.value) + i * px_size)],
+      y: [[]],
+      z: [[]],
+    }
+
+    Plotly.restyle(plot_ref.value, restyle)
+  }
+
+  Plotly.extendTraces(plot_ref.value, { y: ys, z: zs }, [0], entries.value)
+}
+
+watch(data, props.is_heatmap ? update_heatmap : update_plot)
 </script>
 
 <style>
 .plot_container {
-  background-color: #002b36;
+  background-color: black;
   display: inline-block;
   width: 100%;
 }
@@ -113,9 +139,9 @@ watch(data, new_data => {
 }
 
 .plot_button {
-  background-color: #002b36;
-  border: 1.5px solid #586e75;
-  color: #586e75;
+  background-color: black;
+  border: 1.5px solid gray;
+  color: gray;
   cursor: pointer;
   font-family: monospace;
   font-size: 18px;
-- 
cgit v1.2.1

