bands-visualiser
Version:
A JavaScript library for visualising bandstructures.
215 lines (189 loc) • 4.99 kB
JavaScript
const baseColors = [
"#636EFA",
"#EF553B",
"#00CC96",
"#AB63FA",
"#FFA15A",
"#19D3F3",
"#FF6692",
"#B6E880",
"#FF97FF",
"#FECB52",
];
export function getColor(i) {
if (i < baseColors.length) {
return baseColors[i];
} else {
// Fallback: generate a unique-ish color using HSL
const hue = (i * 47) % 360; // 47 is a prime: avoids obvious cycles
return `hsl(${hue}, 70%, 50%)`;
}
}
export function mergeLegendTracesByName(traces) {
const merged = new Set();
const mergedTraces = traces.map((trace) => {
const traceName = trace.name || "";
const show = !merged.has(traceName);
if (show) {
merged.add(traceName);
}
return {
...trace,
legendgroup: traceName,
showlegend: show,
};
});
return mergedTraces;
}
export function getXYData(bandData) {
const xs = [];
const ys = [];
let lastX = null;
bandData.paths.forEach((path, pathIndex) => {
const pathX = path.x;
const pathVals = path.values;
if (pathIndex > 0) {
xs.push(null);
ys.forEach((yBand) => yBand.push(null));
}
pathX.forEach((xVal, i) => {
if (xVal === lastX) {
xs.push(null);
ys.forEach((yBand) => yBand.push(null));
}
xs.push(xVal);
lastX = xVal;
pathVals.forEach((band, bIndex) => {
if (!ys[bIndex]) ys[bIndex] = [];
ys[bIndex].push(band[i]);
});
});
});
return { xs, ys };
}
export function prettifyLabels(label) {
const greekMapping = {
GAMMA: "Γ",
DELTA: "Δ",
SIGMA: "Σ",
LAMBDA: "Λ",
};
Object.keys(greekMapping).forEach((symbol) => {
const regex = new RegExp(symbol, "gi");
label = label.replace(regex, greekMapping[symbol]);
});
label = label.replace(/\bG\b/g, "Γ");
label = label.replace(/-/g, "—");
const ssMapping = {
0: "₀",
1: "₁",
2: "₂",
3: "₃",
4: "₄",
5: "₅",
6: "₆",
7: "₇",
8: "₈",
9: "₉",
};
label = label.replace(/_(.)/g, (match, p1) => ssMapping[p1] || match);
return label;
}
export function getXLabelPos(paths = td.paths) {
const xLabelPos = [0];
for (const path of paths) {
if (path.x.length < 3) continue; //skip short paths (these dont move on x)
xLabelPos.push(path.x[path.x.length - 1]);
}
return xLabelPos;
}
export function getXLabelsfromkpath(kpath) {
const arr = kpath.flat();
const result = [];
result.push(arr[0]);
for (let i = 1; i < arr.length - 1; i++) {
if (arr[i] === arr[i + 1]) {
result.push(arr[i]);
i++;
} else {
result.push(arr[i] + "|" + arr[i + 1]);
i++;
}
}
// Always push last
result.push(arr[arr.length - 1]);
return result;
} // Extract min/max ranges for specified x and y axes from traces
function getXYRangeForAxis(traces, axisX = "x", axisY = "y") {
let minX = Infinity,
maxX = -Infinity;
let minY = Infinity,
maxY = -Infinity;
for (const trace of traces) {
const matchesX = trace.xaxis === axisX || (!trace.xaxis && axisX === "x");
const matchesY = trace.yaxis === axisY || (!trace.yaxis && axisY === "y");
if (!matchesX || !matchesY) continue;
for (const x of trace.x ?? []) {
if (typeof x === "number") {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
}
}
for (const y of trace.y ?? []) {
if (typeof y === "number") {
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
}
return {
x: minX === Infinity ? null : [minX, maxX],
y: minY === Infinity ? null : [minY, maxY],
};
}
// helper fxn to create a fermiLine trace.
function createFermiLine(x, y, fermi, opts = {}) {
return {
x,
y,
mode: "lines",
type: "scatter",
name: `Fermi [${fermi}eV]`,
legendgroup: `fermi-${fermi}`,
showlegend: opts.showlegend ?? true,
line: { color: "green", width: 1.25, dash: "dash" },
...opts,
};
}
// helper function to add Fermi level lines
export function addfermiLines(fermiLevels, traces, hasDOS, hasBands) {
const out = [...traces];
// DOS
if (hasDOS && !hasBands) {
const { y } = getXYRangeForAxis(traces);
for (const fermi of fermiLevels) {
out.push(createFermiLine([fermi, fermi], [y[0], y[1]], fermi));
}
// Bands
} else if (hasBands && !hasDOS) {
const { x } = getXYRangeForAxis(traces);
for (const fermi of fermiLevels) {
out.push(createFermiLine([x[0], x[1]], [fermi, fermi], fermi));
}
// Both
} else if (hasBands && hasDOS) {
const { x: x2 } = getXYRangeForAxis(traces, "x2", "y");
const { x, y } = getXYRangeForAxis(traces, "x", "y");
for (const fermi of fermiLevels) {
out.push(createFermiLine([x[0], x[1]], [fermi, fermi], fermi));
out.push(
createFermiLine([x2[0], x2[1]], [fermi, fermi], fermi, {
showlegend: false,
xaxis: "x2",
yaxis: "y",
})
);
}
}
return out;
}