@webspatial/core-sdk 1.2.1 → 1.4.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 +69 -1
- package/dist/iife/index.d.ts +115 -32
- package/dist/iife/index.global.js +68 -3
- package/dist/iife/index.global.js.map +1 -1
- package/dist/index.d.ts +115 -32
- package/dist/index.js +603 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/JSBCommand.ts +65 -0
- package/src/Spatial.ts +22 -1
- package/src/SpatialScene.ts +20 -0
- package/src/SpatialSession.ts +15 -1
- package/src/SpatializedDynamic3DElement.ts +19 -0
- package/src/SpatializedElement.ts +0 -29
- package/src/SpatializedElementCreator.ts +1 -1
- package/src/SpatializedStatic3DElement.test.ts +125 -0
- package/src/SpatializedStatic3DElement.ts +14 -1
- package/src/WebMsgCommand.ts +0 -26
- package/src/index.ts +2 -0
- package/src/jsbcommand.coverage.test.ts +0 -43
- package/src/physicalMetrics.test.ts +110 -0
- package/src/physicalMetrics.ts +101 -0
- package/src/platform-adapter/index.ts +5 -1
- package/src/platform-adapter/puppeteer/PuppeteerPlatform.ts +470 -0
- package/src/reality/Attachment.ts +47 -0
- package/src/reality/entity/SpatialEntity.ts +45 -24
- package/src/reality/index.ts +1 -0
- package/src/types/global.d.ts +7 -5
- package/src/types/types.ts +29 -2
package/package.json
CHANGED
package/src/JSBCommand.ts
CHANGED
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
SpatialModelEntityCreationOptions,
|
|
22
22
|
SpatialEntityEventType,
|
|
23
23
|
Vec3,
|
|
24
|
+
AttachmentEntityOptions,
|
|
25
|
+
AttachmentEntityUpdateOptions,
|
|
24
26
|
} from './types/types'
|
|
25
27
|
import { SpatialSceneCreationOptionsInternal } from './types/internal'
|
|
26
28
|
import { composeSRT } from './utils'
|
|
@@ -500,6 +502,24 @@ export class ConvertFromSceneToEntityCommand extends JSBCommand {
|
|
|
500
502
|
commandType = 'ConvertFromSceneToEntity'
|
|
501
503
|
}
|
|
502
504
|
|
|
505
|
+
export class ConvertCoordinateCommand extends JSBCommand {
|
|
506
|
+
constructor(
|
|
507
|
+
public position: Vec3,
|
|
508
|
+
public fromId: string,
|
|
509
|
+
public toId: string,
|
|
510
|
+
) {
|
|
511
|
+
super()
|
|
512
|
+
}
|
|
513
|
+
protected getParams(): Record<string, any> | undefined {
|
|
514
|
+
return {
|
|
515
|
+
position: this.position,
|
|
516
|
+
fromId: this.fromId,
|
|
517
|
+
toId: this.toId,
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
commandType = 'ConvertCoordinate'
|
|
521
|
+
}
|
|
522
|
+
|
|
503
523
|
export class CreateTextureResourceCommand extends JSBCommand {
|
|
504
524
|
constructor(private url: string) {
|
|
505
525
|
super()
|
|
@@ -620,6 +640,51 @@ export class createSpatialSceneCommand extends WebSpatialProtocolCommand {
|
|
|
620
640
|
}
|
|
621
641
|
}
|
|
622
642
|
|
|
643
|
+
export class CreateAttachmentEntityCommand extends WebSpatialProtocolCommand {
|
|
644
|
+
commandType = 'createAttachment'
|
|
645
|
+
constructor(private options: AttachmentEntityOptions) {
|
|
646
|
+
super()
|
|
647
|
+
}
|
|
648
|
+
protected getParams() {
|
|
649
|
+
return {} // No metadata — just trigger engine/webview creation
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
export class InitializeAttachmentCommand extends JSBCommand {
|
|
654
|
+
commandType = 'InitializeAttachment'
|
|
655
|
+
constructor(
|
|
656
|
+
private attachmentId: string,
|
|
657
|
+
private options: AttachmentEntityOptions,
|
|
658
|
+
) {
|
|
659
|
+
super()
|
|
660
|
+
}
|
|
661
|
+
protected getParams() {
|
|
662
|
+
return {
|
|
663
|
+
id: this.attachmentId,
|
|
664
|
+
parentEntityId: this.options.parentEntityId,
|
|
665
|
+
position: this.options.position ?? [0, 0, 0],
|
|
666
|
+
size: this.options.size,
|
|
667
|
+
ownerViewId: this.options.ownerViewId,
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export class UpdateAttachmentEntityCommand extends JSBCommand {
|
|
673
|
+
commandType = 'UpdateAttachmentEntity'
|
|
674
|
+
constructor(
|
|
675
|
+
private attachmentId: string,
|
|
676
|
+
private options: AttachmentEntityUpdateOptions,
|
|
677
|
+
) {
|
|
678
|
+
super()
|
|
679
|
+
}
|
|
680
|
+
protected getParams() {
|
|
681
|
+
return {
|
|
682
|
+
id: this.attachmentId,
|
|
683
|
+
...this.options,
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
623
688
|
// TODO: Can crypto.randomUUID be used instead including in dev environments without https
|
|
624
689
|
function uuid(): string {
|
|
625
690
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
package/src/Spatial.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { SpatialWebEvent } from './SpatialWebEvent'
|
|
|
6
6
|
* This is the main entry point for the WebSpatial SDK, providing access to spatial capabilities.
|
|
7
7
|
*/
|
|
8
8
|
export class Spatial {
|
|
9
|
+
private wsAppShellVersionFromUA: string | null | undefined
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Requests a spatial session object from the browser.
|
|
11
13
|
* This is the primary method to initialize spatial functionality.
|
|
@@ -33,6 +35,25 @@ export class Spatial {
|
|
|
33
35
|
return false
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
getShellVersionFromUA(): string | null {
|
|
39
|
+
if (this.wsAppShellVersionFromUA !== undefined) {
|
|
40
|
+
return this.wsAppShellVersionFromUA
|
|
41
|
+
}
|
|
42
|
+
if (
|
|
43
|
+
typeof navigator === 'undefined' ||
|
|
44
|
+
typeof navigator.userAgent !== 'string'
|
|
45
|
+
) {
|
|
46
|
+
this.wsAppShellVersionFromUA = null
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const match = navigator.userAgent.match(
|
|
51
|
+
/WSAppShell\/(\d+(?:\.\d+){2}(?:[-+][0-9A-Za-z.-]+)*)/,
|
|
52
|
+
)
|
|
53
|
+
this.wsAppShellVersionFromUA = match ? match[1] : '1.3.0'
|
|
54
|
+
return this.wsAppShellVersionFromUA
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
/** @deprecated
|
|
37
58
|
* Checks if WebSpatial is supported in the current environment.
|
|
38
59
|
* Verifies compatibility between native and client versions.
|
|
@@ -51,7 +72,7 @@ export class Spatial {
|
|
|
51
72
|
if (window.__WebSpatialData && window.__WebSpatialData.getNativeVersion) {
|
|
52
73
|
return window.__WebSpatialData.getNativeVersion()
|
|
53
74
|
}
|
|
54
|
-
return window.WebSpatailNativeVersion === '
|
|
75
|
+
return window.WebSpatailNativeVersion === 'WS_SHELL_VERSION'
|
|
55
76
|
? this.getClientVersion()
|
|
56
77
|
: window.WebSpatailNativeVersion
|
|
57
78
|
}
|
package/src/SpatialScene.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SpatialSceneCreationOptions,
|
|
3
3
|
SpatialSceneProperties,
|
|
4
|
+
Vec3,
|
|
4
5
|
} from './types/types'
|
|
5
6
|
import { SpatialSceneCreationOptionsInternal } from './types/internal'
|
|
6
7
|
import {
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
UpdateSceneConfig,
|
|
10
11
|
UpdateSpatialSceneProperties,
|
|
11
12
|
} from './JSBCommand'
|
|
13
|
+
import { ConvertCoordinateCommand } from './JSBCommand'
|
|
12
14
|
|
|
13
15
|
import { SpatializedElement } from './SpatializedElement'
|
|
14
16
|
import { SpatialObject } from './SpatialObject'
|
|
@@ -34,6 +36,24 @@ export class SpatialScene extends SpatialObject {
|
|
|
34
36
|
return instance
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
async convertCoordinate(
|
|
40
|
+
position: Vec3,
|
|
41
|
+
fromId: string,
|
|
42
|
+
toId: string,
|
|
43
|
+
): Promise<Vec3> {
|
|
44
|
+
try {
|
|
45
|
+
const ret = await new ConvertCoordinateCommand(
|
|
46
|
+
position,
|
|
47
|
+
fromId,
|
|
48
|
+
toId,
|
|
49
|
+
).execute()
|
|
50
|
+
return (ret as any)?.data ?? position
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn('SpatialScene.convertCoordinate error:', error)
|
|
53
|
+
throw error
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
/**
|
|
38
58
|
* Updates the properties of the spatial scene.
|
|
39
59
|
* This can include background settings, lighting, and other scene-wide properties.
|
package/src/SpatialSession.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
createSpatializedDynamic3DElement,
|
|
7
7
|
} from './SpatializedElementCreator'
|
|
8
8
|
import { createSpatializedStatic3DElement } from './SpatializedElementCreator'
|
|
9
|
+
import { Attachment, createAttachmentEntity } from './reality/Attachment'
|
|
9
10
|
import { SpatializedStatic3DElement } from './SpatializedStatic3DElement'
|
|
10
11
|
import {
|
|
11
12
|
ModelComponentOptions,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
SpatialSphereGeometryOptions,
|
|
21
22
|
SpatialUnlitMaterialOptions,
|
|
22
23
|
SpatialEntityUserData,
|
|
24
|
+
AttachmentEntityOptions,
|
|
23
25
|
} from './types/types'
|
|
24
26
|
import { SpatializedDynamic3DElement } from './SpatializedDynamic3DElement'
|
|
25
27
|
import { SpatialEntity } from './reality/entity/SpatialEntity'
|
|
@@ -70,7 +72,7 @@ export class SpatialSession {
|
|
|
70
72
|
* @returns Promise resolving to a new SpatializedStatic3DElement instance
|
|
71
73
|
*/
|
|
72
74
|
createSpatializedStatic3DElement(
|
|
73
|
-
modelURL: string
|
|
75
|
+
modelURL: string,
|
|
74
76
|
): Promise<SpatializedStatic3DElement> {
|
|
75
77
|
return createSpatializedStatic3DElement(modelURL)
|
|
76
78
|
}
|
|
@@ -187,4 +189,16 @@ export class SpatialSession {
|
|
|
187
189
|
) {
|
|
188
190
|
return createSpatialModelEntity(options, userData)
|
|
189
191
|
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Creates an attachment entity that renders 2D HTML content as a child
|
|
195
|
+
* of a 3D entity in the scene graph.
|
|
196
|
+
* @param options Configuration options including parent entity ID, position, and size
|
|
197
|
+
* @returns Promise resolving to a new Attachment instance
|
|
198
|
+
*/
|
|
199
|
+
createAttachmentEntity(
|
|
200
|
+
options: AttachmentEntityOptions,
|
|
201
|
+
): Promise<Attachment> {
|
|
202
|
+
return createAttachmentEntity(options)
|
|
203
|
+
}
|
|
190
204
|
}
|
|
@@ -6,11 +6,15 @@ import {
|
|
|
6
6
|
import { SpatialEntity } from './reality'
|
|
7
7
|
import { SpatializedElement } from './SpatializedElement'
|
|
8
8
|
import {
|
|
9
|
+
SpatialEntityEventType,
|
|
9
10
|
SpatialEntityOrReality,
|
|
10
11
|
SpatializedElementProperties,
|
|
11
12
|
} from './types/types'
|
|
13
|
+
|
|
12
14
|
export class SpatializedDynamic3DElement extends SpatializedElement {
|
|
13
15
|
children: SpatialEntityOrReality[] = []
|
|
16
|
+
events: Record<string, (data: any) => void> = {}
|
|
17
|
+
|
|
14
18
|
constructor(id: string) {
|
|
15
19
|
super(id)
|
|
16
20
|
}
|
|
@@ -21,6 +25,21 @@ export class SpatializedDynamic3DElement extends SpatializedElement {
|
|
|
21
25
|
entity.parent = this
|
|
22
26
|
return ans
|
|
23
27
|
}
|
|
28
|
+
|
|
29
|
+
addEvent(type: SpatialEntityEventType, callback: (data: any) => void) {
|
|
30
|
+
this.events[type] = callback
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
removeEvent(eventName: SpatialEntityEventType) {
|
|
34
|
+
if (this.events[eventName]) {
|
|
35
|
+
delete this.events[eventName]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dispatchEvent(evt: CustomEvent) {
|
|
40
|
+
this.events[evt.type]?.(evt)
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
async updateProperties(properties: Partial<SpatializedElementProperties>) {
|
|
25
44
|
return new UpdateSpatializedDynamic3DElementProperties(
|
|
26
45
|
this,
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
SpatialTapEvent,
|
|
17
17
|
} from './types/types'
|
|
18
18
|
import {
|
|
19
|
-
CubeInfoMsg,
|
|
20
19
|
ObjectDestroyMsg,
|
|
21
20
|
SpatialDragEndMsg,
|
|
22
21
|
SpatialDragMsg,
|
|
@@ -27,7 +26,6 @@ import {
|
|
|
27
26
|
SpatialRotateMsg,
|
|
28
27
|
SpatialTapMsg,
|
|
29
28
|
SpatialWebMsgType,
|
|
30
|
-
TransformMsg,
|
|
31
29
|
} from './WebMsgCommand'
|
|
32
30
|
|
|
33
31
|
/**
|
|
@@ -115,8 +113,6 @@ export abstract class SpatializedElement extends SpatialObject {
|
|
|
115
113
|
*/
|
|
116
114
|
protected onReceiveEvent(
|
|
117
115
|
data:
|
|
118
|
-
| CubeInfoMsg
|
|
119
|
-
| TransformMsg
|
|
120
116
|
| SpatialTapMsg
|
|
121
117
|
| SpatialDragStartMsg
|
|
122
118
|
| SpatialDragMsg
|
|
@@ -128,31 +124,6 @@ export abstract class SpatializedElement extends SpatialObject {
|
|
|
128
124
|
const { type } = data
|
|
129
125
|
if (type === SpatialWebMsgType.objectdestroy) {
|
|
130
126
|
this.isDestroyed = true
|
|
131
|
-
} else if (type === SpatialWebMsgType.cubeInfo) {
|
|
132
|
-
// Handle cube info updates (bounding box information)
|
|
133
|
-
const cubeInfoMsg = data as CubeInfoMsg
|
|
134
|
-
this._cubeInfo = new CubeInfo(cubeInfoMsg.size, cubeInfoMsg.origin)
|
|
135
|
-
} else if (type === SpatialWebMsgType.transform) {
|
|
136
|
-
// Handle transformation matrix updates
|
|
137
|
-
this._transform = new DOMMatrix([
|
|
138
|
-
data.detail.column0[0],
|
|
139
|
-
data.detail.column0[1],
|
|
140
|
-
data.detail.column0[2],
|
|
141
|
-
0,
|
|
142
|
-
data.detail.column1[0],
|
|
143
|
-
data.detail.column1[1],
|
|
144
|
-
data.detail.column1[2],
|
|
145
|
-
0,
|
|
146
|
-
data.detail.column2[0],
|
|
147
|
-
data.detail.column2[1],
|
|
148
|
-
data.detail.column2[2],
|
|
149
|
-
0,
|
|
150
|
-
data.detail.column3[0],
|
|
151
|
-
data.detail.column3[1],
|
|
152
|
-
data.detail.column3[2],
|
|
153
|
-
1,
|
|
154
|
-
])
|
|
155
|
-
this._transformInv = this._transform.inverse()
|
|
156
127
|
} else if (type === SpatialWebMsgType.spatialtap) {
|
|
157
128
|
// Handle tap gestures
|
|
158
129
|
const event = createSpatialEvent(
|
|
@@ -30,7 +30,7 @@ export async function createSpatializedStatic3DElement(
|
|
|
30
30
|
throw new Error('createSpatializedStatic3DElement failed')
|
|
31
31
|
} else {
|
|
32
32
|
const { id } = result.data
|
|
33
|
-
return new SpatializedStatic3DElement(id)
|
|
33
|
+
return new SpatializedStatic3DElement(id, modelURL)
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { SpatialWebMsgType } from './WebMsgCommand'
|
|
3
|
+
import { SpatializedStatic3DElement } from './SpatializedStatic3DElement'
|
|
4
|
+
|
|
5
|
+
// Single mock for the native bridge layer — everything else runs as-is
|
|
6
|
+
vi.mock('./JSBCommand', () => {
|
|
7
|
+
class OkCommand {
|
|
8
|
+
execute() {
|
|
9
|
+
return Promise.resolve({
|
|
10
|
+
success: true,
|
|
11
|
+
data: undefined,
|
|
12
|
+
errorCode: '',
|
|
13
|
+
errorMessage: '',
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return { UpdateSpatializedStatic3DElementProperties: OkCommand }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('SpatializedStatic3DElement', () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.clearAllMocks()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('ready starts as a pending promise', () => {
|
|
27
|
+
const el = new SpatializedStatic3DElement('s1', 'model.glb')
|
|
28
|
+
expect(el.ready).toBeInstanceOf(Promise)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('ready resolves to true on modelloaded event', async () => {
|
|
32
|
+
const el = new SpatializedStatic3DElement('s2', 'model.glb')
|
|
33
|
+
const p = el.ready
|
|
34
|
+
|
|
35
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloaded })
|
|
36
|
+
await expect(p).resolves.toBe(true)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('ready resolves to false on modelloadfailed event', async () => {
|
|
40
|
+
const el = new SpatializedStatic3DElement('s3', 'model.glb')
|
|
41
|
+
const p = el.ready
|
|
42
|
+
|
|
43
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloadfailed })
|
|
44
|
+
await expect(p).resolves.toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('fires onLoadCallback on modelloaded', () => {
|
|
48
|
+
const el = new SpatializedStatic3DElement('s4', 'model.glb')
|
|
49
|
+
const cb = vi.fn()
|
|
50
|
+
el.onLoadCallback = cb
|
|
51
|
+
|
|
52
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloaded })
|
|
53
|
+
expect(cb).toHaveBeenCalledTimes(1)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('fires onLoadFailureCallback on modelloadfailed', () => {
|
|
57
|
+
const el = new SpatializedStatic3DElement('s5', 'model.glb')
|
|
58
|
+
const cb = vi.fn()
|
|
59
|
+
el.onLoadFailureCallback = cb
|
|
60
|
+
|
|
61
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloadfailed })
|
|
62
|
+
expect(cb).toHaveBeenCalledTimes(1)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('does not fire callbacks when they are not set', () => {
|
|
66
|
+
const el = new SpatializedStatic3DElement('s6', 'model.glb')
|
|
67
|
+
// Should not throw when no callbacks are registered
|
|
68
|
+
expect(() =>
|
|
69
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloaded }),
|
|
70
|
+
).not.toThrow()
|
|
71
|
+
expect(() =>
|
|
72
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloadfailed }),
|
|
73
|
+
).not.toThrow()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('resets ready when modelURL changes', async () => {
|
|
77
|
+
const el = new SpatializedStatic3DElement('s7', 'a.glb')
|
|
78
|
+
const first = el.ready
|
|
79
|
+
|
|
80
|
+
await el.updateProperties({ modelURL: 'b.glb' })
|
|
81
|
+
expect(el.ready).not.toBe(first)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('does not reset ready when modelURL stays the same', async () => {
|
|
85
|
+
const el = new SpatializedStatic3DElement('s8', 'a.glb')
|
|
86
|
+
const first = el.ready
|
|
87
|
+
|
|
88
|
+
await el.updateProperties({ modelURL: 'a.glb' })
|
|
89
|
+
expect(el.ready).toBe(first)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('cancels old ready promise with false when modelURL changes', async () => {
|
|
93
|
+
const el = new SpatializedStatic3DElement('s9', 'a.glb')
|
|
94
|
+
const first = el.ready
|
|
95
|
+
|
|
96
|
+
await el.updateProperties({ modelURL: 'b.glb' })
|
|
97
|
+
await expect(first).resolves.toBe(false)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('new ready promise works after URL change', async () => {
|
|
101
|
+
const el = new SpatializedStatic3DElement('s10', 'a.glb')
|
|
102
|
+
|
|
103
|
+
await el.updateProperties({ modelURL: 'b.glb' })
|
|
104
|
+
const second = el.ready
|
|
105
|
+
|
|
106
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloaded })
|
|
107
|
+
await expect(second).resolves.toBe(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('handles multiple URL changes in sequence', async () => {
|
|
111
|
+
const el = new SpatializedStatic3DElement('s11', 'a.glb')
|
|
112
|
+
const p1 = el.ready
|
|
113
|
+
|
|
114
|
+
await el.updateProperties({ modelURL: 'b.glb' })
|
|
115
|
+
await expect(p1).resolves.toBe(false)
|
|
116
|
+
|
|
117
|
+
const p2 = el.ready
|
|
118
|
+
await el.updateProperties({ modelURL: 'c.glb' })
|
|
119
|
+
await expect(p2).resolves.toBe(false)
|
|
120
|
+
|
|
121
|
+
const p3 = el.ready
|
|
122
|
+
el.onReceiveEvent({ type: SpatialWebMsgType.modelloaded })
|
|
123
|
+
await expect(p3).resolves.toBe(true)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
@@ -9,6 +9,17 @@ import { SpatialWebMsgType } from './WebMsgCommand'
|
|
|
9
9
|
* and provides events for load success and failure.
|
|
10
10
|
*/
|
|
11
11
|
export class SpatializedStatic3DElement extends SpatializedElement {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new spatialized static 3D element with the specified ID and URL.
|
|
14
|
+
* Registers the element to receive spatial events.
|
|
15
|
+
* @param id Unique identifier for this element
|
|
16
|
+
* @param modelURL URL of the 3D model
|
|
17
|
+
*/
|
|
18
|
+
constructor(id: string, modelURL: string) {
|
|
19
|
+
super(id)
|
|
20
|
+
this.modelURL = modelURL
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
/**
|
|
13
24
|
* Promise resolver for the ready state.
|
|
14
25
|
* Used to resolve the ready promise when the model is loaded.
|
|
@@ -19,13 +30,15 @@ export class SpatializedStatic3DElement extends SpatializedElement {
|
|
|
19
30
|
* Caches the last model URL to detect changes.
|
|
20
31
|
* Used to reset the ready promise when the model URL changes.
|
|
21
32
|
*/
|
|
22
|
-
private modelURL: string
|
|
33
|
+
private modelURL: string
|
|
23
34
|
|
|
24
35
|
/**
|
|
25
36
|
* Creates a new promise for tracking the ready state of the model.
|
|
26
37
|
* @returns Promise that resolves when the model is loaded (true) or fails to load (false)
|
|
27
38
|
*/
|
|
28
39
|
private createReadyPromise() {
|
|
40
|
+
// If there's an existing promise reject it before it's replaced
|
|
41
|
+
this._readyResolve?.(false)
|
|
29
42
|
return new Promise<boolean>(resolve => {
|
|
30
43
|
this._readyResolve = resolve
|
|
31
44
|
})
|
package/src/WebMsgCommand.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Vec3,
|
|
3
|
-
Size3D,
|
|
4
2
|
SpatialDragEventDetail,
|
|
5
3
|
SpatialTapEventDetail,
|
|
6
4
|
SpatialRotateEventDetail,
|
|
@@ -10,8 +8,6 @@ import {
|
|
|
10
8
|
} from './types/types'
|
|
11
9
|
|
|
12
10
|
export enum SpatialWebMsgType {
|
|
13
|
-
cubeInfo = 'cubeInfo',
|
|
14
|
-
transform = 'transform',
|
|
15
11
|
modelloaded = 'modelloaded',
|
|
16
12
|
modelloadfailed = 'modelloadfailed',
|
|
17
13
|
spatialtap = 'spatialtap',
|
|
@@ -30,28 +26,6 @@ export interface ObjectDestroyMsg {
|
|
|
30
26
|
type: SpatialWebMsgType.objectdestroy
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
export interface CubeInfoMsg {
|
|
34
|
-
type: SpatialWebMsgType.cubeInfo
|
|
35
|
-
origin: Vec3
|
|
36
|
-
size: Size3D
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface CubeInfoMsg {
|
|
40
|
-
type: SpatialWebMsgType.cubeInfo
|
|
41
|
-
origin: Vec3
|
|
42
|
-
size: Size3D
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface TransformMsg {
|
|
46
|
-
type: SpatialWebMsgType.transform
|
|
47
|
-
detail: {
|
|
48
|
-
column0: [number, number, number]
|
|
49
|
-
column1: [number, number, number]
|
|
50
|
-
column2: [number, number, number]
|
|
51
|
-
column3: [number, number, number]
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
29
|
export interface SpatialTapMsg {
|
|
56
30
|
type: SpatialWebMsgType.spatialtap
|
|
57
31
|
detail: SpatialTapEventDetail
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { SpatializedElement } from './SpatializedElement'
|
|
|
6
6
|
export { Spatialized2DElement } from './Spatialized2DElement'
|
|
7
7
|
export { SpatializedStatic3DElement } from './SpatializedStatic3DElement'
|
|
8
8
|
export { SpatializedDynamic3DElement } from './SpatializedDynamic3DElement'
|
|
9
|
+
export * as PhysicalMetrics from './physicalMetrics'
|
|
10
|
+
|
|
9
11
|
export * from './reality'
|
|
10
12
|
export * from './types/types'
|
|
11
13
|
export * from './types/global.d'
|
|
@@ -434,49 +434,6 @@ describe('SpatializedElement', () => {
|
|
|
434
434
|
})
|
|
435
435
|
})
|
|
436
436
|
|
|
437
|
-
it('handles transform and cubeInfo events and updates internal state', async () => {
|
|
438
|
-
const { SpatialWebEvent } = await import('./SpatialWebEvent')
|
|
439
|
-
const { SpatialWebMsgType } = await import('./WebMsgCommand')
|
|
440
|
-
const { SpatializedElement } = await import('./SpatializedElement')
|
|
441
|
-
|
|
442
|
-
SpatialWebEvent.init()
|
|
443
|
-
|
|
444
|
-
class TestElement extends SpatializedElement {
|
|
445
|
-
updateProperties = vi.fn().mockResolvedValue({
|
|
446
|
-
success: true,
|
|
447
|
-
data: undefined,
|
|
448
|
-
errorCode: '',
|
|
449
|
-
errorMessage: '',
|
|
450
|
-
})
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const e = new TestElement('el2')
|
|
454
|
-
window.__SpatialWebEvent({
|
|
455
|
-
id: 'el2',
|
|
456
|
-
data: {
|
|
457
|
-
type: SpatialWebMsgType.cubeInfo,
|
|
458
|
-
size: { width: 1, height: 2, depth: 3 },
|
|
459
|
-
origin: { x: 4, y: 5, z: 6 },
|
|
460
|
-
},
|
|
461
|
-
})
|
|
462
|
-
expect(e.cubeInfo?.front).toBe(9)
|
|
463
|
-
|
|
464
|
-
window.__SpatialWebEvent({
|
|
465
|
-
id: 'el2',
|
|
466
|
-
data: {
|
|
467
|
-
type: SpatialWebMsgType.transform,
|
|
468
|
-
detail: {
|
|
469
|
-
column0: [1, 0, 0],
|
|
470
|
-
column1: [0, 1, 0],
|
|
471
|
-
column2: [0, 0, 1],
|
|
472
|
-
column3: [10, 20, 30],
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
})
|
|
476
|
-
expect(e.transform).toBeDefined()
|
|
477
|
-
expect(e.transformInv).toBeDefined()
|
|
478
|
-
})
|
|
479
|
-
|
|
480
437
|
it('updates flags via gesture handler setters and updates transform via JSB', async () => {
|
|
481
438
|
const { SpatialWebEvent } = await import('./SpatialWebEvent')
|
|
482
439
|
const { SpatializedElement } = await import('./SpatializedElement')
|