Files
webeditor3d/tests/domain/box-brush-face-editing.command.test.ts

305 lines
8.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { createEditorStore } from "../../src/app/editor-store";
import { createCreateBoxBrushCommand } from "../../src/commands/create-box-brush-command";
import { createSetBoxBrushAllFaceMaterialsCommand } from "../../src/commands/set-box-brush-all-face-materials-command";
import { createSetBoxBrushFaceMaterialCommand } from "../../src/commands/set-box-brush-face-material-command";
import { createSetBoxBrushFaceUvStateCommand } from "../../src/commands/set-box-brush-face-uv-state-command";
import { createUpdateBoxBrushAllFaceUvsCommand } from "../../src/commands/update-box-brush-all-face-uvs-command";
import { BOX_FACE_IDS } from "../../src/document/brushes";
describe("box brush face editing commands", () => {
it("applies a material to one box face and supports undo/redo", () => {
const store = createEditorStore();
store.executeCommand(createCreateBoxBrushCommand());
const createdBrush = Object.values(store.getState().document.brushes)[0];
store.executeCommand(
createSetBoxBrushFaceMaterialCommand({
brushId: createdBrush.id,
faceId: "posZ",
materialId: "starter-amber-grid"
})
);
expect(store.getState().document.brushes[createdBrush.id].faces.posZ.materialId).toBe("starter-amber-grid");
expect(store.getState().selection).toEqual({
kind: "brushFace",
brushId: createdBrush.id,
faceId: "posZ"
});
expect(store.undo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posZ.materialId).toBeNull();
expect(store.redo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posZ.materialId).toBe("starter-amber-grid");
});
it("updates face UV state through an undoable command", () => {
const store = createEditorStore();
store.executeCommand(createCreateBoxBrushCommand());
const createdBrush = Object.values(store.getState().document.brushes)[0];
store.executeCommand(
createSetBoxBrushFaceUvStateCommand({
brushId: createdBrush.id,
faceId: "posY",
uvState: {
offset: {
x: 0.5,
y: -0.25
},
scale: {
x: 0.25,
y: 0.5
},
rotationQuarterTurns: 1,
flipU: true,
flipV: false
},
label: "Adjust top face UVs"
})
);
expect(store.getState().document.brushes[createdBrush.id].faces.posY.uv).toEqual({
offset: {
x: 0.5,
y: -0.25
},
scale: {
x: 0.25,
y: 0.5
},
rotationQuarterTurns: 1,
flipU: true,
flipV: false
});
expect(store.undo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posY.uv).toEqual({
offset: {
x: 0,
y: 0
},
scale: {
x: 1,
y: 1
},
rotationQuarterTurns: 0,
flipU: false,
flipV: false
});
expect(store.redo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posY.uv.rotationQuarterTurns).toBe(1);
expect(store.getState().document.brushes[createdBrush.id].faces.posY.uv.flipU).toBe(true);
});
it("applies one material across all box faces and restores prior face materials on undo", () => {
const store = createEditorStore();
store.executeCommand(createCreateBoxBrushCommand());
const createdBrush = Object.values(store.getState().document.brushes)[0];
store.executeCommand(
createSetBoxBrushFaceMaterialCommand({
brushId: createdBrush.id,
faceId: "posX",
materialId: "starter-hazard-stripe"
})
);
store.executeCommand(
createSetBoxBrushFaceMaterialCommand({
brushId: createdBrush.id,
faceId: "negY",
materialId: "starter-concrete-checker"
})
);
store.setSelection({
kind: "brushes",
ids: [createdBrush.id]
});
store.executeCommand(
createSetBoxBrushAllFaceMaterialsCommand({
brushId: createdBrush.id,
materialId: "starter-amber-grid"
})
);
BOX_FACE_IDS.forEach((faceId) => {
expect(
store.getState().document.brushes[createdBrush.id].faces[faceId]
.materialId
).toBe("starter-amber-grid");
});
expect(store.getState().selection).toEqual({
kind: "brushes",
ids: [createdBrush.id]
});
expect(store.undo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posX.materialId).toBe("starter-hazard-stripe");
expect(store.getState().document.brushes[createdBrush.id].faces.negY.materialId).toBe("starter-concrete-checker");
expect(store.getState().document.brushes[createdBrush.id].faces.posZ.materialId).toBeNull();
expect(store.redo()).toBe(true);
BOX_FACE_IDS.forEach((faceId) => {
expect(
store.getState().document.brushes[createdBrush.id].faces[faceId]
.materialId
).toBe("starter-amber-grid");
});
});
it("updates all face UV states and restores prior per-face transforms on undo", () => {
const store = createEditorStore();
store.executeCommand(createCreateBoxBrushCommand());
const createdBrush = Object.values(store.getState().document.brushes)[0];
store.executeCommand(
createSetBoxBrushFaceUvStateCommand({
brushId: createdBrush.id,
faceId: "posX",
uvState: {
offset: {
x: 0.25,
y: -0.5
},
scale: {
x: 2,
y: 0.5
},
rotationQuarterTurns: 1,
flipU: false,
flipV: true
},
label: "Seed right face UVs"
})
);
store.executeCommand(
createSetBoxBrushFaceUvStateCommand({
brushId: createdBrush.id,
faceId: "negZ",
uvState: {
offset: {
x: -0.125,
y: 0.375
},
scale: {
x: 0.75,
y: 1.25
},
rotationQuarterTurns: 3,
flipU: true,
flipV: false
},
label: "Seed back face UVs"
})
);
store.setSelection({
kind: "brushes",
ids: [createdBrush.id]
});
store.executeCommand(
createUpdateBoxBrushAllFaceUvsCommand({
brushId: createdBrush.id,
label: "Normalize solid UVs",
updateUvState: (uvState, faceId) => ({
...uvState,
offset: {
x: BOX_FACE_IDS.indexOf(faceId as (typeof BOX_FACE_IDS)[number]),
y: uvState.offset.y + 1
},
scale: {
x: 0.5,
y: 0.75
},
rotationQuarterTurns: 2,
flipU: true,
flipV: false
})
})
);
BOX_FACE_IDS.forEach((faceId, index) => {
const nextUv =
store.getState().document.brushes[createdBrush.id].faces[faceId].uv;
expect(nextUv.offset.x).toBe(index);
expect(nextUv.scale).toEqual({
x: 0.5,
y: 0.75
});
expect(nextUv.rotationQuarterTurns).toBe(2);
expect(nextUv.flipU).toBe(true);
expect(nextUv.flipV).toBe(false);
});
expect(store.getState().selection).toEqual({
kind: "brushes",
ids: [createdBrush.id]
});
expect(store.undo()).toBe(true);
expect(store.getState().document.brushes[createdBrush.id].faces.posX.uv).toEqual({
offset: {
x: 0.25,
y: -0.5
},
scale: {
x: 2,
y: 0.5
},
rotationQuarterTurns: 1,
flipU: false,
flipV: true
});
expect(store.getState().document.brushes[createdBrush.id].faces.negZ.uv).toEqual({
offset: {
x: -0.125,
y: 0.375
},
scale: {
x: 0.75,
y: 1.25
},
rotationQuarterTurns: 3,
flipU: true,
flipV: false
});
expect(store.getState().document.brushes[createdBrush.id].faces.posY.uv).toEqual({
offset: {
x: 0,
y: 0
},
scale: {
x: 1,
y: 1
},
rotationQuarterTurns: 0,
flipU: false,
flipV: false
});
expect(store.redo()).toBe(true);
BOX_FACE_IDS.forEach((faceId, index) => {
const nextUv =
store.getState().document.brushes[createdBrush.id].faces[faceId].uv;
expect(nextUv.offset.x).toBe(index);
expect(nextUv.rotationQuarterTurns).toBe(2);
expect(nextUv.flipU).toBe(true);
expect(nextUv.flipV).toBe(false);
});
});
});