diff --git a/lib/www/client/source/src/components/graph-guns-heatmap.vue b/lib/www/client/source/src/components/graph-guns-heatmap.vue index b43ca55..ebb0a71 100644 --- a/lib/www/client/source/src/components/graph-guns-heatmap.vue +++ b/lib/www/client/source/src/components/graph-guns-heatmap.vue @@ -34,6 +34,7 @@ import { mapActions, mapGetters } from 'vuex'; import unpack from '@/lib/unpack.js'; import * as aes from '@/lib/graphs/aesthetics.js'; + export default { name: 'DougalGraphGunsDepth', @@ -48,6 +49,7 @@ export default { // TODO: aspects should be a prop aspects: [ "Mode", "Detect", "Autofire", "Aimpoint", "Firetime", "Delay", + "Delta", "Depth", "Pressure", "Volume", "Filltime" ] }; @@ -91,78 +93,181 @@ export default { console.log("missing data"); return; } - + function transform (data, aspects=["Depth", "Pressure"]) { - const labels = [ - "Mode", "Detect", "Autofire", "Aimpoint", "Firetime", "Delay", - "Depth", "Pressure", "Volume", "Filltime" - ]; - const indices = [ - 3, 4, 5, 7, 8, 9, 10, 11, 12, 13 - ]; - const conversions = [ + + const facets = [ // Mode - (v) => { - switch (v) { - case "A": - return 1; - case "M": - return 2; - case "O": - return 0; - case "D": - return 3; + { + params: { + name: "Mode", + hovertemplate: "SP%{x}
%{y}
%{text}", + }, + + text: [ "Off", "Auto", "Manual", "Disabled" ], + + conversion: (gun, shot) => { + switch (gun[3]) { + case "A": + return 1; + case "M": + return 2; + case "O": + return 0; + case "D": + return 3; + } } }, // Detect - (v) => { - switch (v) { - case "P": - return 1; - case "Z": - return 0; - case "L": - return 2; + { + params: { + name: "Detect", + hovertemplate: "SP%{x}
%{y}
%{text}", + }, + + text: [ "Zero", "Peak", "Level" ], + + conversion: (gun, shot) => { + switch (gun[4]) { + case "P": + return 1; + case "Z": + return 0; + case "L": + return 2; + } } }, // Autofire - (v) => v ? 1 : 0 - ]; - const meta = { - // Mode - Mode: { - text: [ "Off", "Auto", "Manual", "Off" ] + { + params: { + name: "Autofire", + hovertemplate: "SP%{x}
%{y}
%{text}", + }, + + text: [ "False", "True" ], + + conversion: (gun, shot) => { + return gun[5] ? 1 : 0; + } }, - // Detect - Detect: { - text: [ "Zero", "Peak", "Level" ] + + // Aimpoint + { + params: { + name: "Aimpoint", + hovertemplate: "SP%{x}
%{y}
%{z} ms" + }, + + conversion: (gun, shot) => gun[7] }, - // Autofire - Autofire: { - text: [ "False", "True" ] + + // Firetime + { + params: { + name: "Firetime", + hovertemplate: "SP%{x}
%{y}
%{z} ms" + }, + + conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[8] : null + }, + + // Delta + { + params: { + name: "Delta", + hovertemplate: "SP%{x}
%{y}
%{z} ms", + // NOTE: These values are based on + // Grane + Snorre's ±1.5 ms spec. While a fairly + // common range, I still consider these min / max + // numbers to have been chosen semi-arbitrarily. + zmin: -2, + zmax: 2 + }, + + conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[7]-gun[8] : null + }, + + // Delay + { + params: { + name: "Delay", + hovertemplate: "SP%{x}
%{y}
%{z} ms" + }, + + conversion: (gun, shot) => gun[9] + }, + + // Depth + { + params: { + name: "Depth", + hovertemplate: "SP%{x}
%{y}
%{z} m" + }, + + conversion: (gun, shot) => gun[10] + }, + + // Pressure + { + params: { + name: "Pressure", + hovertemplate: "SP%{x}
%{y}
%{z} psi" + }, + + conversion: (gun, shot) => gun[11] + }, + + // Volume + { + params: { + name: "Volume", + hovertemplate: "SP%{x}
%{y}
%{z} in³" + }, + + conversion: (gun, shot) => gun[12] + }, + + // Filltime + { + params: { + name: "Filltime", + hovertemplate: "SP%{x}
%{y}
%{z} ms" + }, + + // NOTE that filltime is applicable to the *non* firing guns + conversion: (gun, shot) => gun[2] == shot.meta.src_number ? null : gun[13] } - }; - const hovertemplate = { - Filltime: "SP%{x}
%{y}
%{z} ms", - Volume: "SP%{x}
%{y}
%{z} in³", - Pressure: "SP%{x}
%{y}
%{z} psi", - Depth: "SP%{x}
%{y}
%{z} m", - Delay: "SP%{x}
%{y}
%{z} ms", - Firetime: "SP%{x}
%{y}
%{z} ms", - Aimpoint: "SP%{x}
%{y}
%{z} ms", - Autofire: "SP%{x}
%{y}
%{text}", - Detect: "SP%{x}
%{y}
%{text}", - Mode: "SP%{x}
%{y}
%{text}" - }; + + + ]; + + // Get gun numbers const guns = [...new Set(data.map( s => s.meta.guns.map( g => g[1] ) ).flat())]; + + // z eventually will have the structure: + // z = { + // [aspect]: [ // First shotpoint + // [ // Value for gun 0, gun 1, … ], + // …more shotpoints… + // ] + // } const z = {}; + + // x is an array of shotpoints const x = []; + + // y is an array of gun numbers const y = guns.map( gun => `G${gun}` ); - // Build array of guns - for (let label of labels) { + // Build array of guns (i.e., populate z) + // We prefer to do this outside the shot-to-shot loop + // for efficiency + for (const facet of facets) { + const label = facet.params.name; z[label] = Array(guns.length); for (let i=0; i { - const parameter = z[labels[i]]; - const conversion = conversions[i] || ((v) => v); - shot.meta.guns.forEach ( gun => { - const gunParameter = parameter[gun[1]-1]; - gunParameter.push(conversion(gun[index])); - }); - }); + + for (const facet of facets) { + const label = facet.params.name; + const facetGunsArray = z[label]; + + for (const gun of shot.meta.guns) { + const gunIndex = gun[1]-1; + const facetGun = facetGunsArray[gunIndex]; + facetGun.push(facet.conversion(gun, shot)); + } + } } - console.log("X, Y, Z", x, y, z); - console.log("ASPECTS", aspects); - return aspects.map ( (aspect, idx) => { - return { + return aspects.map( (aspect, idx) => { + const facet = facets.find(el => el.params.name == aspect) || {}; + + const defaultParams = { name: aspect, type: "heatmap", showscale: false, x, y, z: z[aspect], + text: facet.text ? z[aspect].map(row => row.map(v => facet.text[v])) : undefined, xaxis: "x", - yaxis: "y" + (idx > 0 ? idx+1 : ""), - text: meta[aspect]?.text ? z[aspect].map( row => row.map( v => meta[aspect].text[v] )) : undefined, - hovertemplate: hovertemplate[aspect], - meta: meta[aspect] + yaxis: "y" + (idx > 0 ? idx+1 : "") } + + + return Object.assign({}, defaultParams, facet.params); }); } - + const data = transform(this.data.items, this.aspects); this.busy = false; - console.log("DATA", data); - console.log("ASPECTS", this.aspects, this.aspects.length); const layout = { title: {text: "Gun details – sequence %{meta.sequence}"}, height: 200*this.aspects.length,