Files
cdsl-cad/frontend/src/viewer-runtime/common/camera.test.js
T
2026-08-19 19:34:30 +08:00

99 lines
3.2 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
cameraSpecUsesPerspectiveProjection,
normalizeCameraSpec,
resolveCameraSnapshot
} from "./camera.js";
import {
RENDER_SCENE_SCALE
} from "./renderOptions.js";
const SCALE_SETTINGS = Object.freeze({
[RENDER_SCENE_SCALE.CAD]: Object.freeze({
minBoundsSpan: 1,
minModelRadius: 1,
minFloorSize: 100,
minCameraDistance: 10,
minCameraFar: 1000
}),
[RENDER_SCENE_SCALE.URDF]: Object.freeze({
minBoundsSpan: 0.05,
minModelRadius: 0.05,
minFloorSize: 0.05,
minCameraDistance: 0.5,
minCameraFar: 10
})
});
function assertClose(actual, expected, epsilon = 1e-6) {
assert.ok(Math.abs(actual - expected) <= epsilon, `${actual} !== ${expected}`);
}
test("camera preset strings expand like JSON preset specs", () => {
const stringSpec = normalizeCameraSpec("iso");
const jsonSpec = normalizeCameraSpec({ preset: "iso" });
assert.equal(stringSpec.name, jsonSpec.name);
assert.equal(stringSpec.preset, jsonSpec.preset);
assert.deepEqual(stringSpec.direction, jsonSpec.direction);
assert.deepEqual(stringSpec.up, jsonSpec.up);
assert.equal(stringSpec.zoom, jsonSpec.zoom);
});
test("camera JSON preset overrides merge onto the preset defaults", () => {
const spec = normalizeCameraSpec({
preset: "top",
up: [0, 0, 1],
zoom: 1.4
});
assert.equal(spec.name, "top");
assert.deepEqual(spec.direction, [0, 0, 1]);
assert.deepEqual(spec.up, [0, 0, 1]);
assert.equal(spec.zoom, 1.4);
assert.equal(spec.hasExplicitZoom, true);
});
test("explicit camera state passes through resolved snapshots", () => {
const snapshot = resolveCameraSnapshot({
position: [10, 20, 30],
target: [1, 2, 3],
up: [0, 0, 1],
zoom: 1.4
}, { min: [0, 0, 0], max: [2, 4, 6] }, {
sceneScale: RENDER_SCENE_SCALE.CAD,
settingsByScale: SCALE_SETTINGS
});
assert.deepEqual(snapshot.position, [10, 20, 30]);
assert.deepEqual(snapshot.target, [1, 2, 3]);
assert.deepEqual(snapshot.up, [0, 0, 1]);
assert.equal(snapshot.zoom, 1.4);
assert.equal(cameraSpecUsesPerspectiveProjection({ position: [10, 20, 30] }), true);
});
test("omitted camera fields derive from model bounds", () => {
const snapshot = resolveCameraSnapshot({ preset: "top" }, { min: [0, 0, 0], max: [2, 4, 6] }, {
sceneScale: RENDER_SCENE_SCALE.CAD,
settingsByScale: SCALE_SETTINGS
});
assert.deepEqual(snapshot.target, [1, 2, 3]);
assertClose(snapshot.position[0], 1);
assertClose(snapshot.position[1], 2);
assert.ok(snapshot.position[2] > 3);
assert.deepEqual(snapshot.up, [0, 1, 0]);
assert.equal(snapshot.zoom, 1);
assert.equal(cameraSpecUsesPerspectiveProjection({ preset: "top" }), false);
});
test("invalid camera specs fail clearly", () => {
assert.throws(() => normalizeCameraSpec({ preset: "wat" }), /Unknown camera preset/);
assert.throws(() => normalizeCameraSpec({ position: [1, 2, "x"] }), /camera.position/);
assert.throws(() => normalizeCameraSpec({ up: [0, 0, 0] }), /camera.up/);
assert.throws(() => normalizeCameraSpec({ zoom: 0 }), /camera.zoom/);
assert.throws(() => normalizeCameraSpec({ preset: "iso", extra: true }), /Unsupported camera fields/);
});