@shopware-ag/dive 1.18.5 → 1.19.1-beta.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/README.md +8 -0
- package/build/dive.cjs +220 -215
- package/build/dive.cjs.map +1 -1
- package/build/dive.js +233 -21102
- package/build/dive.js.map +1 -1
- package/build/dive.mjs +26111 -0
- package/build/dive.mjs.map +1 -0
- package/build/src/ar/AR.d.ts +37 -14
- package/build/src/ar/arquicklook/ARQuickLook.d.ts +5 -6
- package/build/src/ar/sceneviewer/SceneViewer.d.ts +42 -6
- package/build/src/com/actions/index.d.ts +2 -0
- package/build/src/com/actions/renderer/startrender.d.ts +5 -0
- package/build/src/com/actions/scene/launchar.d.ts +5 -2
- package/build/src/converter/Converter.d.ts +21 -0
- package/build/src/dive.d.ts +3 -2
- package/build/src/exporter/Exporter.d.ts +11 -0
- package/build/src/helper/applyMixins/applyMixins.d.ts +22 -6
- package/build/src/info/Info.d.ts +37 -13
- package/build/src/interface/Movable.d.ts +5 -5
- package/build/src/interface/Selectable.d.ts +4 -4
- package/build/src/loader/Loader.d.ts +11 -0
- package/build/src/model/Model.d.ts +2 -2
- package/build/src/node/Node.d.ts +3 -3
- package/build/src/scene/root/Root.d.ts +1 -1
- package/build/src/types/ExporterOptions.d.ts +15 -0
- package/build/src/types/FileTypes.d.ts +27 -0
- package/build/src/types/index.d.ts +4 -0
- package/build/src/types/info/index.d.ts +66 -0
- package/package.json +16 -1
- package/src/ar/AR.ts +72 -69
- package/src/ar/__test__/AR.test.ts +194 -105
- package/src/ar/arquicklook/ARQuickLook.ts +32 -72
- package/src/ar/arquicklook/__test__/ARQuickLook.test.ts +89 -38
- package/src/ar/sceneviewer/SceneViewer.ts +96 -51
- package/src/ar/sceneviewer/__test__/SceneViewer.test.ts +144 -47
- package/src/ar/webxr/WebXR.ts +5 -4
- package/src/com/Communication.ts +10 -7
- package/src/com/__test__/Communication.test.ts +16 -3
- package/src/com/actions/index.ts +2 -0
- package/src/com/actions/renderer/startrender.ts +5 -0
- package/src/com/actions/scene/launchar.ts +2 -2
- package/src/converter/Converter.ts +117 -0
- package/src/dive.ts +10 -6
- package/src/exporter/Exporter.ts +75 -0
- package/src/helper/applyMixins/applyMixins.ts +59 -7
- package/src/info/Info.ts +99 -75
- package/src/info/__test__/Info.test.ts +162 -154
- package/src/interface/Movable.ts +5 -5
- package/src/interface/Selectable.ts +4 -4
- package/src/loader/Loader.ts +48 -0
- package/src/model/Model.ts +10 -5
- package/src/model/__test__/Model.test.ts +4 -11
- package/src/node/Node.ts +7 -5
- package/src/scene/root/Root.ts +4 -4
- package/src/scene/root/__test__/Root.test.ts +4 -4
- package/src/toolbox/Toolbox.ts +1 -3
- package/src/types/ExporterOptions.ts +14 -0
- package/src/types/FileTypes.ts +37 -0
- package/src/types/index.ts +26 -0
- package/src/types/info/index.ts +76 -0
- package/build/src/exporters/usdz/USDZExporter.d.ts +0 -15
- package/build/src/loadingmanager/LoadingManager.d.ts +0 -14
- package/src/exporters/usdz/USDZExporter.ts +0 -21
- package/src/exporters/usdz/__test__/USDZExporter.test.ts +0 -57
- package/src/loadingmanager/LoadingManager.ts +0 -50
- package/src/loadingmanager/__test__/LoadingManager.test.ts +0 -27
package/src/ar/AR.ts
CHANGED
|
@@ -1,49 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export type
|
|
1
|
+
import { SystemInfo } from '../info/Info';
|
|
2
|
+
import { ESystem } from '../types/info';
|
|
3
|
+
import { ARQuickLook } from './arquicklook/ARQuickLook';
|
|
4
|
+
import { SceneViewer } from './sceneviewer/SceneViewer';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for configuring the AR system behavior
|
|
8
|
+
*/
|
|
9
|
+
export type ARSystemOptions = {
|
|
10
|
+
/** The placement orientation for AR content - either horizontal or vertical */
|
|
10
11
|
arPlacement: 'horizontal' | 'vertical';
|
|
12
|
+
/** The scaling behavior for AR content - either automatic or fixed */
|
|
11
13
|
arScale: 'auto' | 'fixed';
|
|
12
|
-
/**
|
|
13
|
-
* experimental, currently deactivated
|
|
14
|
-
*/
|
|
15
|
-
useWebXR: false;
|
|
16
14
|
};
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
public async Launch(options?: DIVEAROptions): Promise<void> {
|
|
34
|
-
const system = DIVEInfo.GetSystem();
|
|
16
|
+
/**
|
|
17
|
+
* Main class for handling AR functionality across different platforms
|
|
18
|
+
* Provides methods to launch AR experiences using platform-specific implementations
|
|
19
|
+
*/
|
|
20
|
+
export class ARSystem {
|
|
21
|
+
/**
|
|
22
|
+
* Launches an AR experience using the appropriate platform-specific implementation
|
|
23
|
+
*
|
|
24
|
+
* @param uri - The URI of the 3D model to display in AR
|
|
25
|
+
* @param options - Optional configuration for the AR experience
|
|
26
|
+
* @returns Promise that resolves when AR is launched successfully
|
|
27
|
+
* @throws Error if AR is not supported on the current platform
|
|
28
|
+
*/
|
|
29
|
+
public async launch(uri: string, options?: ARSystemOptions): Promise<void> {
|
|
30
|
+
const system = SystemInfo.GetSystem();
|
|
35
31
|
|
|
36
|
-
if (system ===
|
|
37
|
-
return this.tryARQuickLook();
|
|
32
|
+
if (system === ESystem.IOS) {
|
|
33
|
+
return this.tryARQuickLook(uri, options);
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
if (system ===
|
|
41
|
-
|
|
42
|
-
console.warn('DIVE: WebXR is experimental on Android.');
|
|
43
|
-
return this.tryWebXR();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return this.trySceneViewer();
|
|
36
|
+
if (system === ESystem.ANDROID) {
|
|
37
|
+
return this.trySceneViewer(uri, options);
|
|
47
38
|
}
|
|
48
39
|
|
|
49
40
|
console.log(
|
|
@@ -51,48 +42,60 @@ export class DIVEAR {
|
|
|
51
42
|
system +
|
|
52
43
|
')',
|
|
53
44
|
);
|
|
45
|
+
return Promise.reject(
|
|
46
|
+
new Error('AR not supported on non-mobile systems'),
|
|
47
|
+
);
|
|
54
48
|
}
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Attempts to launch AR using ARQuickLook (iOS-specific implementation)
|
|
52
|
+
*
|
|
53
|
+
* @param uri - The URI of the 3D model to display in AR
|
|
54
|
+
* @param options - Optional configuration for the AR experience
|
|
55
|
+
* @returns Promise that resolves when ARQuickLook is launched successfully
|
|
56
|
+
* @throws Error if ARQuickLook is not supported on the device
|
|
57
|
+
*/
|
|
58
|
+
private async tryARQuickLook(
|
|
59
|
+
uri: string,
|
|
60
|
+
options?: ARSystemOptions,
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
const support = SystemInfo.GetSupportsARQuickLook();
|
|
58
63
|
if (!support) {
|
|
59
64
|
console.log('ARQuickLook not supported');
|
|
60
|
-
return Promise.reject();
|
|
65
|
+
return Promise.reject(new Error('ARQuickLook not supported'));
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
console.log('DIVE: Launching AR with ARQuickLook ...');
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
private async tryWebXR(): Promise<void> {
|
|
71
|
-
const support = await DIVEInfo.GetSupportsWebXR();
|
|
72
|
-
if (!support) {
|
|
73
|
-
console.log(
|
|
74
|
-
'WebXR not supported. Reason: ' +
|
|
75
|
-
WebXRUnsupportedReason[
|
|
76
|
-
DIVEInfo.GetWebXRUnsupportedReason()!
|
|
77
|
-
],
|
|
78
|
-
);
|
|
79
|
-
return Promise.reject();
|
|
70
|
+
try {
|
|
71
|
+
return new ARQuickLook().launch(uri, options);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Error launching ARQuickLook:', error);
|
|
74
|
+
return Promise.reject(error);
|
|
80
75
|
}
|
|
81
|
-
|
|
82
|
-
console.log('DIVE: Launching AR with WebXR ...');
|
|
83
|
-
// Launch WebXR
|
|
84
|
-
await DIVEWebXR.Launch(this._renderer, this._scene, this._controller);
|
|
85
|
-
return Promise.resolve();
|
|
86
76
|
}
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Launches AR using SceneViewer (Android-specific implementation)
|
|
80
|
+
* Note: SceneViewer is supported on all Android devices. If ARCore is not installed,
|
|
81
|
+
* the model will be displayed in 3D view mode instead of AR mode.
|
|
82
|
+
*
|
|
83
|
+
* @param uri - The URI of the 3D model to display in AR
|
|
84
|
+
* @param options - Optional configuration for the AR experience
|
|
85
|
+
* @returns Promise that resolves when SceneViewer is launched successfully
|
|
86
|
+
* @throws Error if there's an issue launching SceneViewer
|
|
87
|
+
*/
|
|
88
|
+
private async trySceneViewer(
|
|
89
|
+
uri: string,
|
|
90
|
+
options?: ARSystemOptions,
|
|
91
|
+
): Promise<void> {
|
|
93
92
|
console.log('DIVE: Launching AR with SceneViewer ...');
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
try {
|
|
95
|
+
return new SceneViewer().launch(uri, options);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Error launching SceneViewer:', error);
|
|
98
|
+
return Promise.reject(error);
|
|
99
|
+
}
|
|
97
100
|
}
|
|
98
101
|
}
|
|
@@ -1,187 +1,276 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
jest.
|
|
11
|
-
DIVEARQuickLook: {
|
|
12
|
-
Launch: jest.fn(),
|
|
1
|
+
import { ARSystem, type ARSystemOptions } from '../AR';
|
|
2
|
+
import { SystemInfo } from '../../info/Info';
|
|
3
|
+
import { ARQuickLook } from '../arquicklook/ARQuickLook';
|
|
4
|
+
import { SceneViewer } from '../sceneviewer/SceneViewer';
|
|
5
|
+
|
|
6
|
+
// Mock Info
|
|
7
|
+
jest.mock('../../info/Info', () => ({
|
|
8
|
+
SystemInfo: {
|
|
9
|
+
GetSystem: jest.fn(),
|
|
10
|
+
GetSupportsARQuickLook: jest.fn(),
|
|
13
11
|
},
|
|
14
12
|
}));
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
// Mock ARQuickLook
|
|
15
|
+
const mockLaunchARQuickLook = jest.fn().mockResolvedValue(undefined);
|
|
16
|
+
jest.mock('../arquicklook/ARQuickLook', () => ({
|
|
17
|
+
ARQuickLook: jest.fn().mockImplementation(() => ({
|
|
18
|
+
launch: mockLaunchARQuickLook,
|
|
19
|
+
})),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
+
// Mock SceneViewer
|
|
23
|
+
const mockSceneViewerLaunch = jest.fn().mockResolvedValue(undefined);
|
|
22
24
|
jest.mock('../sceneviewer/SceneViewer', () => ({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
25
|
+
SceneViewer: jest.fn().mockImplementation(() => ({
|
|
26
|
+
launch: mockSceneViewerLaunch,
|
|
27
|
+
})),
|
|
26
28
|
}));
|
|
27
29
|
|
|
28
|
-
describe('
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
let controller: DIVEOrbitControls;
|
|
32
|
-
let diveAR: DIVEAR;
|
|
30
|
+
describe('ARSystem', () => {
|
|
31
|
+
let diveAR: ARSystem;
|
|
32
|
+
const mockUri = 'https://example.com/model.glb';
|
|
33
33
|
|
|
34
34
|
beforeEach(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
controller = {} as DIVEOrbitControls;
|
|
38
|
-
diveAR = new DIVEAR(renderer, scene, controller);
|
|
35
|
+
diveAR = new ARSystem();
|
|
36
|
+
jest.clearAllMocks();
|
|
39
37
|
});
|
|
40
38
|
|
|
41
|
-
describe('
|
|
39
|
+
describe('launch', () => {
|
|
42
40
|
describe('AR Quick Look', () => {
|
|
43
41
|
it('should launch ARQuickLook on iOS', async () => {
|
|
44
|
-
jest.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
48
|
-
const arQuickLookLaunchSpy = jest.spyOn(
|
|
49
|
-
DIVEARQuickLook,
|
|
50
|
-
'Launch',
|
|
51
|
-
);
|
|
42
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('iOS');
|
|
43
|
+
(
|
|
44
|
+
SystemInfo.GetSupportsARQuickLook as jest.Mock
|
|
45
|
+
).mockReturnValue(true);
|
|
52
46
|
|
|
53
47
|
const consoleLogSpy = jest
|
|
54
48
|
.spyOn(console, 'log')
|
|
55
49
|
.mockImplementation();
|
|
56
50
|
|
|
57
|
-
await diveAR.
|
|
51
|
+
await diveAR.launch(mockUri);
|
|
58
52
|
|
|
59
|
-
expect(
|
|
60
|
-
|
|
53
|
+
expect(mockLaunchARQuickLook).toHaveBeenCalledWith(
|
|
54
|
+
mockUri,
|
|
61
55
|
undefined,
|
|
62
56
|
);
|
|
63
|
-
|
|
57
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
58
|
+
'DIVE: Launching AR with ARQuickLook ...',
|
|
59
|
+
);
|
|
60
|
+
consoleLogSpy.mockRestore();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should launch ARQuickLook on iOS with options', async () => {
|
|
64
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('iOS');
|
|
65
|
+
(
|
|
66
|
+
SystemInfo.GetSupportsARQuickLook as jest.Mock
|
|
67
|
+
).mockReturnValue(true);
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
const options: ARSystemOptions = {
|
|
70
|
+
arPlacement: 'vertical',
|
|
71
|
+
arScale: 'fixed',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const consoleLogSpy = jest
|
|
75
|
+
.spyOn(console, 'log')
|
|
76
|
+
.mockImplementation();
|
|
77
|
+
|
|
78
|
+
await diveAR.launch(mockUri, options);
|
|
79
|
+
|
|
80
|
+
expect(mockLaunchARQuickLook).toHaveBeenCalledWith(
|
|
81
|
+
mockUri,
|
|
82
|
+
options,
|
|
83
|
+
);
|
|
84
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
85
|
+
'DIVE: Launching AR with ARQuickLook ...',
|
|
86
|
+
);
|
|
66
87
|
consoleLogSpy.mockRestore();
|
|
67
88
|
});
|
|
68
89
|
|
|
69
90
|
it('should not launch ARQuickLook on iOS if not supported', async () => {
|
|
70
|
-
jest.
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('iOS');
|
|
92
|
+
(
|
|
93
|
+
SystemInfo.GetSupportsARQuickLook as jest.Mock
|
|
94
|
+
).mockReturnValue(false);
|
|
95
|
+
|
|
96
|
+
const consoleLogSpy = jest
|
|
97
|
+
.spyOn(console, 'log')
|
|
98
|
+
.mockImplementation();
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
await diveAR.launch(mockUri);
|
|
102
|
+
fail('Expected launch to reject');
|
|
103
|
+
} catch (error: unknown) {
|
|
104
|
+
if (error instanceof Error) {
|
|
105
|
+
expect(error.message).toBe('ARQuickLook not supported');
|
|
106
|
+
} else {
|
|
107
|
+
fail('Expected error to be an Error instance');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
111
|
+
'ARQuickLook not supported',
|
|
73
112
|
);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
consoleLogSpy.mockRestore();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle ARQuickLook launch errors', async () => {
|
|
117
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('iOS');
|
|
118
|
+
(
|
|
119
|
+
SystemInfo.GetSupportsARQuickLook as jest.Mock
|
|
120
|
+
).mockReturnValue(true);
|
|
121
|
+
|
|
122
|
+
const mockError = new Error('Launch failed');
|
|
123
|
+
const mockInstance = {
|
|
124
|
+
launch: jest.fn().mockImplementation(() => {
|
|
125
|
+
throw mockError;
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
(ARQuickLook as jest.Mock).mockImplementation(
|
|
129
|
+
() => mockInstance,
|
|
77
130
|
);
|
|
78
131
|
|
|
79
132
|
const consoleLogSpy = jest
|
|
80
133
|
.spyOn(console, 'log')
|
|
81
134
|
.mockImplementation();
|
|
135
|
+
const consoleErrorSpy = jest
|
|
136
|
+
.spyOn(console, 'error')
|
|
137
|
+
.mockImplementation();
|
|
82
138
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
139
|
+
try {
|
|
140
|
+
await diveAR.launch(mockUri);
|
|
141
|
+
fail('Expected launch to reject');
|
|
142
|
+
} catch (error: unknown) {
|
|
143
|
+
if (error instanceof Error) {
|
|
144
|
+
expect(error.message).toBe('Launch failed');
|
|
145
|
+
} else {
|
|
146
|
+
fail('Expected error to be an Error instance');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
151
|
+
'DIVE: Launching AR with ARQuickLook ...',
|
|
152
|
+
);
|
|
153
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
154
|
+
'Error launching ARQuickLook:',
|
|
155
|
+
mockError,
|
|
156
|
+
);
|
|
87
157
|
|
|
88
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
89
158
|
consoleLogSpy.mockRestore();
|
|
159
|
+
consoleErrorSpy.mockRestore();
|
|
90
160
|
});
|
|
91
161
|
});
|
|
92
162
|
|
|
93
163
|
describe('Scene Viewer', () => {
|
|
94
164
|
it('should launch SceneViewer on Android', async () => {
|
|
95
|
-
jest.
|
|
165
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('Android');
|
|
96
166
|
|
|
97
167
|
const consoleLogSpy = jest
|
|
98
168
|
.spyOn(console, 'log')
|
|
99
169
|
.mockImplementation();
|
|
100
170
|
|
|
101
|
-
await diveAR.
|
|
171
|
+
await diveAR.launch(mockUri);
|
|
102
172
|
|
|
103
|
-
expect(
|
|
104
|
-
|
|
173
|
+
expect(mockSceneViewerLaunch).toHaveBeenCalledWith(
|
|
174
|
+
mockUri,
|
|
105
175
|
undefined,
|
|
106
176
|
);
|
|
107
|
-
|
|
108
|
-
|
|
177
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
178
|
+
'DIVE: Launching AR with SceneViewer ...',
|
|
179
|
+
);
|
|
109
180
|
consoleLogSpy.mockRestore();
|
|
110
181
|
});
|
|
111
|
-
});
|
|
112
182
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
183
|
+
it('should launch SceneViewer on Android with options', async () => {
|
|
184
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('Android');
|
|
185
|
+
|
|
186
|
+
const options: ARSystemOptions = {
|
|
187
|
+
arPlacement: 'vertical',
|
|
188
|
+
arScale: 'fixed',
|
|
189
|
+
};
|
|
119
190
|
|
|
120
191
|
const consoleLogSpy = jest
|
|
121
192
|
.spyOn(console, 'log')
|
|
122
193
|
.mockImplementation();
|
|
123
|
-
const consoleWarnSpy = jest
|
|
124
|
-
.spyOn(console, 'warn')
|
|
125
|
-
.mockImplementation();
|
|
126
194
|
|
|
127
|
-
await diveAR.
|
|
128
|
-
useWebXR: true,
|
|
129
|
-
} as unknown as DIVEAROptions);
|
|
195
|
+
await diveAR.launch(mockUri, options);
|
|
130
196
|
|
|
131
|
-
expect(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
197
|
+
expect(mockSceneViewerLaunch).toHaveBeenCalledWith(
|
|
198
|
+
mockUri,
|
|
199
|
+
options,
|
|
200
|
+
);
|
|
201
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
202
|
+
'DIVE: Launching AR with SceneViewer ...',
|
|
135
203
|
);
|
|
136
|
-
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
137
|
-
consoleWarnSpy.mockRestore();
|
|
138
|
-
|
|
139
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
140
204
|
consoleLogSpy.mockRestore();
|
|
141
205
|
});
|
|
142
206
|
|
|
143
|
-
it('should
|
|
144
|
-
jest.
|
|
145
|
-
|
|
146
|
-
|
|
207
|
+
it('should handle SceneViewer launch errors', async () => {
|
|
208
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('Android');
|
|
209
|
+
|
|
210
|
+
const mockError = new Error('Launch failed');
|
|
211
|
+
const mockInstance = {
|
|
212
|
+
launch: jest.fn().mockImplementation(() => {
|
|
213
|
+
throw mockError;
|
|
214
|
+
}),
|
|
215
|
+
};
|
|
216
|
+
(SceneViewer as jest.Mock).mockImplementation(
|
|
217
|
+
() => mockInstance,
|
|
147
218
|
);
|
|
148
219
|
|
|
149
220
|
const consoleLogSpy = jest
|
|
150
221
|
.spyOn(console, 'log')
|
|
151
222
|
.mockImplementation();
|
|
152
|
-
const
|
|
153
|
-
.spyOn(console, '
|
|
223
|
+
const consoleErrorSpy = jest
|
|
224
|
+
.spyOn(console, 'error')
|
|
154
225
|
.mockImplementation();
|
|
155
226
|
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
227
|
+
try {
|
|
228
|
+
await diveAR.launch(mockUri);
|
|
229
|
+
fail('Expected launch to reject');
|
|
230
|
+
} catch (error: unknown) {
|
|
231
|
+
if (error instanceof Error) {
|
|
232
|
+
expect(error.message).toBe('Launch failed');
|
|
233
|
+
} else {
|
|
234
|
+
fail('Expected error to be an Error instance');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
239
|
+
'DIVE: Launching AR with SceneViewer ...',
|
|
240
|
+
);
|
|
241
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
242
|
+
'Error launching SceneViewer:',
|
|
243
|
+
mockError,
|
|
166
244
|
);
|
|
167
|
-
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
168
|
-
consoleWarnSpy.mockRestore();
|
|
169
245
|
|
|
170
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
171
246
|
consoleLogSpy.mockRestore();
|
|
247
|
+
consoleErrorSpy.mockRestore();
|
|
172
248
|
});
|
|
173
249
|
});
|
|
174
250
|
|
|
175
|
-
it('should
|
|
251
|
+
it('should reject on non-mobile systems', async () => {
|
|
252
|
+
(SystemInfo.GetSystem as jest.Mock).mockReturnValue('Windows');
|
|
253
|
+
|
|
176
254
|
const consoleLogSpy = jest
|
|
177
255
|
.spyOn(console, 'log')
|
|
178
256
|
.mockImplementation();
|
|
179
257
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
258
|
+
try {
|
|
259
|
+
await diveAR.launch(mockUri);
|
|
260
|
+
fail('Expected launch to reject');
|
|
261
|
+
} catch (error: unknown) {
|
|
262
|
+
if (error instanceof Error) {
|
|
263
|
+
expect(error.message).toBe(
|
|
264
|
+
'AR not supported on non-mobile systems',
|
|
265
|
+
);
|
|
266
|
+
} else {
|
|
267
|
+
fail('Expected error to be an Error instance');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
271
|
+
'DIVE: AR not supported. Not a mobile system. (System is Windows)',
|
|
272
|
+
);
|
|
273
|
+
consoleLogSpy.mockRestore();
|
|
185
274
|
});
|
|
186
275
|
});
|
|
187
276
|
});
|
|
@@ -1,20 +1,39 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { type ARSystemOptions } from '../AR';
|
|
2
|
+
import { Converter } from '../../converter/Converter';
|
|
3
|
+
import { type USDZExporterOptions } from '../../types';
|
|
4
|
+
|
|
5
|
+
export class ARQuickLook {
|
|
6
|
+
public async launch(uri: string, options?: ARSystemOptions): Promise<void> {
|
|
7
|
+
const usdzUrl = await this.convertToUSDZ(uri, options);
|
|
8
|
+
return this.launchARQuickLook(usdzUrl, options);
|
|
9
|
+
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
private async convertToUSDZ(
|
|
12
|
+
uri: string,
|
|
13
|
+
options?: ARSystemOptions,
|
|
14
|
+
): Promise<string> {
|
|
15
|
+
// Convert the file to USDZ format
|
|
16
|
+
const usdzBuffer = await Converter.convert(uri).to('usdz', {
|
|
17
|
+
quickLookCompatible: true,
|
|
18
|
+
ar: {
|
|
19
|
+
anchoring: { type: 'plane' },
|
|
20
|
+
planeAnchoring: {
|
|
21
|
+
alignment:
|
|
22
|
+
options?.arPlacement === 'vertical'
|
|
23
|
+
? 'vertical'
|
|
24
|
+
: 'horizontal',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
} as USDZExporterOptions);
|
|
28
|
+
|
|
29
|
+
// Create a blob from the USDZ buffer
|
|
30
|
+
const blob = new Blob([usdzBuffer], { type: 'model/vnd.usdz+zip' });
|
|
31
|
+
return URL.createObjectURL(blob);
|
|
13
32
|
}
|
|
14
33
|
|
|
15
|
-
private
|
|
34
|
+
private launchARQuickLook(
|
|
16
35
|
uri: string,
|
|
17
|
-
options?:
|
|
36
|
+
options?: ARSystemOptions,
|
|
18
37
|
): Promise<void> {
|
|
19
38
|
return new Promise((resolve) => {
|
|
20
39
|
if (options?.arScale === 'fixed') {
|
|
@@ -31,63 +50,4 @@ export class DIVEARQuickLook {
|
|
|
31
50
|
a.click();
|
|
32
51
|
});
|
|
33
52
|
}
|
|
34
|
-
|
|
35
|
-
private static findARQuickLookSrc(scene: DIVEScene): string {
|
|
36
|
-
let uri: string | null = null;
|
|
37
|
-
|
|
38
|
-
scene.traverse((object) => {
|
|
39
|
-
if (uri) return;
|
|
40
|
-
if (object.userData.uri) {
|
|
41
|
-
uri = object.userData.uri;
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (!uri) {
|
|
46
|
-
throw new Error('No model found in scene');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return uri;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// private static extractModels(scene: DIVEScene): Object3D[] {
|
|
53
|
-
// // extract models
|
|
54
|
-
// return scene.Root.children;
|
|
55
|
-
// }
|
|
56
|
-
|
|
57
|
-
// private static launchARFromNode(
|
|
58
|
-
// node: Object3D,
|
|
59
|
-
// options?: DIVEAROptions,
|
|
60
|
-
// ): Promise<void> {
|
|
61
|
-
// // bundle USDZ
|
|
62
|
-
// return this._usdzExporter
|
|
63
|
-
// .parse(node, {
|
|
64
|
-
// quickLookCompatible: true,
|
|
65
|
-
// ar: {
|
|
66
|
-
// anchoring: { type: 'plane' },
|
|
67
|
-
// planeAnchoring: {
|
|
68
|
-
// alignment:
|
|
69
|
-
// options?.arPlacement === 'vertical'
|
|
70
|
-
// ? 'vertical'
|
|
71
|
-
// : 'horizontal',
|
|
72
|
-
// },
|
|
73
|
-
// },
|
|
74
|
-
// })
|
|
75
|
-
// .then((usdz: Uint8Array) => {
|
|
76
|
-
// // create blob
|
|
77
|
-
// const blob = new Blob([usdz], { type: 'model/vnd.usdz+zip' });
|
|
78
|
-
// let url = URL.createObjectURL(blob);
|
|
79
|
-
|
|
80
|
-
// if (options?.arScale === 'fixed') {
|
|
81
|
-
// url = url.concat('#allowsContentScaling=0');
|
|
82
|
-
// }
|
|
83
|
-
|
|
84
|
-
// // launch ARQuickLook
|
|
85
|
-
// const a = document.createElement('a');
|
|
86
|
-
// a.innerHTML = '<picture></picture>'; // This is actually needed so the viewer opens instantly
|
|
87
|
-
// a.rel = 'ar';
|
|
88
|
-
// a.href = url;
|
|
89
|
-
// a.download = 'scene.usdz';
|
|
90
|
-
// a.click();
|
|
91
|
-
// });
|
|
92
|
-
// }
|
|
93
53
|
}
|