mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
113 lines
2.9 KiB
JavaScript
113 lines
2.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
|
|
import { solveLayoutWithJs } from "../ui/graph-layout-solver.js";
|
|
import {
|
|
getNativeModuleStatus,
|
|
solveLayout,
|
|
} from "../vendor/wasm/stbme_core.js";
|
|
|
|
const originalLoader = globalThis.__stBmeLoadRustWasmLayout;
|
|
|
|
function buildPayload(seed = 42, nodeCount = 180) {
|
|
let state = seed;
|
|
const rand = () => {
|
|
state = (state * 1664525 + 1013904223) >>> 0;
|
|
return state / 0xffffffff;
|
|
};
|
|
|
|
const regionRect = { x: 0, y: 0, w: 980, h: 620 };
|
|
const nodes = [];
|
|
for (let i = 0; i < nodeCount; i++) {
|
|
nodes.push({
|
|
x: regionRect.x + rand() * regionRect.w,
|
|
y: regionRect.y + rand() * regionRect.h,
|
|
vx: 0,
|
|
vy: 0,
|
|
pinned: false,
|
|
radius: 6 + rand() * 8,
|
|
regionKey: "objective",
|
|
regionRect,
|
|
});
|
|
}
|
|
|
|
const edges = [];
|
|
const edgeTarget = Math.max(nodeCount * 4, 1);
|
|
for (let i = 0; i < edgeTarget; i++) {
|
|
const from = Math.floor(rand() * nodeCount);
|
|
let to = Math.floor(rand() * nodeCount);
|
|
if (to === from) to = (to + 1) % nodeCount;
|
|
edges.push({
|
|
from,
|
|
to,
|
|
strength: 0.25 + rand() * 0.75,
|
|
});
|
|
}
|
|
|
|
return {
|
|
nodes,
|
|
edges,
|
|
config: {
|
|
iterations: 52,
|
|
repulsion: 2600,
|
|
springK: 0.05,
|
|
damping: 0.87,
|
|
centerGravity: 0.015,
|
|
minGap: 11,
|
|
speedCap: 3.2,
|
|
},
|
|
};
|
|
}
|
|
|
|
function meanAbsDiff(a = new Float32Array(0), b = new Float32Array(0)) {
|
|
const len = Math.min(a.length, b.length);
|
|
if (len === 0) return 0;
|
|
let sum = 0;
|
|
for (let i = 0; i < len; i++) {
|
|
sum += Math.abs(a[i] - b[i]);
|
|
}
|
|
return sum / len;
|
|
}
|
|
|
|
try {
|
|
globalThis.__stBmeLoadRustWasmLayout = async () => ({
|
|
solve_layout(payload) {
|
|
const jsResult = solveLayoutWithJs(payload);
|
|
return {
|
|
ok: true,
|
|
positions: Array.from(jsResult.positions),
|
|
diagnostics: {
|
|
solver: "mock-rust-wasm",
|
|
nodeCount: jsResult.diagnostics.nodeCount,
|
|
edgeCount: jsResult.diagnostics.edgeCount,
|
|
iterations: jsResult.diagnostics.iterations,
|
|
},
|
|
};
|
|
},
|
|
});
|
|
|
|
const payload = buildPayload();
|
|
const jsResult = solveLayoutWithJs(payload);
|
|
const nativeResult = await solveLayout(payload);
|
|
|
|
assert.equal(jsResult.ok, true);
|
|
assert.equal(nativeResult.ok, true);
|
|
assert.ok(nativeResult.positions instanceof Float32Array);
|
|
assert.equal(jsResult.positions.length, nativeResult.positions.length);
|
|
|
|
const mad = meanAbsDiff(jsResult.positions, nativeResult.positions);
|
|
const nativeStatus = getNativeModuleStatus();
|
|
const threshold = nativeStatus.source === "wasm-pack-artifact" ? 2e-4 : 1e-6;
|
|
assert.ok(
|
|
mad <= threshold,
|
|
`mean abs diff too high: ${mad} (source=${nativeStatus.source || "unknown"}, threshold=${threshold})`,
|
|
);
|
|
} finally {
|
|
if (typeof originalLoader === "function") {
|
|
globalThis.__stBmeLoadRustWasmLayout = originalLoader;
|
|
} else {
|
|
delete globalThis.__stBmeLoadRustWasmLayout;
|
|
}
|
|
}
|
|
|
|
console.log("native-layout-parity tests passed");
|