1import { expect, it } from "vite-plus/test";
2import { Quaternion, Vector3 } from "three";
3import { duoViewSnaps, nearestDuoView } from "./duoSnap.ts";
4
5const frames = [
6 {
7 face: "inside" as const,
8 normal: new Vector3(0, 0, 1),
9 up: new Vector3(0, 1, 0),
10 center: new Vector3(),
11 },
12];
13const rotation = (x: number, y: number, z = 0) =>
14 new Quaternion().setFromAxisAngle(new Vector3(x, y, z).normalize(), Math.hypot(x, y, z));
15
16it("chooses a leaf-focused seated view when it is closer than the middle of the fold", () => {
17 const leaf = {
18 face: "right" as const,
19 normal: new Vector3(0.6, 0, 0.8),
20 up: new Vector3(0, 1, 0),
21 center: new Vector3(1, 0, 0),
22 };
23 const candidates = duoViewSnaps([...frames, leaf], 3);
24 const seat = candidates.find(
25 (candidate) => candidate.face === "right" && candidate.orientation === "portrait",
26 )!;
27 const released = seat.rotation.clone().premultiply(rotation(0.04, 0.05));
28 const chosen = nearestDuoView(released, candidates)!;
29 expect(chosen.face).toBe("right");
30 expect(chosen.orientation).toBe("portrait");
31 expect(chosen.center.x).toBe(1);
32});
33
34it("a seated view exposes the base, and upright yaw keeps both inner displays facing the camera", () => {
35 const folded = [
36 frames[0]!,
37 ...(["left", "right"] as const).map((face) => ({
38 face,
39 normal: new Vector3(face === "left" ? Math.SQRT1_2 : -Math.SQRT1_2, 0, Math.SQRT1_2),
40 up: new Vector3(0, 1, 0),
41 center: new Vector3(face === "left" ? -1 : 1, 0, 0),
42 })),
43 ];
44 const candidates = duoViewSnaps(folded, 3);
45 expect(candidates.filter((candidate) => candidate.face === "right")).toHaveLength(1);
46 for (const candidate of candidates) {
47 for (const frame of folded.slice(1))
48 expect(frame.normal.clone().applyQuaternion(candidate.rotation).z).toBeGreaterThan(0.2);
49 const side = candidate.rotation.clone().premultiply(rotation(0, 1.5));
50 const chosen = nearestDuoView(side, [candidate])!;
51 for (const frame of folded.slice(1))
52 expect(frame.normal.clone().applyQuaternion(chosen.rotation).z).toBeGreaterThan(0.02);
53 }
54});