import { readFile, mkdir } from "node:fs/promises"; import path from "node:path"; import sharp from "sharp"; const directory = path.resolve("artifacts/playtest/wow-environment-quality"); const { results } = JSON.parse(await readFile(path.join(directory, "results.json"), "utf8")); const loaded = results.filter((entry) => entry.status === "loaded"); const width = 384, height = 240, labelHeight = 34, rowsPerSheet = 6; const escaped = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); await mkdir(directory, { recursive: true }); for (let start = 0; start < loaded.length; start += rowsPerSheet) { const entries = loaded.slice(start, start + rowsPerSheet); const composites = []; for (const [row, entry] of entries.entries()) { const columns = [ { file: `${entry.id}-standard.png`, label: "Standard entrance" }, { file: `${entry.id}-high-view-0.png`, label: "High entrance" }, { file: `${entry.id}-high-view-${entry.viewpoints.length - 1}.png`, label: "High final boss area" }, { file: `${entry.id}-entrance-reverse.png`, label: "High reverse entrance" }, ]; for (const [column, capture] of columns.entries()) { const top = row * (height + labelHeight), left = column * width; const thumbnail = await sharp(path.join(directory, capture.file)).resize(width, height).png().toBuffer(); const label = Buffer.from(`${escaped(entry.id)}${capture.label}`); composites.push({ input: label, top, left }, { input: thumbnail, top: top + labelHeight, left }); } } const output = path.join(directory, `review-sheet-${String(start / rowsPerSheet + 1).padStart(2, "0")}.png`); await sharp({ create: { width: width * 4, height: entries.length * (height + labelHeight), channels: 3, background: "#171a21" } }).composite(composites).png().toFile(output); console.log(path.relative(process.cwd(), output)); }