forked from phenom/healer-man
89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
countSurfaceBucketReferences,
|
|
selectDetourAnchorComponents,
|
|
} from "./detour-component-selection.mjs";
|
|
|
|
function topologyFixture() {
|
|
const polygons = [
|
|
[0, 0, 0, 0, 0, 0, 0, 2, 4],
|
|
[1, 0, 0, 0, 0, 1, 2, 1, 3],
|
|
[2, 1, 1, 0, 0, 0, 3, 3, 4],
|
|
[3, 2, 2, 0, 0, 0, 6, 1, 3],
|
|
];
|
|
const link = (from, to, edge) => [from, to, edge, 0, 0, 255, 0, 1, from, 0, 0, to, 0, 0];
|
|
return {
|
|
schemaVersion: 1,
|
|
polygonCount: 4,
|
|
tileCount: 3,
|
|
detailTriangleCount: 7,
|
|
geometryTriangleCount: 7,
|
|
polygonFanTriangleCount: 5,
|
|
directedLinkCount: 2,
|
|
unrepresentedLinkCount: 0,
|
|
offMeshPolygonCount: 0,
|
|
weakComponents: { count: 3, largestPolygonCount: 2, polygonCounts: [2, 1, 1] },
|
|
polygons,
|
|
links: [link(0, 2, 0), link(2, 0, 1)],
|
|
};
|
|
}
|
|
|
|
test("source anchors retain complete weak Detour components and remap only exact source links", () => {
|
|
const selected = selectDetourAnchorComponents(topologyFixture(), [
|
|
{ id: "entry", kind: "entry", position: [0, 0, 0], polygonId: 0, distance: 0.2, maximumDistance: 0.5, required: true },
|
|
{ id: "boss", kind: "boss", position: [100, 0, 0], polygonId: 3, distance: 0.1, maximumDistance: 0.25, required: true },
|
|
{ id: "far-combat", kind: "combat", position: [50, 0, 0], polygonId: 1, distance: 7, maximumDistance: 4 },
|
|
]);
|
|
|
|
assert.deepEqual([...selected.selectedSourcePolygonIds], [0, 2, 3]);
|
|
assert.deepEqual([...selected.sourceToSelectedPolygonId], [[0, 0], [2, 1], [3, 2]]);
|
|
assert.equal(selected.topology.polygonCount, 3);
|
|
assert.equal(selected.topology.directedLinkCount, 2);
|
|
assert.equal(selected.topology.detailTriangleCount, 6);
|
|
assert.deepEqual(selected.topology.polygons.map((row) => row.slice(0, 8)), [
|
|
[0, 0, 0, 0, 0, 0, 0, 2],
|
|
[1, 1, 1, 0, 0, 0, 2, 3],
|
|
[2, 2, 2, 0, 0, 0, 5, 1],
|
|
]);
|
|
assert.deepEqual(selected.topology.links.map((row) => row.slice(0, 2)), [[0, 1], [1, 0]]);
|
|
assert.equal(selected.summary.retainedAnchorIds.includes("entry"), true);
|
|
assert.equal(selected.summary.retainedAnchorIds.includes("boss"), true);
|
|
assert.equal(selected.summary.unmatchedAnchors[0].id, "far-combat");
|
|
assert.equal(selected.summary.excluded.polygonCount, 1);
|
|
assert.equal(selected.summary.excluded.disposition, "unanchored-components-excluded; not individually proven decorative");
|
|
});
|
|
|
|
test("selection fails closed when an anchored component exceeds any runtime budget", () => {
|
|
const source = topologyFixture();
|
|
assert.throws(() => selectDetourAnchorComponents(source, [
|
|
{ id: "entry", polygonId: 0, distance: 0, maximumDistance: 1 },
|
|
], { limits: { polygons: 1 } }), /polygons budget/);
|
|
assert.throws(() => selectDetourAnchorComponents(source, [
|
|
{ id: "entry", polygonId: 0, distance: 0, maximumDistance: 1 },
|
|
], { limits: { directedLinks: 1 } }), /directedLinks budget/);
|
|
assert.throws(() => selectDetourAnchorComponents(source, [
|
|
{ id: "entry", polygonId: 0, distance: 0, maximumDistance: 1 },
|
|
], { limits: { detailTriangles: 1 } }), /detailTriangles budget/);
|
|
assert.throws(() => selectDetourAnchorComponents(source, [
|
|
{ id: "entry", polygonId: 0, distance: 0, maximumDistance: 1 },
|
|
], { limits: { surfaceBucketReferences: 3 }, surfaceBucketReferenceCount: 4 }), /surfaceBucketReferences budget/);
|
|
});
|
|
|
|
test("source anchors outside their explicit projection limit do not select components", () => {
|
|
assert.throws(() => selectDetourAnchorComponents(topologyFixture(), [
|
|
{ id: "unprojected", polygonId: 2, distance: 4.001, maximumDistance: 4 },
|
|
]), /No source anchors map/);
|
|
});
|
|
|
|
test("surface bucket references match runtime's XZ cell coverage rule", () => {
|
|
const positions = new Float32Array([
|
|
0, 0, 0,
|
|
8, 0, 0,
|
|
0, 0, 8,
|
|
-0.1, 0, -0.1,
|
|
]);
|
|
const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
assert.equal(countSurfaceBucketReferences(positions, indices, 8), 10);
|
|
});
|