Add tests for whitebox primitives creation, runtime, geometry, and serialization

This commit is contained in:
2026-04-15 08:11:43 +02:00
parent abdcab4605
commit 1fdf8895c5
4 changed files with 234 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { describe, expect, it } from "vitest";
import { createEditorStore } from "../../src/app/editor-store";
import { createCreateRadialPrismBrushCommand } from "../../src/commands/create-radial-prism-brush-command";
import { createCreateWedgeBrushCommand } from "../../src/commands/create-wedge-brush-command";
describe("whitebox primitive creation commands", () => {
it("creates a wedge brush with undo and redo", () => {
const store = createEditorStore();
store.executeCommand(
createCreateWedgeBrushCommand({
center: {
x: 2.2,
y: 1.4,
z: -1.1
},
size: {
x: 4.2,
y: 2.1,
z: 6.2
}
})
);
const brush = Object.values(store.getState().document.brushes)[0];
expect(brush.kind).toBe("wedge");
expect(Object.keys(brush.faces)).toEqual([
"bottom",
"back",
"slope",
"left",
"right"
]);
expect(store.getState().selection).toEqual({
kind: "brushes",
ids: [brush.id]
});
expect(store.undo()).toBe(true);
expect(store.getState().document.brushes).toEqual({});
expect(store.redo()).toBe(true);
expect(store.getState().document.brushes[brush.id]).toEqual(brush);
});
it("creates a cylinder brush as a 12-sided radial prism", () => {
const store = createEditorStore();
store.executeCommand(
createCreateRadialPrismBrushCommand({
sideCount: 12
})
);
const brush = Object.values(store.getState().document.brushes)[0];
expect(brush.kind).toBe("radialPrism");
expect(brush.sideCount).toBe(12);
expect(Object.keys(brush.faces)).toHaveLength(14);
});
});

View File

@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import {
createRadialPrismBrush,
createWedgeBrush
} from "../../src/document/brushes";
import { createPlayerStartEntity } from "../../src/entities/entity-instances";
import { createEmptySceneDocument } from "../../src/document/scene-document";
import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build";
describe("whitebox primitives runtime build", () => {
it("builds runtime meshes and colliders for wedge and cylinder solids", () => {
const document = createEmptySceneDocument({ name: "Primitive Runtime" });
const wedge = createWedgeBrush({
id: "brush-wedge-runtime",
center: { x: -2, y: 1, z: 0 }
});
const cylinder = createRadialPrismBrush({
id: "brush-cylinder-runtime",
center: { x: 2, y: 1, z: 0 },
sideCount: 12
});
const playerStart = createPlayerStartEntity({
id: "entity-player-start-primitives"
});
document.brushes[wedge.id] = wedge;
document.brushes[cylinder.id] = cylinder;
document.entities[playerStart.id] = playerStart;
const runtimeScene = buildRuntimeSceneFromDocument(document);
expect(runtimeScene.brushes.map((brush) => brush.kind)).toEqual([
"wedge",
"radialPrism"
]);
expect(runtimeScene.colliders).toHaveLength(2);
expect(
runtimeScene.colliders.every(
(collider) =>
collider.kind === "trimesh" &&
collider.source === "brush" &&
collider.vertices.length > 0 &&
collider.indices.length > 0
)
).toBe(true);
});
});

View File

@@ -0,0 +1,93 @@
import { describe, expect, it } from "vitest";
import {
createRadialPrismBrush,
createWedgeBrush,
deriveBrushSizeFromGeometry
} from "../../src/document/brushes";
import { buildBoxBrushDerivedMeshData, validateBoxBrushGeometry } from "../../src/geometry/box-brush-mesh";
import {
getBrushEdgeIds,
getBrushFaceIds,
getBrushVertexIds
} from "../../src/geometry/whitebox-topology";
describe("whitebox primitives", () => {
it("builds deterministic wedge topology and finite derived mesh data", () => {
const brush = createWedgeBrush({
id: "brush-wedge-test",
size: {
x: 4,
y: 2,
z: 6
}
});
const derivedMesh = buildBoxBrushDerivedMeshData(brush);
expect(getBrushFaceIds(brush)).toEqual([
"bottom",
"back",
"slope",
"left",
"right"
]);
expect(getBrushEdgeIds(brush)).toEqual([
"bottomBack",
"bottomFront",
"bottomLeft",
"bottomRight",
"topBack",
"leftBack",
"rightBack",
"leftSlope",
"rightSlope"
]);
expect(getBrushVertexIds(brush)).toEqual([
"negX_negY_negZ",
"posX_negY_negZ",
"negX_negY_posZ",
"posX_negY_posZ",
"negX_posY_negZ",
"posX_posY_negZ"
]);
expect(validateBoxBrushGeometry(brush)).toEqual([]);
expect(derivedMesh.faceIdsInOrder).toEqual(getBrushFaceIds(brush));
expect(Array.from(derivedMesh.colliderVertices).every(Number.isFinite)).toBe(
true
);
expect(Array.from(derivedMesh.colliderIndices).every(Number.isFinite)).toBe(
true
);
expect(deriveBrushSizeFromGeometry(brush.geometry)).toEqual(brush.size);
});
it("builds deterministic cylinder topology and finite collider buffers", () => {
const brush = createRadialPrismBrush({
id: "brush-cylinder-test",
sideCount: 12,
size: {
x: 4,
y: 3,
z: 4
}
});
const derivedMesh = buildBoxBrushDerivedMeshData(brush);
expect(getBrushFaceIds(brush)).toEqual([
"top",
"bottom",
...Array.from({ length: 12 }, (_, index) => `side-${index}`)
]);
expect(getBrushEdgeIds(brush)).toHaveLength(36);
expect(getBrushVertexIds(brush)).toHaveLength(24);
expect(validateBoxBrushGeometry(brush)).toEqual([]);
expect(derivedMesh.faceIdsInOrder).toEqual(getBrushFaceIds(brush));
expect(Array.from(derivedMesh.colliderVertices).every(Number.isFinite)).toBe(
true
);
expect(Array.from(derivedMesh.colliderIndices).every(Number.isFinite)).toBe(
true
);
expect(deriveBrushSizeFromGeometry(brush.geometry)).toEqual(brush.size);
});
});

View File

@@ -0,0 +1,31 @@
import { describe, expect, it } from "vitest";
import {
createRadialPrismBrush,
createWedgeBrush
} from "../../src/document/brushes";
import { createEmptySceneDocument } from "../../src/document/scene-document";
import {
parseSceneDocumentJson,
serializeSceneDocument
} from "../../src/serialization/scene-document-json";
describe("whitebox primitive scene JSON", () => {
it("round-trips wedge and cylinder brushes through scene JSON", () => {
const document = createEmptySceneDocument({ name: "Primitive Scene" });
const wedge = createWedgeBrush({
id: "brush-wedge-json"
});
const cylinder = createRadialPrismBrush({
id: "brush-cylinder-json",
sideCount: 12
});
document.brushes[wedge.id] = wedge;
document.brushes[cylinder.id] = cylinder;
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
document
);
});
});