import { describe, expect, it } from "vitest"; import { createModelInstance } from "../../src/assets/model-instances"; import { createBoxBrush } from "../../src/document/brushes"; import { createScenePath } from "../../src/document/paths"; import { createEmptySceneDocument } from "../../src/document/scene-document"; import { createTerrain } from "../../src/document/terrains"; import { createCameraRigEntity, createPointLightEntity, createPlayerStartEntity, createSpotLightEntity, createTriggerVolumeEntity } from "../../src/entities/entity-instances"; import { resolveViewportFocusTarget } from "../../src/viewport-three/viewport-focus"; describe("resolveViewportFocusTarget", () => { it("frames the selected brush", () => { const brush = createBoxBrush({ id: "brush-room", center: { x: 3, y: 2, z: -1 }, size: { x: 6, y: 4, z: 2 } }); const document = { ...createEmptySceneDocument(), brushes: { [brush.id]: brush } }; expect( resolveViewportFocusTarget(document, { kind: "brushes", ids: [brush.id] }) ).toEqual({ center: { x: 3, y: 2, z: -1 }, radius: Math.hypot(6, 4, 2) * 0.5 }); }); it("frames rotated whitebox boxes around their authored center with a stable object radius", () => { const brush = createBoxBrush({ id: "brush-rotated-room", center: { x: 1.25, y: 1.5, z: -0.75 }, rotationDegrees: { x: 0, y: 45, z: 0 }, size: { x: 2, y: 2, z: 4 } }); const document = { ...createEmptySceneDocument(), brushes: { [brush.id]: brush } }; expect( resolveViewportFocusTarget(document, { kind: "brushes", ids: [brush.id] }) ).toEqual({ center: { x: 1.25, y: 1.5, z: -0.75 }, radius: Math.hypot(2, 2, 4) * 0.5 }); }); it("frames the owning brush when a face is selected", () => { const brush = createBoxBrush({ id: "brush-face-room" }); const document = { ...createEmptySceneDocument(), brushes: { [brush.id]: brush } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "brushFace", brushId: brush.id, faceId: "posZ" }); expect(focusTarget?.center).toEqual(brush.center); expect(focusTarget?.radius).toBe(Math.hypot(2, 2, 2) * 0.5); }); it("frames the selected Player Start helper", () => { const playerStart = createPlayerStartEntity({ id: "entity-player-start-main", position: { x: 4, y: 0, z: -2 }, yawDegrees: 90 }); const document = { ...createEmptySceneDocument(), entities: { [playerStart.id]: playerStart } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [playerStart.id] }); expect(focusTarget?.center).toEqual({ x: 4, y: 0.3, z: -2 }); expect(focusTarget?.radius).toBeGreaterThan(0.6); }); it("frames the selected Point Light helper", () => { const pointLight = createPointLightEntity({ id: "entity-point-light-main", position: { x: 2, y: 3, z: -1 }, distance: 8 }); const document = { ...createEmptySceneDocument(), entities: { [pointLight.id]: pointLight } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [pointLight.id] }); expect(focusTarget).toEqual({ center: { x: 2, y: 3, z: -1 }, radius: 8 }); }); it("frames the selected Camera Rig helper", () => { const cameraRig = createCameraRigEntity({ id: "entity-camera-rig-focus", position: { x: -3, y: 2, z: 5 } }); const document = { ...createEmptySceneDocument(), entities: { [cameraRig.id]: cameraRig } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [cameraRig.id] }); expect(focusTarget?.center.x).toBeCloseTo(-3); expect(focusTarget?.center.y).toBeCloseTo(2.28); expect(focusTarget?.center.z).toBeCloseTo(5); expect(focusTarget?.radius).toBeGreaterThan(0.45); }); it("frames the selected rail Camera Rig helper from its resolved path position", () => { const path = createScenePath({ id: "path-camera-rig-focus-rail", points: [ { id: "point-a", position: { x: 0, y: 2, z: 0 } }, { id: "point-b", position: { x: 10, y: 2, z: 0 } } ] }); const cameraRig = createCameraRigEntity({ id: "entity-camera-rig-focus-rail", rigType: "rail", pathId: path.id, target: { kind: "worldPoint", point: { x: 3, y: 1, z: 4 } } }); const document = { ...createEmptySceneDocument(), paths: { [path.id]: path }, entities: { [cameraRig.id]: cameraRig } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [cameraRig.id] }); expect(focusTarget?.center.x).toBeCloseTo(3); expect(focusTarget?.center.y).toBeCloseTo(2.28); expect(focusTarget?.center.z).toBeCloseTo(0); expect(focusTarget?.radius).toBeGreaterThan(0.45); }); it("frames the selected Path around its authored point bounds", () => { const path = createScenePath({ id: "path-focus", points: [ { id: "point-a", position: { x: -2, y: 0, z: 1 } }, { id: "point-b", position: { x: 4, y: 2, z: 3 } } ] }); const document = { ...createEmptySceneDocument(), paths: { [path.id]: path } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "paths", ids: [path.id] }); expect(focusTarget).toEqual({ center: { x: 1, y: 1, z: 2 }, radius: Math.hypot(6, 2, 2) * 0.5 }); }); it("frames the selected terrain from its authored grid bounds", () => { const terrain = createTerrain({ id: "terrain-focus", position: { x: -4, y: 2, z: -2 }, sampleCountX: 3, sampleCountZ: 2, cellSize: 2, heights: [0, 1, 2, -1, 0, 1] }); const document = { ...createEmptySceneDocument(), terrains: { [terrain.id]: terrain } }; expect( resolveViewportFocusTarget(document, { kind: "terrains", ids: [terrain.id] }) ).toEqual({ center: { x: -2, y: 2.5, z: -1 }, radius: Math.max(0.5, Math.hypot(4, 3, 2) * 0.5) }); }); it("frames a selected Path Point tightly around its authored position", () => { const path = createScenePath({ id: "path-point-focus", points: [ { id: "point-focus-a", position: { x: -2, y: 0, z: 1 } }, { id: "point-focus-b", position: { x: 4, y: 2, z: 3 } } ] }); const document = { ...createEmptySceneDocument(), paths: { [path.id]: path } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "pathPoint", pathId: path.id, pointId: path.points[1].id }); expect(focusTarget).toEqual({ center: { x: 4, y: 2, z: 3 }, radius: 0.5 }); }); it("frames the selected Spot Light helper", () => { const spotLight = createSpotLightEntity({ id: "entity-spot-light-main", position: { x: -2, y: 4, z: 1 }, distance: 12 }); const document = { ...createEmptySceneDocument(), entities: { [spotLight.id]: spotLight } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [spotLight.id] }); expect(focusTarget).toEqual({ center: { x: -2, y: 4, z: 1 }, radius: 12 }); }); it("frames a selected Trigger Volume around its authored bounds", () => { const triggerVolume = createTriggerVolumeEntity({ id: "entity-trigger-main", position: { x: 3, y: 2, z: -1 }, size: { x: 4, y: 6, z: 2 } }); const document = { ...createEmptySceneDocument(), entities: { [triggerVolume.id]: triggerVolume } }; const focusTarget = resolveViewportFocusTarget(document, { kind: "entities", ids: [triggerVolume.id] }); expect(focusTarget).toEqual({ center: { x: 3, y: 2, z: -1 }, radius: Math.hypot(2, 3, 1) }); }); it("frames multiple selected model instances around their combined authored bounds", () => { const modelInstanceA = createModelInstance({ id: "model-focus-a", assetId: "asset-model-focus", position: { x: 0, y: 0, z: 0 }, scale: { x: 2, y: 2, z: 2 } }); const modelInstanceB = createModelInstance({ id: "model-focus-b", assetId: "asset-model-focus", position: { x: 6, y: 0, z: 0 }, scale: { x: 2, y: 2, z: 2 } }); const document = { ...createEmptySceneDocument(), modelInstances: { [modelInstanceA.id]: modelInstanceA, [modelInstanceB.id]: modelInstanceB } }; expect( resolveViewportFocusTarget(document, { kind: "modelInstances", ids: [modelInstanceA.id, modelInstanceB.id] }) ).toEqual({ center: { x: 3, y: 0, z: 0 }, radius: Math.hypot(8, 2, 2) * 0.5 }); }); it("frames the authored scene when nothing is selected and returns null when the scene is empty", () => { const brush = createBoxBrush({ id: "brush-room" }); const populatedDocument = { ...createEmptySceneDocument(), brushes: { [brush.id]: brush } }; expect(resolveViewportFocusTarget(populatedDocument, { kind: "none" })).toEqual({ center: { x: 0, y: 1, z: 0 }, radius: Math.hypot(2, 2, 2) * 0.5 }); expect(resolveViewportFocusTarget(createEmptySceneDocument(), { kind: "none" })).toBeNull(); }); });