Wire bake orchestrator and CLI

Connects scan + lock + pack + write into the full pipeline. CLI entry
parses args, dispatches to bake(), prints summary or error. Five
integration tests cover fresh bake, lock-aware ID preservation,
deleted-tile tracking, oversize hard-fail, and bit-identical
determinism across re-bakes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Axel Meyer
2026-05-21 22:48:08 +02:00
parent 589bdaef10
commit dea2d8fb88
7 changed files with 309 additions and 0 deletions

71
src/bake.js Normal file
View File

@@ -0,0 +1,71 @@
// src/bake.js
const path = require('node:path');
const { scanSourceDir } = require('./scan');
const { loadLock, assignIds, serializeLock } = require('./lock');
const { packMaxRects } = require('./pack');
const { probeImageDims, composeAtlas, buildAtlasJson, writeOutputs } = require('./write');
async function bake(opts) {
const {
inDir,
outDir,
atlasId,
tileSize = 64,
maxSize = 4096,
lockPath,
blocksSightPattern,
} = opts;
if (!atlasId) throw new Error('bake: --atlas-id is required');
if (!inDir) throw new Error('bake: --in is required');
if (!outDir) throw new Error('bake: --out is required');
const scanResult = scanSourceDir(inDir);
if (scanResult.errors.length > 0) {
const e = new Error(scanResult.errors[0]);
e.scanErrors = scanResult.errors;
throw e;
}
// Determine each source's tile-dimensions.
// - If --tile-size is a positive integer, all tiles are forced to that
// size (composeAtlas auto-resizes mismatched sources via jimp).
// - If --tile-size === 'auto', each tile keeps its native dims.
for (const src of scanResult.sources) {
if (tileSize && tileSize !== 'auto') {
src.w = tileSize;
src.h = tileSize;
} else {
const dims = await probeImageDims(src.diffusePath);
src.w = dims.width;
src.h = dims.height;
}
}
const existingLock = loadLock(lockPath || path.join(outDir, 'tiles.atlas.lock.json'));
const { tiles, lock } = assignIds(scanResult.sources, existingLock, atlasId);
const packResult = packMaxRects(tiles, maxSize);
if (packResult.error) {
throw new Error(packResult.message);
}
const { diffuse, height } = await composeAtlas(packResult.placed, packResult.boundsW, packResult.boundsH);
const atlasJson = buildAtlasJson(
atlasId, 1, (tileSize === 'auto') ? null : tileSize,
packResult.boundsW, packResult.boundsH,
packResult.placed,
blocksSightPattern,
);
writeOutputs(outDir, diffuse, height, atlasJson, lock);
return {
outDir,
atlasId,
tileCount: packResult.placed.length,
boundsW: packResult.boundsW,
boundsH: packResult.boundsH,
};
}
module.exports = { bake };