import { BufferGeometry, Float32BufferAttribute } from "three"; import { describe, expect, it } from "vitest"; import type { DungeonTraversalRamp } from "./dungeonTypes"; import { bridgeDungeonStairNavigation, smoothDungeonStairGeometry } from "./dungeonTraversalRamps"; const RAMP: DungeonTraversalRamp = { id: "stairs", start: [0, 0, 0], end: [0, 2, 6], width: 4, navigationBridge: { linkId: "stair-seam", startPortal: [[-1, 0.5, 2], [1, 0.5, 2]], endPortal: [[-1, 1, 3], [1, 1, 3]], }, }; describe("reviewed dungeon stair proxies", () => { it("smooths tread and riser vertices but preserves upper walls, ceilings and geometry outside the flight", () => { const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([ 0, 0.5, 2, 0, 1, 2, 0, 3, 2, 0, 5, 2, 3, 0.5, 2, 0, 0.5, -1, ], 3)); smoothDungeonStairGeometry(geometry, [RAMP]); const position = geometry.getAttribute("position"); expect(position.getY(0)).toBeCloseTo(2 / 3); expect(position.getY(1)).toBeCloseTo(2 / 3); expect([2, 3, 4, 5].map((vertex) => position.getY(vertex))).toEqual([3, 5, 0.5, 0.5]); geometry.dispose(); }); it("bridges exactly the existing reviewed portal vertices and rejects stale asset coordinates", () => { const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([ ...RAMP.navigationBridge!.startPortal.flat(), ...RAMP.navigationBridge!.endPortal.flat(), ], 3)); geometry.setIndex([]); smoothDungeonStairGeometry(geometry, [RAMP], 0.05); bridgeDungeonStairNavigation(geometry, [RAMP], 0.05); expect(Array.from(geometry.index!.array)).toEqual([0, 1, 3, 0, 3, 2]); geometry.getAttribute("position").setX(0, -1.1); expect(() => bridgeDungeonStairNavigation(geometry, [RAMP], 0.05)) .toThrow("reviewed navigation portal no longer matches the source GLB"); geometry.dispose(); }); });