bands-visualiser
Version:
A plotly.js renderer for Bands, DOS, combined Bands/Dos and fatBands.
138 lines (113 loc) • 3.24 kB
JavaScript
export function transpose(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
const result = new Array(cols);
for (let i = 0; i < cols; i++) {
result[i] = new Array(rows);
for (let j = 0; j < rows; j++) {
result[i][j] = matrix[j][i];
}
}
return result;
}
export function splitBandsData(bandsData, nParts) {
const { path, label, paths } = bandsData;
// Determine number of bands (from first path)
const totalBands = paths[0]?.values.length || 0;
const perChunk = Math.floor(totalBands / nParts);
const result = [];
for (let i = 0; i < nParts; i++) {
const start = i * perChunk;
const end = i === nParts - 1 ? totalBands : (i + 1) * perChunk;
const newPaths = paths.map((pathObj) => {
const { values, ...rest } = pathObj;
return {
...rest,
values: values.slice(start, end),
};
});
result.push({
path,
label,
paths: newPaths,
});
}
return result;
}
export function getBandByIndex(bandsData, index) {
const { path, label, paths } = bandsData;
const newPaths = paths.map((pathObj) => {
const { values, ...rest } = pathObj;
return {
...rest,
values: [values[index]], // extract a single band
};
});
return {
path,
label,
paths: newPaths,
};
}
// method to downsample a ProjectionData array.
// downsampling operates over the x-axis data set
// Needs some testing to measure break-points in performance
// and whether any clarity is lost.
export function downsampleProjections(projections, step) {
const { x, ys, weights } = projections;
const len = x.length;
const nRows = ys.length;
// Pre-allocate output arrays
const downsampledX = new Float32Array(Math.ceil(len / step));
const downsampledYs = Array.from(
{ length: nRows },
() => new Float32Array(Math.ceil(len / step))
);
const downsampledWeights = Array.from(
{ length: nRows },
() => new Float32Array(Math.ceil(len / step))
);
let outIdx = 0;
for (let i = 0; i < len; i += step, outIdx++) {
downsampledX[outIdx] = x[i];
for (let j = 0; j < nRows; j++) {
downsampledYs[j][outIdx] = ys[j][i];
downsampledWeights[j][outIdx] = weights[j][i];
}
}
return {
x: downsampledX,
ys: downsampledYs,
weights: downsampledWeights,
};
}
// sanitisation / autopacking utils
export function ensureArray(value) {
if (value == null) return [];
return Array.isArray(value) ? value : [value];
}
export function sanitizeBandsInput(input) {
if (input == null) return [];
const bands = (Array.isArray(input) ? input.flat() : [input]).filter(Boolean);
return bands.map((band) => {
// Already in expected internal shape
if (band.bandsData) {
return {
projections: [],
...band,
};
}
// Flat band object → wrap it
if (band.path || band.paths) {
const { traceFormat, projections, ...bandsData } = band;
return {
bandsData,
traceFormat,
projections: projections ?? [], // ✅ critical fix
};
}
throw new Error(
"BandsVisualiser: invalid band object shape. Expected bandsData or band-like object."
);
});
}