@webspatial/core-sdk 1.6.1 → 2.0.0
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/CHANGELOG.md +84 -0
- package/README.md +10 -0
- package/dist/JSBCommand.d.ts +398 -0
- package/dist/JSBCommand.js +668 -0
- package/dist/JSBCommand.js.map +1 -0
- package/dist/Spatial.d.ts +63 -0
- package/dist/Spatial.js +60 -0
- package/dist/Spatial.js.map +1 -0
- package/dist/SpatialObject.d.ts +19 -0
- package/dist/SpatialObject.js +44 -0
- package/dist/SpatialObject.js.map +1 -0
- package/dist/SpatialScene.d.ts +51 -0
- package/dist/SpatialScene.js +74 -0
- package/dist/SpatialScene.js.map +1 -0
- package/dist/SpatialSession.d.ts +143 -0
- package/dist/SpatialSession.js +173 -0
- package/dist/SpatialSession.js.map +1 -0
- package/dist/SpatialWebEvent.d.ts +8 -0
- package/dist/SpatialWebEvent.js +18 -0
- package/dist/SpatialWebEvent.js.map +1 -0
- package/dist/SpatialWebEventCreator.d.ts +11 -0
- package/dist/SpatialWebEventCreator.js +11 -0
- package/dist/SpatialWebEventCreator.js.map +1 -0
- package/dist/Spatialized2DElement.d.ts +42 -0
- package/dist/Spatialized2DElement.js +47 -0
- package/dist/Spatialized2DElement.js.map +1 -0
- package/dist/SpatializedDynamic3DElement.d.ts +7 -0
- package/dist/SpatializedDynamic3DElement.js +43 -0
- package/dist/SpatializedDynamic3DElement.js.map +1 -0
- package/dist/SpatializedElement.d.ts +7 -0
- package/dist/SpatializedElement.js +206 -0
- package/dist/SpatializedElement.js.map +1 -0
- package/dist/SpatializedElementCreator.d.ts +15 -0
- package/dist/SpatializedElementCreator.js +47 -0
- package/dist/SpatializedElementCreator.js.map +1 -0
- package/dist/SpatializedStatic3DElement.d.ts +195 -0
- package/dist/SpatializedStatic3DElement.js +327 -0
- package/dist/SpatializedStatic3DElement.js.map +1 -0
- package/dist/WebMsgCommand.d.ts +7 -0
- package/dist/WebMsgCommand.js +23 -0
- package/dist/WebMsgCommand.js.map +1 -0
- package/dist/index.d.ts +35 -1101
- package/dist/index.js +21 -3393
- package/dist/index.js.map +1 -1
- package/dist/install-polyfills.d.ts +2 -0
- package/dist/install-polyfills.js +13 -0
- package/dist/install-polyfills.js.map +1 -0
- package/dist/isSSREnv.d.ts +3 -0
- package/dist/isSSREnv.js +6 -0
- package/dist/isSSREnv.js.map +1 -0
- package/dist/motion/AnimationObject.d.ts +7 -0
- package/dist/motion/AnimationObject.js +140 -0
- package/dist/motion/AnimationObject.js.map +1 -0
- package/dist/motion/compute/index.d.ts +10 -0
- package/dist/motion/compute/index.js +19 -0
- package/dist/motion/compute/index.js.map +1 -0
- package/dist/motion/compute/motionPropertyValues.d.ts +19 -0
- package/dist/motion/compute/motionPropertyValues.js +50 -0
- package/dist/motion/compute/motionPropertyValues.js.map +1 -0
- package/dist/motion/compute/normalize.d.ts +31 -0
- package/dist/motion/compute/normalize.js +201 -0
- package/dist/motion/compute/normalize.js.map +1 -0
- package/dist/motion/compute/validate.d.ts +22 -0
- package/dist/motion/compute/validate.js +103 -0
- package/dist/motion/compute/validate.js.map +1 -0
- package/dist/motion/index.d.ts +8 -0
- package/dist/motion/index.js +6 -0
- package/dist/motion/index.js.map +1 -0
- package/dist/motion/native/serializeMotionTimeline.d.ts +12 -0
- package/dist/motion/native/serializeMotionTimeline.js +21 -0
- package/dist/motion/native/serializeMotionTimeline.js.map +1 -0
- package/dist/physicalMetrics-W5uZ5xKb.d.ts +48 -0
- package/dist/physicalMetrics.d.ts +1 -0
- package/dist/physicalMetrics.js +58 -0
- package/dist/physicalMetrics.js.map +1 -0
- package/dist/platform-adapter/CommandResultUtils.d.ts +12 -0
- package/dist/platform-adapter/CommandResultUtils.js +21 -0
- package/dist/platform-adapter/CommandResultUtils.js.map +1 -0
- package/dist/platform-adapter/createPlatformSync.d.ts +16 -0
- package/dist/platform-adapter/createPlatformSync.js +30 -0
- package/dist/platform-adapter/createPlatformSync.js.map +1 -0
- package/dist/platform-adapter/index.d.ts +12 -0
- package/dist/platform-adapter/index.js +10 -0
- package/dist/platform-adapter/index.js.map +1 -0
- package/dist/platform-adapter/interface.d.ts +7 -0
- package/dist/platform-adapter/interface.js +1 -0
- package/dist/platform-adapter/interface.js.map +1 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.d.ts +25 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js +168 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js.map +1 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.d.ts +66 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js +372 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js.map +1 -0
- package/dist/platform-adapter/spatialRequestMetadata.d.ts +6 -0
- package/dist/platform-adapter/spatialRequestMetadata.js +39 -0
- package/dist/platform-adapter/spatialRequestMetadata.js.map +1 -0
- package/dist/platform-adapter/spatialSceneQuery.d.ts +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js.map +1 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.d.ts +19 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js +77 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js.map +1 -0
- package/dist/platform-runtime.d.ts +12 -0
- package/dist/platform-runtime.js +14 -0
- package/dist/platform-runtime.js.map +1 -0
- package/dist/reality/Attachment.d.ts +19 -0
- package/dist/reality/Attachment.js +39 -0
- package/dist/reality/Attachment.js.map +1 -0
- package/dist/reality/component/ModelComponent.d.ts +14 -0
- package/dist/reality/component/ModelComponent.js +11 -0
- package/dist/reality/component/ModelComponent.js.map +1 -0
- package/dist/reality/component/SpatialComponent.d.ts +8 -0
- package/dist/reality/component/SpatialComponent.js +19 -0
- package/dist/reality/component/SpatialComponent.js.map +1 -0
- package/dist/reality/component/index.d.ts +8 -0
- package/dist/reality/component/index.js +3 -0
- package/dist/reality/component/index.js.map +1 -0
- package/dist/reality/entity/SpatialEntity.d.ts +7 -0
- package/dist/reality/entity/SpatialEntity.js +291 -0
- package/dist/reality/entity/SpatialEntity.js.map +1 -0
- package/dist/reality/entity/SpatialModelEntity.d.ts +17 -0
- package/dist/reality/entity/SpatialModelEntity.js +17 -0
- package/dist/reality/entity/SpatialModelEntity.js.map +1 -0
- package/dist/reality/entity/index.d.ts +8 -0
- package/dist/reality/entity/index.js +3 -0
- package/dist/reality/entity/index.js.map +1 -0
- package/dist/reality/geometry/SpatialBoxGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js +13 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialConeGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialConeGeometry.js +13 -0
- package/dist/reality/geometry/SpatialConeGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js +13 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialGeometry.d.ts +7 -0
- package/dist/reality/geometry/SpatialGeometry.js +13 -0
- package/dist/reality/geometry/SpatialGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js +13 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialSphereGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js +13 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js.map +1 -0
- package/dist/reality/geometry/index.d.ts +12 -0
- package/dist/reality/geometry/index.js +7 -0
- package/dist/reality/geometry/index.js.map +1 -0
- package/dist/reality/index.d.ts +18 -0
- package/dist/reality/index.js +7 -0
- package/dist/reality/index.js.map +1 -0
- package/dist/reality/material/SpatialMaterial.d.ts +7 -0
- package/dist/reality/material/SpatialMaterial.js +13 -0
- package/dist/reality/material/SpatialMaterial.js.map +1 -0
- package/dist/reality/material/SpatialUnlitMaterial.d.ts +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js.map +1 -0
- package/dist/reality/material/index.d.ts +8 -0
- package/dist/reality/material/index.js +3 -0
- package/dist/reality/material/index.js.map +1 -0
- package/dist/reality/realityCreator.d.ts +22 -0
- package/dist/reality/realityCreator.js +89 -0
- package/dist/reality/realityCreator.js.map +1 -0
- package/dist/reality/resource/SpatialModelAsset.d.ts +15 -0
- package/dist/reality/resource/SpatialModelAsset.js +12 -0
- package/dist/reality/resource/SpatialModelAsset.js.map +1 -0
- package/dist/reality/resource/SpatialTextureResource.d.ts +16 -0
- package/dist/reality/resource/SpatialTextureResource.js +16 -0
- package/dist/reality/resource/SpatialTextureResource.js.map +1 -0
- package/dist/reality/resource/index.d.ts +9 -0
- package/dist/reality/resource/index.js +3 -0
- package/dist/reality/resource/index.js.map +1 -0
- package/dist/runtime/WebSpatialRuntimeError.d.ts +10 -0
- package/dist/runtime/WebSpatialRuntimeError.js +14 -0
- package/dist/runtime/WebSpatialRuntimeError.js.map +1 -0
- package/dist/runtime/capability-data.d.ts +12 -0
- package/dist/runtime/capability-data.js +134 -0
- package/dist/runtime/capability-data.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +27 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/jsbAdapterPlatform.d.ts +9 -0
- package/dist/runtime/jsbAdapterPlatform.js +38 -0
- package/dist/runtime/jsbAdapterPlatform.js.map +1 -0
- package/dist/runtime/keys.d.ts +34 -0
- package/dist/runtime/keys.js +122 -0
- package/dist/runtime/keys.js.map +1 -0
- package/dist/runtime/semver.d.ts +8 -0
- package/dist/runtime/semver.js +32 -0
- package/dist/runtime/semver.js.map +1 -0
- package/dist/runtime/supports.d.ts +22 -0
- package/dist/runtime/supports.js +78 -0
- package/dist/runtime/supports.js.map +1 -0
- package/dist/runtime/types.d.ts +12 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/userAgent.d.ts +20 -0
- package/dist/runtime/userAgent.js +41 -0
- package/dist/runtime/userAgent.js.map +1 -0
- package/dist/scene-polyfill.d.ts +18 -0
- package/dist/scene-polyfill.js +480 -0
- package/dist/scene-polyfill.js.map +1 -0
- package/dist/spatial-host.d.ts +13 -0
- package/dist/spatial-host.js +18 -0
- package/dist/spatial-host.js.map +1 -0
- package/dist/spatial-window-polyfill.d.ts +3 -0
- package/dist/spatial-window-polyfill.js +155 -0
- package/dist/spatial-window-polyfill.js.map +1 -0
- package/dist/spatializedElementMotion-_0XWPQCE.d.ts +1009 -0
- package/dist/types/animation.d.ts +7 -0
- package/dist/types/animation.js +1 -0
- package/dist/types/animation.js.map +1 -0
- package/dist/types/global.d.ts +38 -0
- package/dist/types/global.js +1 -0
- package/dist/types/global.js.map +1 -0
- package/dist/types/internal.d.ts +7 -0
- package/dist/types/internal.js +12 -0
- package/dist/types/internal.js.map +1 -0
- package/dist/types/motion/index.d.ts +7 -0
- package/dist/types/motion/index.js +3 -0
- package/dist/types/motion/index.js.map +1 -0
- package/dist/types/motion/spatializedElementMotion.d.ts +7 -0
- package/dist/types/motion/spatializedElementMotion.js +1 -0
- package/dist/types/motion/spatializedElementMotion.js.map +1 -0
- package/dist/types/motion/spatializedMotion.d.ts +7 -0
- package/dist/types/motion/spatializedMotion.js +16 -0
- package/dist/types/motion/spatializedMotion.js.map +1 -0
- package/dist/types/motion/spatializedPlayback.d.ts +11 -0
- package/dist/types/motion/spatializedPlayback.js +1 -0
- package/dist/types/motion/spatializedPlayback.js.map +1 -0
- package/dist/types/motion/spatializedVisual.d.ts +35 -0
- package/dist/types/motion/spatializedVisual.js +1 -0
- package/dist/types/motion/spatializedVisual.js.map +1 -0
- package/dist/types/types.d.ts +7 -0
- package/dist/types/types.js +117 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +86 -0
- package/dist/utils.js.map +1 -0
- package/package.json +20 -4
- package/src/JSBCommand.ts +148 -91
- package/src/Spatial.ts +0 -21
- package/src/SpatialObject.ts +10 -0
- package/src/SpatialScene.ts +1 -5
- package/src/SpatialSession.ts +17 -3
- package/src/Spatialized2DElement.ts +5 -0
- package/src/SpatializedDynamic3DElement.ts +5 -1
- package/src/SpatializedElement.ts +29 -0
- package/src/SpatializedElementCreator.ts +6 -4
- package/src/SpatializedStatic3DElement.test.ts +99 -0
- package/src/SpatializedStatic3DElement.ts +141 -4
- package/src/WebMsgCommand.ts +50 -0
- package/src/coverage-boost.test.ts +258 -316
- package/src/index.ts +28 -12
- package/src/install-polyfills.ts +38 -0
- package/src/jsbcommand.coverage.test.ts +42 -48
- package/src/motion/AnimationObject.test.ts +421 -0
- package/src/motion/AnimationObject.ts +208 -0
- package/src/motion/compute/index.ts +10 -0
- package/src/motion/compute/motionPropertyValues.test.ts +55 -0
- package/src/motion/compute/motionPropertyValues.ts +70 -0
- package/src/motion/compute/normalize.test.ts +99 -0
- package/src/motion/compute/normalize.ts +289 -0
- package/src/motion/compute/validate.test.ts +229 -0
- package/src/motion/compute/validate.ts +156 -0
- package/src/motion/index.ts +2 -0
- package/src/motion/native/serializeMotionTimeline.test.ts +61 -0
- package/src/motion/native/serializeMotionTimeline.ts +27 -0
- package/src/platform-adapter/CommandResultUtils.ts +2 -2
- package/src/platform-adapter/createPlatformSync.ts +36 -0
- package/src/platform-adapter/index.ts +5 -51
- package/src/platform-adapter/interface.ts +35 -23
- package/src/platform-adapter/pico-os/PicoOSPlatform.ts +156 -59
- package/src/platform-adapter/puppeteer/PuppeteerPlatform.ts +37 -11
- package/src/platform-adapter/spatialRequestMetadata.test.ts +175 -0
- package/src/platform-adapter/spatialRequestMetadata.ts +48 -0
- package/src/platform-adapter/spatialSceneQuery.ts +17 -0
- package/src/platform-adapter/vision-os/VisionOSPlatform.ts +60 -24
- package/src/platform-runtime.ts +13 -0
- package/src/reality/Attachment.ts +2 -2
- package/src/reality/entity/SpatialEntity.ts +127 -2
- package/src/reality/realityCreator.ts +15 -1
- package/src/reality/resource/SpatialTextureResource.ts +16 -0
- package/src/reality/resource/index.ts +1 -0
- package/src/runtime/WebSpatialRuntimeError.ts +16 -0
- package/src/runtime/capability-data.ts +213 -0
- package/src/runtime/contract-review.test.ts +44 -0
- package/src/runtime/index.ts +17 -0
- package/src/runtime/jsbAdapterPlatform.test.ts +36 -0
- package/src/runtime/jsbAdapterPlatform.ts +51 -0
- package/src/runtime/keys.ts +140 -0
- package/src/runtime/semver.ts +33 -0
- package/src/runtime/supports.test.ts +346 -0
- package/src/runtime/supports.ts +112 -0
- package/src/runtime/types.ts +11 -0
- package/src/runtime/useAnimation.capability.test.ts +275 -0
- package/src/runtime/userAgent.ts +64 -0
- package/src/scene-polyfill.manifest.test.ts +312 -9
- package/src/scene-polyfill.request-metadata.test.ts +39 -0
- package/src/scene-polyfill.test.ts +33 -94
- package/src/scene-polyfill.ts +125 -92
- package/src/spatial-host.ts +25 -0
- package/src/spatial-window-polyfill.ts +18 -2
- package/src/transform-roundtrip.test.ts +90 -0
- package/src/types/animation.ts +224 -0
- package/src/types/{global.d.ts → global.ts} +10 -12
- package/src/types/motion/index.ts +10 -0
- package/src/types/motion/spatializedElementMotion.ts +49 -0
- package/src/types/motion/spatializedMotion.ts +147 -0
- package/src/types/motion/spatializedPlayback.ts +17 -0
- package/src/types/motion/spatializedVisual.ts +24 -0
- package/src/types/types.ts +12 -0
- package/src/utils.ts +54 -0
- package/tsup.config.ts +61 -36
- package/vitest.config.ts +5 -1
- package/dist/iife/index.d.ts +0 -1101
- package/dist/iife/index.global.js +0 -75
- package/dist/iife/index.global.js.map +0 -1
- package/src/platform-adapter/android/AndroidPlatform.ts +0 -133
- package/src/platform-adapter/ssr/SSRPlatform.ts +0 -43
- /package/src/{ssr-polyfill.ts → isSSREnv.ts} +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
describe('scene polyfill request metadata forwarding', () => {
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
vi.restoreAllMocks()
|
|
6
|
+
vi.resetModules()
|
|
7
|
+
delete (window as any).webSpatial
|
|
8
|
+
delete (window as any).__webspatialShell__
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it.each(['createSpatialized2DElement', 'createAttachment'])(
|
|
12
|
+
'forwards rid and wsepoch for %s without introducing extra request fields',
|
|
13
|
+
async command => {
|
|
14
|
+
;(window as any).webSpatial = {
|
|
15
|
+
genToken: vi.fn(() => 'token-123'),
|
|
16
|
+
}
|
|
17
|
+
const originalOpen = vi.fn(() => null)
|
|
18
|
+
vi.spyOn(window, 'open').mockImplementation(originalOpen)
|
|
19
|
+
|
|
20
|
+
const { hijackWindowOpen } = await import('./scene-polyfill')
|
|
21
|
+
hijackWindowOpen(window)
|
|
22
|
+
|
|
23
|
+
window.open(`webspatial://${command}?rid=req_1&wsepoch=9`, '_blank')
|
|
24
|
+
|
|
25
|
+
expect(originalOpen).toHaveBeenCalled()
|
|
26
|
+
const redirectedUrl = (originalOpen.mock.calls as unknown[][])[0]?.[0]
|
|
27
|
+
expect(typeof redirectedUrl).toBe('string')
|
|
28
|
+
const redirected = new URL(String(redirectedUrl))
|
|
29
|
+
if (redirected.protocol === 'webspatial:') {
|
|
30
|
+
expect(redirected.host).toBe(command)
|
|
31
|
+
} else {
|
|
32
|
+
expect(redirected.searchParams.get('command')).toBe(command)
|
|
33
|
+
}
|
|
34
|
+
expect(redirected.searchParams.get('rid')).toBe('req_1')
|
|
35
|
+
expect(redirected.searchParams.get('wsepoch')).toBe('9')
|
|
36
|
+
expect(redirected.searchParams.get('wsrid')).toBeNull()
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
})
|
|
@@ -265,93 +265,51 @@ describe('test formatSceneConfig in volume', () => {
|
|
|
265
265
|
|
|
266
266
|
vi.mock('./JSBCommand', () => {
|
|
267
267
|
return {
|
|
268
|
-
GetSpatialSceneState: vi.fn().mockImplementation(()
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
268
|
+
GetSpatialSceneState: vi.fn().mockImplementation(function () {
|
|
269
|
+
return {
|
|
270
|
+
execute: vi.fn().mockResolvedValue({ data: { name: 'pending' } }),
|
|
271
|
+
}
|
|
272
|
+
}),
|
|
273
|
+
UpdateSceneConfig: vi.fn().mockImplementation(function () {
|
|
274
|
+
return {
|
|
275
|
+
execute: vi.fn().mockResolvedValue(undefined),
|
|
276
|
+
}
|
|
277
|
+
}),
|
|
278
|
+
UpdateSpatialSceneProperties: vi.fn().mockImplementation(function () {
|
|
279
|
+
return {
|
|
280
|
+
execute: vi.fn().mockResolvedValue(undefined),
|
|
281
|
+
}
|
|
282
|
+
}),
|
|
283
|
+
AddSpatializedElementToSpatialScene: vi
|
|
284
|
+
.fn()
|
|
285
|
+
.mockImplementation(function () {
|
|
286
|
+
return {
|
|
287
|
+
execute: vi.fn().mockResolvedValue(undefined),
|
|
288
|
+
}
|
|
289
|
+
}),
|
|
280
290
|
}
|
|
281
291
|
})
|
|
282
292
|
|
|
283
|
-
describe('
|
|
293
|
+
describe('removed scene window globals', () => {
|
|
284
294
|
beforeEach(() => {
|
|
285
295
|
;(window as any).opener = {}
|
|
286
296
|
})
|
|
287
297
|
|
|
288
|
-
it('
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
.mockResolvedValue({ defaultSize: { width: 800, height: 600 } })
|
|
294
|
-
window.xrCurrentSceneDefaults = mockFn
|
|
295
|
-
|
|
296
|
-
injectSceneHook()
|
|
297
|
-
|
|
298
|
-
document.dispatchEvent(new Event('DOMContentLoaded'))
|
|
299
|
-
|
|
300
|
-
await vi.runAllTimersAsync()
|
|
301
|
-
|
|
302
|
-
expect(mockFn).toHaveBeenCalledWith(
|
|
303
|
-
expect.objectContaining({ defaultSize: { width: 1280, height: 720 } }),
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
// verify UpdateSceneConfig.execute
|
|
307
|
-
const { UpdateSceneConfig } = await import('./JSBCommand')
|
|
308
|
-
expect(UpdateSceneConfig).toHaveBeenCalledWith({
|
|
309
|
-
type: 'window',
|
|
310
|
-
defaultSize: { width: 800, height: 600 },
|
|
311
|
-
})
|
|
298
|
+
it('does not type removed window scene globals', () => {
|
|
299
|
+
// @ts-expect-error removed undocumented scene global
|
|
300
|
+
void window.xrCurrentSceneDefaults
|
|
301
|
+
// @ts-expect-error removed undocumented scene global
|
|
302
|
+
void window.xrCurrentSceneType
|
|
312
303
|
})
|
|
313
304
|
|
|
314
|
-
it('
|
|
305
|
+
it('does not use opened-page globals to override pending scene config', async () => {
|
|
315
306
|
vi.useFakeTimers()
|
|
316
307
|
|
|
317
308
|
const mockFn = vi
|
|
318
309
|
.fn()
|
|
319
310
|
.mockResolvedValue({ defaultSize: { width: 800, height: 600 } })
|
|
320
|
-
window.xrCurrentSceneDefaults = mockFn
|
|
321
|
-
window.xrCurrentSceneType = '
|
|
322
|
-
|
|
323
|
-
injectSceneHook()
|
|
324
|
-
|
|
325
|
-
document.dispatchEvent(new Event('DOMContentLoaded'))
|
|
326
|
-
|
|
327
|
-
await vi.runAllTimersAsync()
|
|
328
|
-
|
|
329
|
-
expect(mockFn).toHaveBeenCalledWith(
|
|
330
|
-
expect.objectContaining({ defaultSize: { width: 1280, height: 720 } }),
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
// verify UpdateSceneConfig.execute
|
|
334
|
-
const { UpdateSceneConfig } = await import('./JSBCommand')
|
|
335
|
-
expect(UpdateSceneConfig).toHaveBeenCalledWith({
|
|
336
|
-
type: 'window',
|
|
337
|
-
defaultSize: { width: 800, height: 600 },
|
|
338
|
-
})
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
it('with volume type', async () => {
|
|
342
|
-
vi.useFakeTimers()
|
|
343
|
-
|
|
344
|
-
const mockFn = vi.fn().mockResolvedValue({
|
|
345
|
-
defaultSize: { width: 1, height: 1, depth: 1 },
|
|
346
|
-
resizability: {
|
|
347
|
-
minWidth: 0.5,
|
|
348
|
-
minHeight: 1,
|
|
349
|
-
maxWidth: 0.5,
|
|
350
|
-
maxHeight: 1,
|
|
351
|
-
},
|
|
352
|
-
})
|
|
353
|
-
window.xrCurrentSceneDefaults = mockFn
|
|
354
|
-
window.xrCurrentSceneType = 'volume'
|
|
311
|
+
;(window as any).xrCurrentSceneDefaults = mockFn
|
|
312
|
+
;(window as any).xrCurrentSceneType = 'volume'
|
|
355
313
|
|
|
356
314
|
injectSceneHook()
|
|
357
315
|
|
|
@@ -359,28 +317,9 @@ describe('injectScenePolyfill should call xrCurrentSceneDefaults and update scen
|
|
|
359
317
|
|
|
360
318
|
await vi.runAllTimersAsync()
|
|
361
319
|
|
|
362
|
-
expect(mockFn).toHaveBeenCalledWith(
|
|
363
|
-
expect.objectContaining({
|
|
364
|
-
defaultSize: { width: '0.94m', height: '0.94m', depth: '0.94m' },
|
|
365
|
-
}),
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
// verify UpdateSceneConfig.execute
|
|
369
320
|
const { UpdateSceneConfig } = await import('./JSBCommand')
|
|
370
|
-
expect(
|
|
371
|
-
|
|
372
|
-
defaultSize: {
|
|
373
|
-
width: pointToPhysical(1),
|
|
374
|
-
height: pointToPhysical(1),
|
|
375
|
-
depth: pointToPhysical(1),
|
|
376
|
-
},
|
|
377
|
-
resizability: {
|
|
378
|
-
minWidth: 0.5,
|
|
379
|
-
minHeight: 1,
|
|
380
|
-
maxWidth: 0.5,
|
|
381
|
-
maxHeight: 1,
|
|
382
|
-
},
|
|
383
|
-
})
|
|
321
|
+
expect(mockFn).not.toHaveBeenCalled()
|
|
322
|
+
expect(UpdateSceneConfig).not.toHaveBeenCalled()
|
|
384
323
|
})
|
|
385
324
|
})
|
|
386
325
|
|
package/src/scene-polyfill.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { FocusScene } from './JSBCommand'
|
|
2
|
+
import { openSpatialSceneSync } from './spatial-host'
|
|
3
3
|
import {
|
|
4
4
|
SpatialSceneCreationOptions,
|
|
5
5
|
SpatialSceneType,
|
|
6
|
-
SpatialSceneState,
|
|
7
6
|
isValidSceneUnit,
|
|
8
7
|
isValidSpatialSceneType,
|
|
9
8
|
isValidWorldScalingType,
|
|
@@ -73,33 +72,93 @@ function deepMergePlain<
|
|
|
73
72
|
return out
|
|
74
73
|
}
|
|
75
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Pick a value from a manifest-like object where both snake_case and camelCase may exist.
|
|
77
|
+
*
|
|
78
|
+
* Rule: if both keys exist on the same object, snake_case wins.
|
|
79
|
+
*/
|
|
80
|
+
function preferSnake<T>(
|
|
81
|
+
src: Record<string, any> | undefined,
|
|
82
|
+
snakeKey: string,
|
|
83
|
+
camelKey: string,
|
|
84
|
+
): T | undefined {
|
|
85
|
+
if (!src) return undefined
|
|
86
|
+
if (src[snakeKey] !== undefined) return src[snakeKey] as T
|
|
87
|
+
if (src[camelKey] !== undefined) return src[camelKey] as T
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Normalize resizability keys from manifest input into the runtime camelCase shape.
|
|
93
|
+
*
|
|
94
|
+
* Accepts both:
|
|
95
|
+
* - camelCase: minWidth/minHeight/maxWidth/maxHeight
|
|
96
|
+
* - snake_case: min_width/min_height/max_width/max_height
|
|
97
|
+
*
|
|
98
|
+
* Rule: if both exist on the same object, snake_case wins.
|
|
99
|
+
*/
|
|
100
|
+
function normalizeResizability(
|
|
101
|
+
src: Record<string, any> | undefined,
|
|
102
|
+
): SpatialSceneCreationOptions['resizability'] | undefined {
|
|
103
|
+
if (!src || typeof src !== 'object' || Array.isArray(src)) return undefined
|
|
104
|
+
const out: any = {}
|
|
105
|
+
const minWidth = preferSnake<any>(src, 'min_width', 'minWidth')
|
|
106
|
+
if (minWidth !== undefined) out.minWidth = minWidth
|
|
107
|
+
const minHeight = preferSnake<any>(src, 'min_height', 'minHeight')
|
|
108
|
+
if (minHeight !== undefined) out.minHeight = minHeight
|
|
109
|
+
const maxWidth = preferSnake<any>(src, 'max_width', 'maxWidth')
|
|
110
|
+
if (maxWidth !== undefined) out.maxWidth = maxWidth
|
|
111
|
+
const maxHeight = preferSnake<any>(src, 'max_height', 'maxHeight')
|
|
112
|
+
if (maxHeight !== undefined) out.maxHeight = maxHeight
|
|
113
|
+
return Object.keys(out).length > 0 ? out : undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
76
116
|
/**
|
|
77
117
|
* Normalize XRSpatialSceneDefaults (manifest shape) into SpatialSceneCreationOptions (runtime shape).
|
|
78
|
-
*
|
|
79
|
-
* -
|
|
80
|
-
* -
|
|
118
|
+
*
|
|
119
|
+
* - Remaps supported snake_case aliases into camelCase keys.
|
|
120
|
+
* - Keeps output camelCase-only (snake_case keys are treated as input aliases).
|
|
121
|
+
* - Units are not formatted here; downstream formatting is handled by formatSceneConfig.
|
|
122
|
+
*
|
|
123
|
+
* Rule: if both snake_case and camelCase are present on the same object, snake_case wins.
|
|
81
124
|
*/
|
|
82
125
|
function normalizeXRDefaultsToSceneOptions(
|
|
83
126
|
src: XRSpatialSceneDefaults | Record<string, any>,
|
|
84
127
|
): SpatialSceneCreationOptions {
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
const input: any = src || {}
|
|
129
|
+
const out: any = {}
|
|
130
|
+
|
|
131
|
+
const ds = preferSnake<any>(input, 'default_size', 'defaultSize')
|
|
132
|
+
if (ds !== undefined) out.defaultSize = ds
|
|
133
|
+
|
|
134
|
+
const res = input.resizability
|
|
135
|
+
const normalizedRes = normalizeResizability(res)
|
|
136
|
+
if (normalizedRes) out.resizability = normalizedRes
|
|
137
|
+
|
|
138
|
+
const worldScaling = preferSnake<any>(input, 'world_scaling', 'worldScaling')
|
|
139
|
+
if (worldScaling !== undefined) out.worldScaling = worldScaling
|
|
140
|
+
|
|
141
|
+
const worldAlignment = preferSnake<any>(
|
|
142
|
+
input,
|
|
143
|
+
'world_alignment',
|
|
144
|
+
'worldAlignment',
|
|
145
|
+
)
|
|
146
|
+
if (worldAlignment !== undefined) out.worldAlignment = worldAlignment
|
|
147
|
+
|
|
148
|
+
const baseplateVisibility = preferSnake<any>(
|
|
149
|
+
input,
|
|
150
|
+
'baseplate_visibility',
|
|
151
|
+
'baseplateVisibility',
|
|
152
|
+
)
|
|
153
|
+
if (baseplateVisibility !== undefined)
|
|
154
|
+
out.baseplateVisibility = baseplateVisibility
|
|
155
|
+
|
|
96
156
|
return out
|
|
97
157
|
}
|
|
98
158
|
|
|
99
159
|
class SceneManager {
|
|
100
160
|
private originalOpen: any
|
|
101
161
|
private static instance: SceneManager
|
|
102
|
-
private manifestReady: Promise<void> | null = null
|
|
103
162
|
static getInstance() {
|
|
104
163
|
if (!SceneManager.instance) {
|
|
105
164
|
SceneManager.instance = new SceneManager()
|
|
@@ -108,7 +167,9 @@ class SceneManager {
|
|
|
108
167
|
}
|
|
109
168
|
|
|
110
169
|
init(window: WindowProxy) {
|
|
111
|
-
|
|
170
|
+
// Load manifest defaults in the background; scene creation reads the
|
|
171
|
+
// resolved xr_window_defaults / xr_volume_defaults once available.
|
|
172
|
+
void this.setupManifest()
|
|
112
173
|
this.originalOpen = window.open.bind(window)
|
|
113
174
|
;(window as any).open = this.open
|
|
114
175
|
}
|
|
@@ -124,10 +185,6 @@ class SceneManager {
|
|
|
124
185
|
return this.configMap[name]
|
|
125
186
|
}
|
|
126
187
|
|
|
127
|
-
waitManifest(): Promise<void> {
|
|
128
|
-
return this.manifestReady ?? Promise.resolve()
|
|
129
|
-
}
|
|
130
|
-
|
|
131
188
|
// Ensure URL is absolute; only convert when a relative path is provided
|
|
132
189
|
// - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
|
|
133
190
|
// - Handle protocol-relative URLs (//example.com/path)
|
|
@@ -157,12 +214,41 @@ class SceneManager {
|
|
|
157
214
|
const xr = manifest?.xr_spatial_scene
|
|
158
215
|
if (!xr || typeof xr !== 'object') return
|
|
159
216
|
const { overrides, ...topLevel } = xr as XRSpatialSceneConfig
|
|
160
|
-
// Merge top-level defaults with per-scene overrides.
|
|
161
|
-
const windowRaw = deepMergePlain(topLevel, overrides?.window_scene)
|
|
162
|
-
const volumeRaw = deepMergePlain(topLevel, overrides?.volume_scene)
|
|
163
|
-
const windowNext = normalizeXRDefaultsToSceneOptions(windowRaw)
|
|
164
217
|
|
|
165
|
-
|
|
218
|
+
// Normalize top-level defaults and per-type overrides separately, then deep-merge.
|
|
219
|
+
// - Ensures overrides keep higher priority than top-level defaults.
|
|
220
|
+
// - Ensures snake_case vs camelCase resolution is applied within the same object only.
|
|
221
|
+
// - This affects only manifest-resolved defaults; initScene callback chaining remains unchanged
|
|
222
|
+
// because callbackReturnMap stores the raw callback return values.
|
|
223
|
+
const normalizedTopLevel = normalizeXRDefaultsToSceneOptions(
|
|
224
|
+
topLevel as any,
|
|
225
|
+
)
|
|
226
|
+
const overrideObj: any = overrides as any
|
|
227
|
+
const windowOverrideSrc = preferSnake<any>(
|
|
228
|
+
overrideObj,
|
|
229
|
+
'window_scene',
|
|
230
|
+
'windowScene',
|
|
231
|
+
)
|
|
232
|
+
const volumeOverrideSrc = preferSnake<any>(
|
|
233
|
+
overrideObj,
|
|
234
|
+
'volume_scene',
|
|
235
|
+
'volumeScene',
|
|
236
|
+
)
|
|
237
|
+
const normalizedWindowOverride = windowOverrideSrc
|
|
238
|
+
? normalizeXRDefaultsToSceneOptions(windowOverrideSrc)
|
|
239
|
+
: undefined
|
|
240
|
+
const normalizedVolumeOverride = volumeOverrideSrc
|
|
241
|
+
? normalizeXRDefaultsToSceneOptions(volumeOverrideSrc)
|
|
242
|
+
: undefined
|
|
243
|
+
|
|
244
|
+
const windowNext = deepMergePlain(
|
|
245
|
+
normalizedTopLevel,
|
|
246
|
+
normalizedWindowOverride,
|
|
247
|
+
)
|
|
248
|
+
const volumeNext = deepMergePlain(
|
|
249
|
+
normalizedTopLevel,
|
|
250
|
+
normalizedVolumeOverride,
|
|
251
|
+
)
|
|
166
252
|
if (windowNext && Object.keys(windowNext).length > 0) {
|
|
167
253
|
xr_window_defaults = windowNext
|
|
168
254
|
}
|
|
@@ -193,9 +279,12 @@ class SceneManager {
|
|
|
193
279
|
const host = window.location.host
|
|
194
280
|
const protocol = window.location.protocol
|
|
195
281
|
const finalURL = `${protocol}//${host}/${token}/?command=${command}`
|
|
196
|
-
const
|
|
282
|
+
const sourceParams = new URL(url).searchParams
|
|
197
283
|
const final = new URL(finalURL)
|
|
198
|
-
|
|
284
|
+
for (const key of ['rid', 'wsepoch']) {
|
|
285
|
+
const value = sourceParams.get(key)
|
|
286
|
+
if (value) final.searchParams.set(key, value)
|
|
287
|
+
}
|
|
199
288
|
return this.originalOpen(final.toString(), target, features)
|
|
200
289
|
}
|
|
201
290
|
}
|
|
@@ -221,8 +310,12 @@ class SceneManager {
|
|
|
221
310
|
cfg = { ...ans, type: 'window' }
|
|
222
311
|
}
|
|
223
312
|
|
|
224
|
-
const
|
|
225
|
-
|
|
313
|
+
const result = openSpatialSceneSync(
|
|
314
|
+
url!,
|
|
315
|
+
cfg as SpatialSceneCreationOptionsInternal,
|
|
316
|
+
target,
|
|
317
|
+
features,
|
|
318
|
+
)
|
|
226
319
|
|
|
227
320
|
const id = result.data?.id
|
|
228
321
|
|
|
@@ -429,8 +522,6 @@ export function formatSceneConfig(
|
|
|
429
522
|
// defaultSize should format into px if window
|
|
430
523
|
// defaultSize should format into m if volume
|
|
431
524
|
|
|
432
|
-
const defaultSceneConfig = getSceneDefaultConfig(sceneType)
|
|
433
|
-
|
|
434
525
|
const errors: string[] = []
|
|
435
526
|
|
|
436
527
|
const isWindow = sceneType === 'window'
|
|
@@ -567,65 +658,7 @@ function getSceneDefaultConfig(sceneType: SpatialSceneType) {
|
|
|
567
658
|
: xr_volume_defaults || defaultSceneConfigVolume
|
|
568
659
|
}
|
|
569
660
|
|
|
570
|
-
async function injectScenePolyfill() {
|
|
571
|
-
if (!window.opener) return
|
|
572
|
-
|
|
573
|
-
const state = await SpatialScene.getInstance().getState()
|
|
574
|
-
|
|
575
|
-
// only run this in pending state
|
|
576
|
-
if (state !== SpatialSceneState.pending) return
|
|
577
|
-
|
|
578
|
-
function onContentLoaded(callback: any) {
|
|
579
|
-
if (
|
|
580
|
-
document.readyState === 'interactive' ||
|
|
581
|
-
document.readyState === 'complete'
|
|
582
|
-
) {
|
|
583
|
-
callback()
|
|
584
|
-
} else {
|
|
585
|
-
document.addEventListener('DOMContentLoaded', callback)
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
onContentLoaded(async () => {
|
|
590
|
-
await SceneManager.getInstance().waitManifest()
|
|
591
|
-
const sceneType = window.xrCurrentSceneType ?? 'window'
|
|
592
|
-
const rawDefault = getSceneDefaultConfig(sceneType)
|
|
593
|
-
// Provide a formatted 'pre' to the callback for consistent units and types.
|
|
594
|
-
const pre = deepCloneJSON(rawDefault)
|
|
595
|
-
|
|
596
|
-
let cfg = pre
|
|
597
|
-
if (typeof window.xrCurrentSceneDefaults === 'function') {
|
|
598
|
-
try {
|
|
599
|
-
cfg = await window.xrCurrentSceneDefaults?.(pre)
|
|
600
|
-
} catch (error) {
|
|
601
|
-
console.error(error)
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
// fixme: this duration is too short so that hide and show is at racing, so add a little delay to avoid
|
|
605
|
-
await new Promise((resolve, reject) => {
|
|
606
|
-
setTimeout(() => {
|
|
607
|
-
resolve(null)
|
|
608
|
-
}, 1000)
|
|
609
|
-
})
|
|
610
|
-
|
|
611
|
-
// Merge callback return with base defaults to ensure missing fields are filled.
|
|
612
|
-
const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg)
|
|
613
|
-
const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType)
|
|
614
|
-
if (errors.length > 0) {
|
|
615
|
-
console.warn(
|
|
616
|
-
`window.xrCurrentSceneDefaults with errors: ${errors.join(', ')}`,
|
|
617
|
-
)
|
|
618
|
-
}
|
|
619
|
-
const finalCfg = {
|
|
620
|
-
...formattedConfig,
|
|
621
|
-
type: sceneType,
|
|
622
|
-
}
|
|
623
|
-
await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg)
|
|
624
|
-
})
|
|
625
|
-
}
|
|
626
|
-
|
|
627
661
|
export function injectSceneHook() {
|
|
628
662
|
hijackWindowOpen(window)
|
|
629
663
|
hijackWindowATag(window)
|
|
630
|
-
injectScenePolyfill()
|
|
631
664
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WebSpatialProtocolResult } from './platform-adapter/interface'
|
|
2
|
+
import { getPlatform, getPlatformSync } from './platform-runtime'
|
|
3
|
+
import type { SpatialSceneCreationOptionsInternal } from './types/internal'
|
|
4
|
+
import type { AttachmentEntityOptions } from './types/types'
|
|
5
|
+
|
|
6
|
+
export async function createNativeSpatialDiv(): Promise<WebSpatialProtocolResult> {
|
|
7
|
+
const platform = await getPlatform()
|
|
8
|
+
return platform.createNativeSpatialDiv()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function createNativeAttachment(
|
|
12
|
+
options: AttachmentEntityOptions,
|
|
13
|
+
): Promise<WebSpatialProtocolResult> {
|
|
14
|
+
const platform = await getPlatform()
|
|
15
|
+
return platform.createNativeAttachment(options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function openSpatialSceneSync(
|
|
19
|
+
url: string,
|
|
20
|
+
config: SpatialSceneCreationOptionsInternal | undefined,
|
|
21
|
+
target?: string,
|
|
22
|
+
features?: string,
|
|
23
|
+
): WebSpatialProtocolResult {
|
|
24
|
+
return getPlatformSync().openSpatialSceneSync(url, config, target, features)
|
|
25
|
+
}
|
|
@@ -78,10 +78,20 @@ function hijackDocumentElementStyle() {
|
|
|
78
78
|
const rawDocumentStyle = document.documentElement.style
|
|
79
79
|
const styleProxy = new Proxy(rawDocumentStyle, {
|
|
80
80
|
set: function (target, key, value) {
|
|
81
|
-
|
|
81
|
+
let ret: boolean
|
|
82
82
|
|
|
83
83
|
if (key === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
84
|
-
|
|
84
|
+
target.setProperty(
|
|
85
|
+
SpatialGlobalCustomVars.backgroundMaterial,
|
|
86
|
+
String(value),
|
|
87
|
+
)
|
|
88
|
+
ret = true
|
|
89
|
+
} else {
|
|
90
|
+
ret = Reflect.set(target, key, value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (key === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
94
|
+
setCurrentWindowStyle(String(value))
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
if (
|
|
@@ -106,6 +116,12 @@ function hijackDocumentElementStyle() {
|
|
|
106
116
|
return ret
|
|
107
117
|
},
|
|
108
118
|
get: function (target, prop: string) {
|
|
119
|
+
if (prop === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
120
|
+
return target.getPropertyValue(
|
|
121
|
+
SpatialGlobalCustomVars.backgroundMaterial,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
109
125
|
if (typeof target[prop as keyof CSSStyleDeclaration] === 'function') {
|
|
110
126
|
return function (this: any, ...args: any[]) {
|
|
111
127
|
if (prop === 'setProperty') {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { composeSRT, decomposeSRT } from './utils'
|
|
3
|
+
|
|
4
|
+
function approxEqual(a: number, b: number, eps = 1e-6) {
|
|
5
|
+
return Math.abs(a - b) < eps
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe('composeSRT / decomposeSRT round-trip', () => {
|
|
9
|
+
test('identity transform round-trips', () => {
|
|
10
|
+
const pos = { x: 0, y: 0, z: 0 }
|
|
11
|
+
const rot = { x: 0, y: 0, z: 0 }
|
|
12
|
+
const scl = { x: 1, y: 1, z: 1 }
|
|
13
|
+
|
|
14
|
+
const matrix = composeSRT(pos, rot, scl)
|
|
15
|
+
const arr = Array.from(matrix.toFloat64Array())
|
|
16
|
+
const result = decomposeSRT(arr)
|
|
17
|
+
|
|
18
|
+
expect(approxEqual(result.position!.x, 0)).toBe(true)
|
|
19
|
+
expect(approxEqual(result.position!.y, 0)).toBe(true)
|
|
20
|
+
expect(approxEqual(result.position!.z, 0)).toBe(true)
|
|
21
|
+
expect(approxEqual(result.scale!.x, 1)).toBe(true)
|
|
22
|
+
expect(approxEqual(result.scale!.y, 1)).toBe(true)
|
|
23
|
+
expect(approxEqual(result.scale!.z, 1)).toBe(true)
|
|
24
|
+
expect(approxEqual(result.rotation!.x, 0)).toBe(true)
|
|
25
|
+
expect(approxEqual(result.rotation!.y, 0)).toBe(true)
|
|
26
|
+
expect(approxEqual(result.rotation!.z, 0)).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('translation-only round-trips', () => {
|
|
30
|
+
const pos = { x: 3, y: -5, z: 10 }
|
|
31
|
+
const rot = { x: 0, y: 0, z: 0 }
|
|
32
|
+
const scl = { x: 1, y: 1, z: 1 }
|
|
33
|
+
|
|
34
|
+
const matrix = composeSRT(pos, rot, scl)
|
|
35
|
+
const arr = Array.from(matrix.toFloat64Array())
|
|
36
|
+
const result = decomposeSRT(arr)
|
|
37
|
+
|
|
38
|
+
expect(approxEqual(result.position!.x, 3)).toBe(true)
|
|
39
|
+
expect(approxEqual(result.position!.y, -5)).toBe(true)
|
|
40
|
+
expect(approxEqual(result.position!.z, 10)).toBe(true)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('scale-only round-trips', () => {
|
|
44
|
+
const pos = { x: 0, y: 0, z: 0 }
|
|
45
|
+
const rot = { x: 0, y: 0, z: 0 }
|
|
46
|
+
const scl = { x: 2, y: 0.5, z: 3 }
|
|
47
|
+
|
|
48
|
+
const matrix = composeSRT(pos, rot, scl)
|
|
49
|
+
const arr = Array.from(matrix.toFloat64Array())
|
|
50
|
+
const result = decomposeSRT(arr)
|
|
51
|
+
|
|
52
|
+
expect(approxEqual(result.scale!.x, 2)).toBe(true)
|
|
53
|
+
expect(approxEqual(result.scale!.y, 0.5)).toBe(true)
|
|
54
|
+
expect(approxEqual(result.scale!.z, 3)).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('rotation-only round-trips (45 deg Y)', () => {
|
|
58
|
+
const pos = { x: 0, y: 0, z: 0 }
|
|
59
|
+
const rot = { x: 0, y: 45, z: 0 }
|
|
60
|
+
const scl = { x: 1, y: 1, z: 1 }
|
|
61
|
+
|
|
62
|
+
const matrix = composeSRT(pos, rot, scl)
|
|
63
|
+
const arr = Array.from(matrix.toFloat64Array())
|
|
64
|
+
const result = decomposeSRT(arr)
|
|
65
|
+
|
|
66
|
+
expect(approxEqual(result.rotation!.x, 0)).toBe(true)
|
|
67
|
+
expect(approxEqual(result.rotation!.y, 45)).toBe(true)
|
|
68
|
+
expect(approxEqual(result.rotation!.z, 0)).toBe(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('combined SRT round-trips', () => {
|
|
72
|
+
const pos = { x: 1, y: 2, z: 3 }
|
|
73
|
+
const rot = { x: 30, y: 45, z: 60 }
|
|
74
|
+
const scl = { x: 2, y: 3, z: 0.5 }
|
|
75
|
+
|
|
76
|
+
const matrix = composeSRT(pos, rot, scl)
|
|
77
|
+
const arr = Array.from(matrix.toFloat64Array())
|
|
78
|
+
const result = decomposeSRT(arr)
|
|
79
|
+
|
|
80
|
+
expect(approxEqual(result.position!.x, 1)).toBe(true)
|
|
81
|
+
expect(approxEqual(result.position!.y, 2)).toBe(true)
|
|
82
|
+
expect(approxEqual(result.position!.z, 3)).toBe(true)
|
|
83
|
+
expect(approxEqual(result.scale!.x, 2, 1e-4)).toBe(true)
|
|
84
|
+
expect(approxEqual(result.scale!.y, 3, 1e-4)).toBe(true)
|
|
85
|
+
expect(approxEqual(result.scale!.z, 0.5, 1e-4)).toBe(true)
|
|
86
|
+
expect(approxEqual(result.rotation!.x, 30, 0.1)).toBe(true)
|
|
87
|
+
expect(approxEqual(result.rotation!.y, 45, 0.1)).toBe(true)
|
|
88
|
+
expect(approxEqual(result.rotation!.z, 60, 0.1)).toBe(true)
|
|
89
|
+
})
|
|
90
|
+
})
|