Files
sporel-tool-atlas-baker/tests/write.test.js
Axel Meyer 589bdaef10 Add atlas composers and output writers
composeAtlas blits placed tile pixels into bounds-sized RGBA + L8 PNGs,
synthesizing L8=0 planes for unpaired diffuse tiles. buildAtlasJson
emits the metadata with deterministic ID-sorted tile arrays and applies
the optional blocks_sight regex.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:45:54 +02:00

67 lines
3.2 KiB
JavaScript

// tests/write.test.js
const { test } = require('node:test');
const assert = require('node:assert');
const path = require('node:path');
const fs = require('node:fs');
const { PNG } = require('pngjs');
const { composeAtlas, buildAtlasJson } = require('../src/write');
const FIX = path.resolve(__dirname, 'fixtures/small-fresh');
test('compose: 3 tiles into 96x32 atlas', async () => {
const placed = [
{ name: 'grass', id: 1, x: 0, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'grass_diffuse.png'), heightPath: null },
{ name: 'stone', id: 2, x: 32, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'stone_diffuse.png'), heightPath: null },
{ name: 'water', id: 3, x: 64, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'water_diffuse.png'), heightPath: path.join(FIX, 'water_height.png') },
];
const { diffuse, height } = await composeAtlas(placed, 128, 32);
assert.strictEqual(diffuse.width, 128);
assert.strictEqual(diffuse.height, 32);
// Sample grass pixel (0,0): expect RGB ~ (110, 170, 80)
const gIdx = 0;
assert.strictEqual(diffuse.data[gIdx + 0], 110);
assert.strictEqual(diffuse.data[gIdx + 1], 170);
assert.strictEqual(diffuse.data[gIdx + 2], 80);
// Sample stone pixel (32, 0): expect RGB ~ (120, 120, 120)
const sIdx = (0 * 128 + 32) * 4;
assert.strictEqual(diffuse.data[sIdx + 0], 120);
// Height should be 0 for synth-tiles (grass, stone)
assert.strictEqual(height.data[0], 0); // grass position
assert.strictEqual(height.data[(0 * 128 + 32) * 4], 0); // stone position
});
test('buildAtlasJson: sorted by ID, blocks_sight pattern applied', () => {
const placed = [
{ name: 'grass', id: 1, x: 0, y: 0, w: 32, h: 32 },
{ name: 'stone_wall_brick', id: 2, x: 32, y: 0, w: 32, h: 32 },
];
const json = buildAtlasJson('demo', 1, 32, 64, 32, placed, /^stone_wall_/);
assert.strictEqual(json.atlas_id, 'demo');
assert.strictEqual(json.tiles.length, 2);
assert.strictEqual(json.tiles[0].id, 1);
assert.strictEqual(json.tiles[0].name, 'grass');
assert.deepStrictEqual(json.tiles[0].uv, [0, 0, 32, 32]);
assert.strictEqual(json.tiles[0].blocks_sight, undefined);
assert.strictEqual(json.tiles[1].blocks_sight, true);
});
test('compose: WebP source loads + resizes', async () => {
// Optional integration check — only runs if FA-Starter fixture is staged.
// The Task 10 baker run is the real-world verification; this test is a
// cheaper inline guard against jimp.read regressions on WebP.
const webpFixture = path.resolve(__dirname, 'fixtures/multi-format/sample_diffuse.webp');
if (!fs.existsSync(webpFixture)) return; // skip if not staged
const placed = [{ name: 'sample', id: 1, x: 0, y: 0, w: 64, h: 64,
diffusePath: webpFixture, heightPath: null }];
const { diffuse } = await composeAtlas(placed, 64, 64);
assert.strictEqual(diffuse.width, 64);
assert.strictEqual(diffuse.height, 64);
// Just confirm it didn't throw and has nonzero alpha somewhere
let hasAlpha = false;
for (let i = 3; i < diffuse.data.length; i += 4) {
if (diffuse.data[i] > 0) { hasAlpha = true; break; }
}
assert.ok(hasAlpha, 'compose produced fully-transparent atlas');
});