assets/emergence-layout.js
// Squarified areas describe coverage, never a ranking of importance or confidence.
function worst(row, side) {
const sum = row.reduce((n, item) => n + item.value, 0);
if (!sum || !side) return Infinity;
return Math.max(side * side * Math.max(...row.map(i => i.value)) / (sum * sum),
sum * sum / (side * side * Math.min(...row.map(i => i.value))));
}
export function squarify(items) {
const total = items.reduce((n, item) => n + item.value, 0);
const rest = items.filter(i => i.value > 0).map(i => ({ ...i, value: i.value / total }));
const tiles = [];
let x = 0, y = 0, w = 1, h = 1;
while (rest.length) {
const side = Math.min(w, h), row = [rest.shift()];
while (rest.length && worst([...row, rest[0]], side) <= worst(row, side)) row.push(rest.shift());
const sum = row.reduce((n, i) => n + i.value, 0);
if (w >= h) {
const width = sum / h;
let top = y;
for (const item of row) {
const height = item.value / width;
tiles.push({ ...item, x, y: top, w: width, h: height });
top += height;
}
x += width; w = Math.max(0, w - width);
} else {
const height = sum / w;
let left = x;
for (const item of row) {
const width = item.value / height;
tiles.push({ ...item, x: left, y, w: width, h: height });
left += width;
}
y += height; h = Math.max(0, h - height);
}
}
return tiles;
}