@ume-group/contracts 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/adserving.d.ts +150 -0
- package/dist/adserving.d.ts.map +1 -0
- package/dist/adserving.js +8 -0
- package/dist/campaigns.d.ts +37 -0
- package/dist/campaigns.d.ts.map +1 -0
- package/dist/campaigns.js +8 -0
- package/dist/gausst.d.ts +236 -0
- package/dist/gausst.d.ts.map +1 -0
- package/dist/gausst.js +307 -0
- package/dist/gausst.test.d.ts +2 -0
- package/dist/gausst.test.d.ts.map +1 -0
- package/dist/gausst.test.js +71 -0
- package/dist/index.d.ts +1531 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1112 -0
- package/dist/layer2/index.d.ts +9 -0
- package/dist/layer2/index.d.ts.map +1 -0
- package/dist/layer2/index.js +10 -0
- package/dist/layer2/shaders.d.ts +185 -0
- package/dist/layer2/shaders.d.ts.map +1 -0
- package/dist/layer2/shaders.js +604 -0
- package/dist/layer2/webcam-utils.d.ts +113 -0
- package/dist/layer2/webcam-utils.d.ts.map +1 -0
- package/dist/layer2/webcam-utils.js +147 -0
- package/dist/layer2/webcam-utils.test.d.ts +2 -0
- package/dist/layer2/webcam-utils.test.d.ts.map +1 -0
- package/dist/layer2/webcam-utils.test.js +18 -0
- package/dist/layer2.d.ts +558 -0
- package/dist/layer2.d.ts.map +1 -0
- package/dist/layer2.js +376 -0
- package/dist/layer2.test.d.ts +2 -0
- package/dist/layer2.test.d.ts.map +1 -0
- package/dist/layer2.test.js +65 -0
- package/dist/perspective.d.ts +28 -0
- package/dist/perspective.d.ts.map +1 -0
- package/dist/perspective.js +157 -0
- package/dist/segmentation/MediaPipeSegmenter.d.ts +201 -0
- package/dist/segmentation/MediaPipeSegmenter.d.ts.map +1 -0
- package/dist/segmentation/MediaPipeSegmenter.js +434 -0
- package/dist/segmentation/index.d.ts +5 -0
- package/dist/segmentation/index.d.ts.map +1 -0
- package/dist/segmentation/index.js +4 -0
- package/dist/webcam/GarbageMatteDragManager.d.ts +63 -0
- package/dist/webcam/GarbageMatteDragManager.d.ts.map +1 -0
- package/dist/webcam/GarbageMatteDragManager.js +183 -0
- package/dist/webcam/WebcamStreamManager.d.ts +103 -0
- package/dist/webcam/WebcamStreamManager.d.ts.map +1 -0
- package/dist/webcam/WebcamStreamManager.js +356 -0
- package/dist/webcam/index.d.ts +5 -0
- package/dist/webcam/index.d.ts.map +1 -0
- package/dist/webcam/index.js +2 -0
- package/openapi/admetise.yaml +632 -0
- package/openapi/includu.yaml +621 -0
- package/openapi/integration.yaml +372 -0
- package/openapi/shared/schemas.yaml +227 -0
- package/package.json +53 -0
package/dist/gausst.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gausst Coordinate System Utilities
|
|
3
|
+
*
|
|
4
|
+
* Implements the coordinate system from Patent US8761580B2 and the
|
|
5
|
+
* legacy Gausst Maya plugin (gausstCamera.cpp).
|
|
6
|
+
*
|
|
7
|
+
* This system enables accurate camera matching between physical tracked
|
|
8
|
+
* cameras and virtual 3D scenes.
|
|
9
|
+
*
|
|
10
|
+
* Key features:
|
|
11
|
+
* - 35mm full-frame equivalent film gate (1.417" × 0.945")
|
|
12
|
+
* - FOV ↔ focal length conversion
|
|
13
|
+
* - Gausst rotation order: Tilt → Pan (negated) → Roll (negated)
|
|
14
|
+
* - Video plane sizing at distance
|
|
15
|
+
*/
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Constants (from legacy gausstCamera.cpp)
|
|
18
|
+
// =============================================================================
|
|
19
|
+
export const GAUSST = {
|
|
20
|
+
// 35mm full-frame equivalent film gate
|
|
21
|
+
// From: MDistance(1.417, MDistance::uiUnit()) horizontal
|
|
22
|
+
// From: MDistance(.945, MDistance::uiUnit()) vertical
|
|
23
|
+
HORIZONTAL_APERTURE: 1.417, // inches (≈36mm)
|
|
24
|
+
VERTICAL_APERTURE: 0.945, // inches (≈24mm)
|
|
25
|
+
// Default values from legacy plugin
|
|
26
|
+
DEFAULT_FOCAL_LENGTH: 35, // mm
|
|
27
|
+
VIDEO_PLANE_DISTANCE: 500, // Maya units
|
|
28
|
+
DEFAULT_OVERSCAN: 1.3,
|
|
29
|
+
// Conversion constants
|
|
30
|
+
DEG_TO_RAD: Math.PI / 180,
|
|
31
|
+
RAD_TO_DEG: 180 / Math.PI,
|
|
32
|
+
MM_PER_INCH: 25.4,
|
|
33
|
+
// Default camera position (eye level, standing human)
|
|
34
|
+
DEFAULT_CAMERA_POSITION: [0, 1.6, 0],
|
|
35
|
+
DEFAULT_FOV: 60
|
|
36
|
+
};
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// FOV ↔ Focal Length Conversions
|
|
39
|
+
// =============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Convert field of view (degrees) to focal length (mm)
|
|
42
|
+
*
|
|
43
|
+
* Formula from legacy gausstCamera.h:
|
|
44
|
+
* focalLength = ((aperture / 2) / tan(fieldOfView / 2)) * 25.4
|
|
45
|
+
*
|
|
46
|
+
* @param fovDegrees - Horizontal field of view in degrees
|
|
47
|
+
* @param apertureInches - Horizontal film aperture in inches (default: 35mm equivalent)
|
|
48
|
+
* @returns Focal length in millimeters
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* fovToFocalLength(60) // ≈ 31mm
|
|
52
|
+
* fovToFocalLength(90) // ≈ 18mm
|
|
53
|
+
* fovToFocalLength(45) // ≈ 43mm
|
|
54
|
+
*/
|
|
55
|
+
export function fovToFocalLength(fovDegrees, apertureInches = GAUSST.HORIZONTAL_APERTURE) {
|
|
56
|
+
const fovRadians = fovDegrees * GAUSST.DEG_TO_RAD;
|
|
57
|
+
return ((apertureInches / 2) / Math.tan(fovRadians / 2)) * GAUSST.MM_PER_INCH;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Convert focal length (mm) to field of view (degrees)
|
|
61
|
+
*
|
|
62
|
+
* Inverse of fovToFocalLength
|
|
63
|
+
*
|
|
64
|
+
* @param focalLengthMM - Focal length in millimeters
|
|
65
|
+
* @param apertureInches - Horizontal film aperture in inches (default: 35mm equivalent)
|
|
66
|
+
* @returns Horizontal field of view in degrees
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* focalLengthToFov(35) // ≈ 54.4°
|
|
70
|
+
* focalLengthToFov(50) // ≈ 39.6°
|
|
71
|
+
* focalLengthToFov(24) // ≈ 73.7°
|
|
72
|
+
*/
|
|
73
|
+
export function focalLengthToFov(focalLengthMM, apertureInches = GAUSST.HORIZONTAL_APERTURE) {
|
|
74
|
+
const fovRadians = 2 * Math.atan((apertureInches / 2 * GAUSST.MM_PER_INCH) / focalLengthMM);
|
|
75
|
+
return fovRadians * GAUSST.RAD_TO_DEG;
|
|
76
|
+
}
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// Rotation Utilities
|
|
79
|
+
// =============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Convert Gausst rotation (tilt, pan, roll) to Euler angles for Three.js
|
|
82
|
+
*
|
|
83
|
+
* From legacy gausstCamera.cpp moveTheCamera():
|
|
84
|
+
* - Maya rotation order: tilt, pan, roll
|
|
85
|
+
* - Pan and Roll are NEGATED before application
|
|
86
|
+
* - Three.js equivalent: 'YXZ' Euler order
|
|
87
|
+
*
|
|
88
|
+
* @param tiltDeg - Tilt (pitch) in degrees - rotation around X axis
|
|
89
|
+
* @param panDeg - Pan (yaw) in degrees - rotation around Y axis (will be negated)
|
|
90
|
+
* @param rollDeg - Roll in degrees - rotation around Z axis (will be negated)
|
|
91
|
+
* @returns Object with euler values and order for Three.js
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const { x, y, z, order } = gausst RotationToEuler(10, 20, 5);
|
|
95
|
+
* mesh.rotation.set(x, y, z);
|
|
96
|
+
* mesh.rotation.order = order;
|
|
97
|
+
*/
|
|
98
|
+
export function gausstRotationToEuler(tiltDeg, panDeg, rollDeg) {
|
|
99
|
+
return {
|
|
100
|
+
x: tiltDeg * GAUSST.DEG_TO_RAD, // Tilt (unchanged)
|
|
101
|
+
y: -panDeg * GAUSST.DEG_TO_RAD, // Pan (NEGATED)
|
|
102
|
+
z: -rollDeg * GAUSST.DEG_TO_RAD, // Roll (NEGATED)
|
|
103
|
+
order: 'YXZ'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Convert Three.js Euler angles back to Gausst rotation
|
|
108
|
+
*
|
|
109
|
+
* Inverse of gausst RotationToEuler - extracts tilt, pan, roll from Euler
|
|
110
|
+
* Assumes the object uses 'YXZ' rotation order
|
|
111
|
+
*
|
|
112
|
+
* @param x - Euler X rotation in radians
|
|
113
|
+
* @param y - Euler Y rotation in radians
|
|
114
|
+
* @param z - Euler Z rotation in radians
|
|
115
|
+
* @returns Gausst rotation as [tilt, pan, roll] in degrees
|
|
116
|
+
*/
|
|
117
|
+
export function eulerToGausstRotation(x, y, z) {
|
|
118
|
+
return [
|
|
119
|
+
x * GAUSST.RAD_TO_DEG, // Tilt
|
|
120
|
+
-y * GAUSST.RAD_TO_DEG, // Pan (negate back)
|
|
121
|
+
-z * GAUSST.RAD_TO_DEG // Roll (negate back)
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// Video Plane Calculations
|
|
126
|
+
// =============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* Calculate video plane dimensions at a given distance from camera
|
|
129
|
+
*
|
|
130
|
+
* From legacy gausstCamera.cpp draw():
|
|
131
|
+
* horizontalSize = videoPolygonDistance * horizontalFilmApertureValue * 25.4f / focalLengthValue;
|
|
132
|
+
* verticalSize = videoPolygonDistance * verticalFilmApertureValue * 25.4f / focalLengthValue;
|
|
133
|
+
*
|
|
134
|
+
* @param distance - Distance from camera in world units
|
|
135
|
+
* @param focalLengthMM - Focal length in millimeters
|
|
136
|
+
* @param horizontalAperture - Horizontal film aperture in inches
|
|
137
|
+
* @param verticalAperture - Vertical film aperture in inches
|
|
138
|
+
* @returns Width and height of video plane at that distance
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // Video plane at 10m with 35mm lens
|
|
142
|
+
* const { width, height } = getVideoPlaneSize(10, 35);
|
|
143
|
+
* // width ≈ 10.27m, height ≈ 6.85m
|
|
144
|
+
*/
|
|
145
|
+
export function getVideoPlaneSize(distance, focalLengthMM, horizontalAperture = GAUSST.HORIZONTAL_APERTURE, verticalAperture = GAUSST.VERTICAL_APERTURE) {
|
|
146
|
+
const width = (distance * horizontalAperture * GAUSST.MM_PER_INCH) / focalLengthMM;
|
|
147
|
+
const height = (distance * verticalAperture * GAUSST.MM_PER_INCH) / focalLengthMM;
|
|
148
|
+
return { width, height };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Calculate video plane dimensions using FOV instead of focal length
|
|
152
|
+
*
|
|
153
|
+
* @param distance - Distance from camera in world units
|
|
154
|
+
* @param fovDegrees - Horizontal field of view in degrees
|
|
155
|
+
* @returns Width and height of video plane at that distance
|
|
156
|
+
*/
|
|
157
|
+
export function getVideoPlaneSizeFromFov(distance, fovDegrees) {
|
|
158
|
+
const focalLength = fovToFocalLength(fovDegrees);
|
|
159
|
+
return getVideoPlaneSize(distance, focalLength);
|
|
160
|
+
}
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// Position Conversions
|
|
163
|
+
// =============================================================================
|
|
164
|
+
/**
|
|
165
|
+
* Convert tracking packet position to world coordinates
|
|
166
|
+
*
|
|
167
|
+
* From legacy gausstCamera.cpp moveTheCamera():
|
|
168
|
+
* Xpos = convert4bytesToFloat(&theBuffer[6]) / 1000;
|
|
169
|
+
* (Position comes as mm × 100, divide by 1000 to get meters)
|
|
170
|
+
*
|
|
171
|
+
* @param packetValue - Raw value from tracking packet (mm × 100)
|
|
172
|
+
* @returns Position in meters
|
|
173
|
+
*/
|
|
174
|
+
export function trackingPacketToMeters(packetValue) {
|
|
175
|
+
return packetValue / 100000; // mm×100 → meters
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Convert meters to tracking packet format
|
|
179
|
+
*
|
|
180
|
+
* @param meters - Position in meters
|
|
181
|
+
* @returns Raw value for tracking packet (mm × 100)
|
|
182
|
+
*/
|
|
183
|
+
export function metersToTrackingPacket(meters) {
|
|
184
|
+
return Math.round(meters * 100000);
|
|
185
|
+
}
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// Aspect Ratio Utilities
|
|
188
|
+
// =============================================================================
|
|
189
|
+
/**
|
|
190
|
+
* Get the default aspect ratio from Gausst film gate
|
|
191
|
+
*
|
|
192
|
+
* @returns Aspect ratio (width / height)
|
|
193
|
+
*/
|
|
194
|
+
export function getGausstAspectRatio() {
|
|
195
|
+
return GAUSST.HORIZONTAL_APERTURE / GAUSST.VERTICAL_APERTURE;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Calculate vertical FOV from horizontal FOV using Gausst aspect ratio
|
|
199
|
+
*
|
|
200
|
+
* @param horizontalFovDeg - Horizontal field of view in degrees
|
|
201
|
+
* @returns Vertical field of view in degrees
|
|
202
|
+
*/
|
|
203
|
+
export function horizontalToVerticalFov(horizontalFovDeg) {
|
|
204
|
+
const aspectRatio = getGausstAspectRatio();
|
|
205
|
+
const hFovRad = horizontalFovDeg * GAUSST.DEG_TO_RAD;
|
|
206
|
+
const vFovRad = 2 * Math.atan(Math.tan(hFovRad / 2) / aspectRatio);
|
|
207
|
+
return vFovRad * GAUSST.RAD_TO_DEG;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Calculate horizontal FOV from vertical FOV using Gausst aspect ratio
|
|
211
|
+
*
|
|
212
|
+
* @param verticalFovDeg - Vertical field of view in degrees
|
|
213
|
+
* @returns Horizontal field of view in degrees
|
|
214
|
+
*/
|
|
215
|
+
export function verticalToHorizontalFov(verticalFovDeg) {
|
|
216
|
+
const aspectRatio = getGausstAspectRatio();
|
|
217
|
+
const vFovRad = verticalFovDeg * GAUSST.DEG_TO_RAD;
|
|
218
|
+
const hFovRad = 2 * Math.atan(Math.tan(vFovRad / 2) * aspectRatio);
|
|
219
|
+
return hFovRad * GAUSST.RAD_TO_DEG;
|
|
220
|
+
}
|
|
221
|
+
// =============================================================================
|
|
222
|
+
// Three.js Integration Helpers
|
|
223
|
+
// =============================================================================
|
|
224
|
+
/**
|
|
225
|
+
* Apply Gausst rotation directly to a Three.js Object3D
|
|
226
|
+
*
|
|
227
|
+
* This is the primary function for setting rotation on 3D objects
|
|
228
|
+
* using the Gausst coordinate system.
|
|
229
|
+
*
|
|
230
|
+
* @param object - Three.js Object3D (mesh, camera, group, etc.)
|
|
231
|
+
* @param tiltDeg - Tilt (pitch) in degrees
|
|
232
|
+
* @param panDeg - Pan (yaw) in degrees
|
|
233
|
+
* @param rollDeg - Roll in degrees
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* import * as THREE from 'three';
|
|
237
|
+
* const mesh = new THREE.Mesh(geometry, material);
|
|
238
|
+
* applyGausstRotation(mesh, 10, 45, 0); // 10° tilt, 45° pan
|
|
239
|
+
*/
|
|
240
|
+
export function applyGausstRotation(object, tiltDeg, panDeg, rollDeg) {
|
|
241
|
+
const euler = gausstRotationToEuler(tiltDeg, panDeg, rollDeg);
|
|
242
|
+
object.rotation.order = euler.order;
|
|
243
|
+
object.rotation.set(euler.x, euler.y, euler.z);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Configure a Three.js PerspectiveCamera with Gausst parameters
|
|
247
|
+
*
|
|
248
|
+
* @param camera - Three.js PerspectiveCamera
|
|
249
|
+
* @param fovDegrees - Horizontal field of view in degrees
|
|
250
|
+
* @param position - Camera position [x, y, z] in meters
|
|
251
|
+
* @param lookAt - Look-at target [x, y, z] in meters
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* const camera = new THREE.PerspectiveCamera();
|
|
255
|
+
* configureGausstCamera(camera, 60, [0, 1.6, 0], [0, 1.6, -10]);
|
|
256
|
+
*/
|
|
257
|
+
export function configureGausstCamera(camera, fovDegrees, position, lookAt) {
|
|
258
|
+
// Note: Three.js PerspectiveCamera.fov is VERTICAL FOV
|
|
259
|
+
camera.fov = horizontalToVerticalFov(fovDegrees);
|
|
260
|
+
camera.position.set(...position);
|
|
261
|
+
camera.lookAt(...lookAt);
|
|
262
|
+
camera.updateProjectionMatrix();
|
|
263
|
+
}
|
|
264
|
+
// =============================================================================
|
|
265
|
+
// Validation Utilities
|
|
266
|
+
// =============================================================================
|
|
267
|
+
/**
|
|
268
|
+
* Validate FOV is within reasonable bounds
|
|
269
|
+
*
|
|
270
|
+
* @param fovDegrees - Field of view to validate
|
|
271
|
+
* @returns true if valid, false otherwise
|
|
272
|
+
*/
|
|
273
|
+
export function isValidFov(fovDegrees) {
|
|
274
|
+
return fovDegrees > 1 && fovDegrees < 179;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Validate focal length is within reasonable bounds
|
|
278
|
+
*
|
|
279
|
+
* @param focalLengthMM - Focal length to validate
|
|
280
|
+
* @returns true if valid, false otherwise
|
|
281
|
+
*/
|
|
282
|
+
export function isValidFocalLength(focalLengthMM) {
|
|
283
|
+
return focalLengthMM > 1 && focalLengthMM < 1000;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Clamp FOV to valid range
|
|
287
|
+
*
|
|
288
|
+
* @param fovDegrees - FOV to clamp
|
|
289
|
+
* @returns Clamped FOV
|
|
290
|
+
*/
|
|
291
|
+
export function clampFov(fovDegrees) {
|
|
292
|
+
return Math.max(1, Math.min(179, fovDegrees));
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Clamp rotation to reasonable range (-180 to 180)
|
|
296
|
+
*
|
|
297
|
+
* @param degrees - Rotation in degrees
|
|
298
|
+
* @returns Normalized rotation in -180 to 180 range
|
|
299
|
+
*/
|
|
300
|
+
export function normalizeRotation(degrees) {
|
|
301
|
+
let normalized = degrees % 360;
|
|
302
|
+
if (normalized > 180)
|
|
303
|
+
normalized -= 360;
|
|
304
|
+
if (normalized < -180)
|
|
305
|
+
normalized += 360;
|
|
306
|
+
return normalized;
|
|
307
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gausst.test.d.ts","sourceRoot":"","sources":["../src/gausst.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { GAUSST, fovToFocalLength, focalLengthToFov, gausstRotationToEuler, eulerToGausstRotation, normalizeRotation, isValidFov, clampFov, horizontalToVerticalFov, verticalToHorizontalFov } from './gausst';
|
|
3
|
+
describe('fovToFocalLength', () => {
|
|
4
|
+
it('converts 60° FOV to ~31.19mm', () => {
|
|
5
|
+
const result = fovToFocalLength(60);
|
|
6
|
+
expect(result).toBeCloseTo(31.19, 1);
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
describe('fovToFocalLength / focalLengthToFov round-trip', () => {
|
|
10
|
+
it('recovers original FOV within 0.001°', () => {
|
|
11
|
+
const originalFov = 72.5;
|
|
12
|
+
const focal = fovToFocalLength(originalFov);
|
|
13
|
+
const recovered = focalLengthToFov(focal);
|
|
14
|
+
expect(recovered).toBeCloseTo(originalFov, 3);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('gausstRotationToEuler', () => {
|
|
18
|
+
it('negates pan and roll, keeps tilt', () => {
|
|
19
|
+
const tilt = 10;
|
|
20
|
+
const pan = 20;
|
|
21
|
+
const roll = 5;
|
|
22
|
+
const result = gausstRotationToEuler(tilt, pan, roll);
|
|
23
|
+
expect(result.x).toBeCloseTo(tilt * GAUSST.DEG_TO_RAD, 10);
|
|
24
|
+
expect(result.y).toBeCloseTo(-pan * GAUSST.DEG_TO_RAD, 10);
|
|
25
|
+
expect(result.z).toBeCloseTo(-roll * GAUSST.DEG_TO_RAD, 10);
|
|
26
|
+
expect(result.order).toBe('YXZ');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('gausstRotationToEuler / eulerToGausstRotation round-trip', () => {
|
|
30
|
+
it('recovers original tilt, pan, roll', () => {
|
|
31
|
+
const tilt = 15;
|
|
32
|
+
const pan = -30;
|
|
33
|
+
const roll = 7.5;
|
|
34
|
+
const euler = gausstRotationToEuler(tilt, pan, roll);
|
|
35
|
+
const [rTilt, rPan, rRoll] = eulerToGausstRotation(euler.x, euler.y, euler.z);
|
|
36
|
+
expect(rTilt).toBeCloseTo(tilt, 10);
|
|
37
|
+
expect(rPan).toBeCloseTo(pan, 10);
|
|
38
|
+
expect(rRoll).toBeCloseTo(roll, 10);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('normalizeRotation', () => {
|
|
42
|
+
it('normalizes boundary cases', () => {
|
|
43
|
+
expect(normalizeRotation(181)).toBeCloseTo(-179, 10);
|
|
44
|
+
expect(normalizeRotation(360)).toBeCloseTo(0, 10);
|
|
45
|
+
expect(normalizeRotation(-270)).toBeCloseTo(90, 10);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('isValidFov', () => {
|
|
49
|
+
it('rejects boundaries, accepts valid range', () => {
|
|
50
|
+
expect(isValidFov(1)).toBe(false);
|
|
51
|
+
expect(isValidFov(179)).toBe(false);
|
|
52
|
+
expect(isValidFov(60)).toBe(true);
|
|
53
|
+
expect(isValidFov(0)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('clampFov', () => {
|
|
57
|
+
it('clamps to [1, 179]', () => {
|
|
58
|
+
expect(clampFov(200)).toBe(179);
|
|
59
|
+
expect(clampFov(0)).toBe(1);
|
|
60
|
+
expect(clampFov(-50)).toBe(1);
|
|
61
|
+
expect(clampFov(90)).toBe(90);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('horizontalToVerticalFov / verticalToHorizontalFov round-trip', () => {
|
|
65
|
+
it('recovers original horizontal FOV within 0.001°', () => {
|
|
66
|
+
const hFov = 65;
|
|
67
|
+
const vFov = horizontalToVerticalFov(hFov);
|
|
68
|
+
const recovered = verticalToHorizontalFov(vFov);
|
|
69
|
+
expect(recovered).toBeCloseTo(hFov, 3);
|
|
70
|
+
});
|
|
71
|
+
});
|