Refactor gun heatmaps component.

Fixes #150.

Contributes towards the goal of #149. As irrelevant data (such
as for non-firing guns) is no longer shown at all. This affects:

* Firetime (only active array data shown)
* Gun deltas (only active array shown)
* Fill time (only non-active array shown)
This commit is contained in:
D. Berge
2021-09-21 00:32:00 +02:00
parent 862e754a6f
commit 454094b187

View File

@@ -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}<br>%{y}<br>%{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}<br>%{y}<br>%{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}<br>%{y}<br>%{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}<br>%{y}<br>%{z} ms"
},
conversion: (gun, shot) => gun[7]
},
// Autofire
Autofire: {
text: [ "False", "True" ]
// Firetime
{
params: {
name: "Firetime",
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
},
conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[8] : null
},
// Delta
{
params: {
name: "Delta",
hovertemplate: "SP%{x}<br>%{y}<br>%{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}<br>%{y}<br>%{z} ms"
},
conversion: (gun, shot) => gun[9]
},
// Depth
{
params: {
name: "Depth",
hovertemplate: "SP%{x}<br>%{y}<br>%{z} m"
},
conversion: (gun, shot) => gun[10]
},
// Pressure
{
params: {
name: "Pressure",
hovertemplate: "SP%{x}<br>%{y}<br>%{z} psi"
},
conversion: (gun, shot) => gun[11]
},
// Volume
{
params: {
name: "Volume",
hovertemplate: "SP%{x}<br>%{y}<br>%{z} in³"
},
conversion: (gun, shot) => gun[12]
},
// Filltime
{
params: {
name: "Filltime",
hovertemplate: "SP%{x}<br>%{y}<br>%{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}<br>%{y}<br>%{z} ms",
Volume: "SP%{x}<br>%{y}<br>%{z} in³",
Pressure: "SP%{x}<br>%{y}<br>%{z} psi",
Depth: "SP%{x}<br>%{y}<br>%{z} m",
Delay: "SP%{x}<br>%{y}<br>%{z} ms",
Firetime: "SP%{x}<br>%{y}<br>%{z} ms",
Aimpoint: "SP%{x}<br>%{y}<br>%{z} ms",
Autofire: "SP%{x}<br>%{y}<br>%{text}",
Detect: "SP%{x}<br>%{y}<br>%{text}",
Mode: "SP%{x}<br>%{y}<br>%{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<guns.length; i++) {
z[label][i] = [];
@@ -172,40 +277,42 @@ export default {
// Populate array of guns with shotpoint data
for (let shot of data) {
x.push(shot.point);
indices.forEach( (index, 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,