Files

59 lines
3.3 KiB
JavaScript

// The base64 bridge must round-trip bytes exactly and give the same fins as
// calling the engine directly.
import { b64ToBytes, bytesToB64, computeFinsB64, drawWallB64 } from '../engine/bridge.js';
import { computeFins } from '../engine/fins_entry.js';
import { drawWall } from '../engine/draw_entry.js';
import { readSTL, MODELS, rotX, assert, tiltedBlockTopo } from '../../../tests/_util.js';
Deno.test('base64 round-trips every length mod 3', () => {
for (let n = 0; n < 50; n++) {
const b = new Uint8Array(n).map((_, i) => (i * 37 + n) & 255);
const back = b64ToBytes(bytesToB64(b));
assert(back.length === n && back.every((v, i) => v === b[i]), `length ${n} failed`);
}
});
Deno.test('matches the platform btoa on random data', () => {
const b = new Uint8Array(100000).map(() => (Math.random() * 256) | 0);
let bin = '';
for (const v of b) bin += String.fromCharCode(v);
assert(bytesToB64(b) === btoa(bin), 'differs from btoa');
});
Deno.test('bridge gives the same fins as the direct call', () => {
const raw = readSTL(Deno.readFileSync(`${MODELS}cube.stl`));
const m = rotX(45);
const posed = new Float64Array(raw.length);
for (let i = 0; i < raw.length; i += 3) {
const x = raw[i], y = raw[i + 1], z = raw[i + 2];
posed[i] = m[0] * x + m[3] * y + m[6] * z + 50;
posed[i + 1] = m[1] * x + m[4] * y + m[7] * z + 60;
posed[i + 2] = m[2] * x + m[5] * y + m[8] * z;
}
const direct = computeFins(posed, { layerHeight: 0.2 });
const viaB64 = JSON.parse(computeFinsB64(bytesToB64(new Uint8Array(posed.buffer)), JSON.stringify({ layerHeight: 0.2 })));
const bytes = b64ToBytes(viaB64.triangles);
const tris = new Float32Array(bytes.buffer);
assert(tris.length === direct.triangles.length, 'triangle count differs');
assert(tris.every((v, i) => v === direct.triangles[i]), 'triangles differ');
assert(JSON.stringify(viaB64.offset) === JSON.stringify(direct.offset), 'offset differs');
assert(JSON.stringify(viaB64.pieces) === JSON.stringify(direct.pieces), 'pieces differ');
assert(JSON.stringify(viaB64.overFaces) === JSON.stringify(Array.from(direct.overFaces)), 'overFaces differ');
});
Deno.test('bridge draws the same wall as the direct call, and passes a refusal through', () => {
const pos = Float64Array.from(tiltedBlockTopo(-20, 20, -30, 30, -6, 6, 45).pos);
const soup = bytesToB64(new Uint8Array(pos.buffer));
// a level line under the tilted block's underside (at y = -5 it is ~12 mm up)
const a = [-8, -5, 11.97], b = [8, -5, 11.97];
const direct = drawWall(pos, a, b, {});
const via = JSON.parse(drawWallB64(soup, JSON.stringify(a), JSON.stringify(b), '{}'));
assert(direct.ok && via.ok, `wall failed: ${direct.reason ?? via.reason}`);
const tris = new Float32Array(b64ToBytes(via.triangles).buffer);
assert(tris.length === direct.triangles.length && tris.every((v, i) => v === direct.triangles[i]), 'triangles differ');
assert(JSON.stringify(via.offset) === JSON.stringify(direct.offset), 'offset differs');
assert(JSON.stringify(via.stats) === JSON.stringify(direct.stats), 'stats differ');
const short = JSON.parse(drawWallB64(soup, JSON.stringify(a), JSON.stringify([-7, -5, 11.97]), '{}')); // 1 mm, under DRAW_MIN_LEN
assert(!short.ok && /too short/.test(short.reason), `expected a refusal, got ${JSON.stringify(short)}`);
});