102 lines
5.3 KiB
JavaScript
102 lines
5.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
|
|
const root = new URL("../../", import.meta.url);
|
|
const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root));
|
|
const golden = JSON.parse(fs.readFileSync(new URL("tests/golden/N-015/nonmesh-desktop-geometry.json", root), "utf8"));
|
|
const fixture = fs.readFileSync(new URL(`tests/files/web/${golden.fixture}`, root));
|
|
|
|
function readOutput(engine, handle, callback) {
|
|
const dataOut = engine._malloc(4);
|
|
const lengthOut = engine._malloc(4);
|
|
try {
|
|
assert.equal(callback(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
const pointer = engine.HEAPU32[dataOut >>> 2];
|
|
const length = engine.HEAPU32[lengthOut >>> 2];
|
|
return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length)));
|
|
}
|
|
finally {
|
|
engine._free(dataOut);
|
|
engine._free(lengthOut);
|
|
}
|
|
}
|
|
|
|
function close(actual, expected, tolerance, label) {
|
|
assert.ok(Math.abs(actual - expected) <= tolerance, `${label}: expected ${expected}, got ${actual}`);
|
|
}
|
|
|
|
function metrics(geometry) {
|
|
const positions = geometry.positions ?? [];
|
|
const edgeIndices = geometry.edgeVertexIndices ?? [];
|
|
const indices = geometry.indices ?? [];
|
|
const vertexCount = positions.length / 3;
|
|
const boundsMin = [0, 1, 2].map((axis) => vertexCount ? Math.min(...Array.from({ length: vertexCount }, (_, index) => positions[index * 3 + axis])) : 0);
|
|
const boundsMax = [0, 1, 2].map((axis) => vertexCount ? Math.max(...Array.from({ length: vertexCount }, (_, index) => positions[index * 3 + axis])) : 0);
|
|
const centroid = [0, 1, 2].map((axis) => vertexCount ? Array.from({ length: vertexCount }, (_, index) => positions[index * 3 + axis]).reduce((sum, value) => sum + value, 0) / vertexCount : 0);
|
|
let surfaceArea = 0;
|
|
for (let triangle = 0; triangle < indices.length; triangle += 3) {
|
|
const a = indices[triangle] * 3;
|
|
const b = indices[triangle + 1] * 3;
|
|
const c = indices[triangle + 2] * 3;
|
|
const ab = [positions[b] - positions[a], positions[b + 1] - positions[a + 1], positions[b + 2] - positions[a + 2]];
|
|
const ac = [positions[c] - positions[a], positions[c + 1] - positions[a + 1], positions[c + 2] - positions[a + 2]];
|
|
const cross = [ab[1] * ac[2] - ab[2] * ac[1], ab[2] * ac[0] - ab[0] * ac[2], ab[0] * ac[1] - ab[1] * ac[0]];
|
|
surfaceArea += Math.hypot(...cross) * 0.5;
|
|
}
|
|
return {
|
|
vertexCount,
|
|
edgeCount: edgeIndices.length / 2,
|
|
triangleCount: indices.length / 3,
|
|
boundsMin,
|
|
boundsMax,
|
|
centroid,
|
|
surfaceArea,
|
|
positionMoment: positions.reduce((sum, value, index) => sum + (index + 1) * value, 0),
|
|
edgeIndexMoment: edgeIndices.reduce((sum, value, index) => sum + (index + 1) * (value + 1), 0),
|
|
indexMoment: indices.reduce((sum, value, index) => sum + (index + 1) * (value + 1), 0),
|
|
};
|
|
}
|
|
|
|
assert.equal(golden.schemaVersion, 1);
|
|
assert.equal(golden.blenderVersion, "5.2.0 LTS");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
try {
|
|
const input = engine._malloc(fixture.byteLength);
|
|
engine.HEAPU8.set(fixture, input);
|
|
try {
|
|
assert.equal(engine._web_engine_open_blend(handle, input, fixture.byteLength), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
}
|
|
finally {
|
|
engine._free(input);
|
|
}
|
|
const evaluation = readOutput(engine, handle, engine._web_engine_evaluate_depsgraph);
|
|
const byObject = new Map(evaluation.nonMeshGeometries.map((geometry) => [geometry.objectId.replace(/^object:/, ""), geometry]));
|
|
for (const expected of golden.geometries) {
|
|
const geometry = byObject.get(expected.object);
|
|
assert.ok(geometry, `desktop golden object ${expected.object} is missing from the Web evaluation`);
|
|
assert.equal(geometry.status, "EVALUATED");
|
|
assert.equal(geometry.sourceType, expected.sourceType);
|
|
const actual = metrics(geometry);
|
|
assert.equal(actual.vertexCount, expected.vertexCount, `${expected.object} vertex count`);
|
|
assert.equal(actual.edgeCount, expected.edgeCount, `${expected.object} edge count`);
|
|
assert.equal(actual.triangleCount, expected.triangleCount, `${expected.object} triangle count`);
|
|
assert.equal(actual.edgeIndexMoment, expected.edgeIndexMoment, `${expected.object} edge index moment`);
|
|
assert.equal(actual.indexMoment, expected.indexMoment, `${expected.object} index moment`);
|
|
for (let axis = 0; axis < 3; axis++) {
|
|
close(actual.boundsMin[axis], expected.boundsMin[axis], golden.tolerance.coordinate, `${expected.object} boundsMin[${axis}]`);
|
|
close(actual.boundsMax[axis], expected.boundsMax[axis], golden.tolerance.coordinate, `${expected.object} boundsMax[${axis}]`);
|
|
close(actual.centroid[axis], expected.centroid[axis], golden.tolerance.coordinate, `${expected.object} centroid[${axis}]`);
|
|
}
|
|
close(actual.surfaceArea, expected.surfaceArea, golden.tolerance.surfaceArea, `${expected.object} surface area`);
|
|
close(actual.positionMoment, expected.positionMoment, golden.tolerance.positionMoment, `${expected.object} position moment`);
|
|
}
|
|
assert.equal(byObject.size, golden.geometries.length, "unexpected evaluated non-mesh object count");
|
|
}
|
|
finally {
|
|
engine._web_engine_destroy(handle);
|
|
}
|
|
|
|
console.log(`nonmesh-desktop-golden-ok fixture=${golden.fixture} geometries=${golden.geometries.length}`);
|