@shopware-ag/dive 1.16.24 → 1.16.26-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.
Files changed (44) hide show
  1. package/build/dive.cjs +1652 -203
  2. package/build/dive.cjs.map +1 -1
  3. package/build/dive.d.cts +50 -8
  4. package/build/dive.d.ts +50 -8
  5. package/build/dive.js +1619 -159
  6. package/build/dive.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/ar/AR.ts +164 -0
  9. package/src/ar/arquicklook/ARQuickLook.ts +42 -0
  10. package/src/ar/webxr/WebXR.ts +176 -0
  11. package/src/ar/webxr/controller/WebXRController.ts +334 -0
  12. package/src/ar/webxr/crosshair/WebXRCrosshair.ts +35 -0
  13. package/src/ar/webxr/origin/WebXROrigin.ts +191 -0
  14. package/src/ar/webxr/overlay/Overlay.ts +50 -0
  15. package/src/ar/webxr/raycaster/WebXRRaycaster.ts +131 -0
  16. package/src/ar/webxr/raycaster/ar/WebXRRaycasterAR.ts +102 -0
  17. package/src/ar/webxr/raycaster/three/WebXRRaycasterTHREE.ts +49 -0
  18. package/src/ar/webxr/touchscreencontrols/WebXRTouchscreenControls.ts +356 -0
  19. package/src/axiscamera/AxisCamera.ts +4 -4
  20. package/src/axiscamera/__test__/AxisCamera.test.ts +4 -0
  21. package/src/com/Communication.ts +17 -0
  22. package/src/com/__test__/Communication.test.ts +1 -1
  23. package/src/com/actions/index.ts +2 -0
  24. package/src/dive.ts +51 -9
  25. package/src/events/EventExecutor.ts +35 -0
  26. package/src/helper/findSceneRecursive/findSceneRecursive.ts +2 -2
  27. package/src/info/Info.ts +37 -1
  28. package/src/info/__test__/Info.test.ts +45 -5
  29. package/src/mediacreator/MediaCreator.ts +4 -4
  30. package/src/mediacreator/__test__/MediaCreator.test.ts +7 -2
  31. package/src/renderer/Renderer.ts +21 -11
  32. package/src/renderer/__test__/Renderer.test.ts +19 -1
  33. package/src/scene/Scene.ts +35 -12
  34. package/src/scene/__test__/Scene.test.ts +39 -5
  35. package/src/scene/root/Root.ts +1 -0
  36. package/src/scene/xrroot/XRRoot.ts +56 -0
  37. package/src/scene/xrroot/xrlightroot/XRLightRoot.ts +80 -0
  38. package/src/toolbox/BaseTool.ts +9 -3
  39. package/src/toolbox/Toolbox.ts +1 -1
  40. package/src/toolbox/__test__/Toolbox.test.ts +1 -1
  41. package/src/toolbox/select/SelectTool.ts +1 -1
  42. package/src/toolbox/select/__test__/SelectTool.test.ts +1 -1
  43. package/src/toolbox/transform/TransformTool.ts +4 -4
  44. package/src/toolbox/transform/__test__/TransformTool.test.ts +2 -4
@@ -0,0 +1,131 @@
1
+ import { Matrix4, Mesh, Vector3 } from 'three';
2
+ import { DIVERenderer } from '../../../renderer/Renderer';
3
+ import { DIVEWebXRRaycasterAR } from './ar/WebXRRaycasterAR';
4
+ import { DIVEWebXRRaycasterTHREE } from './three/WebXRRaycasterTHREE';
5
+ import { DIVEScene } from '../../../scene/Scene';
6
+ import { DIVEEventExecutor } from '../../../events/EventExecutor';
7
+
8
+ /**
9
+ * object is undefined when AR world is hit.
10
+ */
11
+ export type DIVEHitResult = {
12
+ point: Vector3;
13
+ matrix: Matrix4;
14
+ object?: Mesh;
15
+ };
16
+
17
+ export type DIVEWebXRRaycasterEvents = {
18
+ AR_HIT_FOUND: {
19
+ hit: DIVEHitResult;
20
+ };
21
+ AR_HIT_LOST: undefined;
22
+ SCENE_HIT_FOUND: {
23
+ hit: DIVEHitResult;
24
+ };
25
+ SCENE_HIT_LOST: undefined;
26
+ };
27
+
28
+ export class DIVEWebXRRaycaster extends DIVEEventExecutor<DIVEWebXRRaycasterEvents> {
29
+ private _session: XRSession;
30
+
31
+ private _initialized: boolean = false;
32
+
33
+ private _threeRaycaster: DIVEWebXRRaycasterTHREE;
34
+ private _arRaycaster: DIVEWebXRRaycasterAR;
35
+
36
+ private _arHitResultBuffer: DIVEHitResult[] = [];
37
+ private _sceneHitResultBuffer: DIVEHitResult[] = [];
38
+
39
+ // buffers
40
+ private _hasHit: boolean = false;
41
+
42
+ constructor(session: XRSession, renderer: DIVERenderer, scene: DIVEScene) {
43
+ super();
44
+
45
+ this._session = session;
46
+
47
+ this._threeRaycaster = new DIVEWebXRRaycasterTHREE(renderer, scene);
48
+ this._arRaycaster = new DIVEWebXRRaycasterAR(session, renderer);
49
+ }
50
+
51
+ public Dispose(): void {
52
+ // dispose code here
53
+ this._initialized = false;
54
+ }
55
+
56
+ public async Init(): Promise<this> {
57
+ if (!this._session) {
58
+ console.error(
59
+ 'DIVEWebXRRaycaster: No session set in Init()! Aborting initialization...',
60
+ );
61
+ return Promise.reject();
62
+ }
63
+
64
+ if (this._initialized) {
65
+ console.error(
66
+ 'DIVEWebXRRaycaster: Already initialized! Aborting initialization...',
67
+ );
68
+ return Promise.reject();
69
+ }
70
+
71
+ await this._threeRaycaster.Init();
72
+ await this._arRaycaster.Init();
73
+
74
+ console.log('DIVEWebXRRaycaster: Initialized');
75
+
76
+ this._initialized = true;
77
+
78
+ return Promise.resolve(this);
79
+ }
80
+
81
+ public GetARIntersections(frame: XRFrame): DIVEHitResult[] {
82
+ // check for ar hits
83
+ this._arHitResultBuffer = this._arRaycaster.GetIntersections(frame);
84
+ if (this._arHitResultBuffer.length > 0) {
85
+ // hit found
86
+ this.onARHitFound(this._arHitResultBuffer[0]);
87
+ } else {
88
+ // hit nothing
89
+ this.onARHitLost();
90
+ }
91
+ return this._arHitResultBuffer;
92
+ }
93
+
94
+ public GetSceneIntersections(): DIVEHitResult[] {
95
+ // check for scene hits
96
+ this._sceneHitResultBuffer = this._threeRaycaster.GetIntersections();
97
+ if (this._sceneHitResultBuffer.length > 0) {
98
+ // scene hit found
99
+ this.onSceneHitFound(this._sceneHitResultBuffer[0]);
100
+ // early return to prevent ar raycaster from overriding scene hit
101
+ } else {
102
+ // scene hit nothing
103
+ this.onSceneHitLost();
104
+ }
105
+ return this._sceneHitResultBuffer;
106
+ }
107
+
108
+ private onARHitFound(hit: DIVEHitResult): void {
109
+ this._hasHit = true;
110
+ this.dispatch('AR_HIT_FOUND', { hit });
111
+ }
112
+
113
+ private onARHitLost(): void {
114
+ if (!this._hasHit) return;
115
+
116
+ this._hasHit = false;
117
+ this.dispatch('AR_HIT_LOST');
118
+ }
119
+
120
+ private onSceneHitFound(hit: DIVEHitResult): void {
121
+ this._hasHit = true;
122
+ this.dispatch('SCENE_HIT_FOUND', { hit });
123
+ }
124
+
125
+ private onSceneHitLost(): void {
126
+ if (!this._hasHit) return;
127
+
128
+ this._hasHit = false;
129
+ this.dispatch('SCENE_HIT_LOST');
130
+ }
131
+ }
@@ -0,0 +1,102 @@
1
+ import { Matrix4, Vector3 } from 'three';
2
+ import { type DIVERenderer } from '../../../../renderer/Renderer';
3
+ import { type DIVEHitResult } from '../WebXRRaycaster';
4
+
5
+ export class DIVEWebXRRaycasterAR {
6
+ private _session: XRSession;
7
+ private _renderer: DIVERenderer;
8
+
9
+ private _transientHitTestSource: XRTransientInputHitTestSource | undefined;
10
+ private _referenceSpaceBuffer: XRReferenceSpace | null = null;
11
+
12
+ private _requesting: boolean = false;
13
+ private _initialized: boolean = false;
14
+
15
+ private _hitMatrixBuffer: Matrix4;
16
+
17
+ constructor(session: XRSession, renderer: DIVERenderer) {
18
+ this._session = session;
19
+ this._renderer = renderer;
20
+
21
+ this._hitMatrixBuffer = new Matrix4();
22
+ }
23
+
24
+ public Dispose(): void {
25
+ this._transientHitTestSource?.cancel();
26
+ this._transientHitTestSource = undefined;
27
+
28
+ this._initialized = false;
29
+ }
30
+
31
+ public async Init(): Promise<this> {
32
+ if (!this._session) {
33
+ console.error(
34
+ 'DIVEWebXRRaycaster: No session set in Init()! Aborting initialization...',
35
+ );
36
+ return Promise.reject();
37
+ }
38
+
39
+ if (this._requesting) {
40
+ console.error(
41
+ 'DIVEWebXRRaycaster: Currently initializing! Aborting initialization...',
42
+ );
43
+ return Promise.reject();
44
+ }
45
+
46
+ if (this._initialized) {
47
+ console.error(
48
+ 'DIVEWebXRRaycaster: Already initialized! Aborting initialization...',
49
+ );
50
+ return Promise.reject();
51
+ }
52
+
53
+ this._requesting = true;
54
+ this._transientHitTestSource = await this._session
55
+ .requestHitTestSourceForTransientInput!({
56
+ profile: 'generic-touchscreen',
57
+ });
58
+ this._referenceSpaceBuffer = this._renderer.xr.getReferenceSpace();
59
+ this._requesting = false;
60
+
61
+ if (!this._transientHitTestSource) {
62
+ return Promise.reject();
63
+ }
64
+
65
+ this._initialized = true;
66
+
67
+ console.log('DIVEWebXRRaycasterAR: Initialized');
68
+
69
+ return Promise.resolve(this);
70
+ }
71
+
72
+ public GetIntersections(frame: XRFrame): DIVEHitResult[] {
73
+ if (!this._transientHitTestSource) return [];
74
+
75
+ const touches = frame.getHitTestResultsForTransientInput(
76
+ this._transientHitTestSource,
77
+ );
78
+ if (touches.length === 0) return [];
79
+
80
+ const hits = touches.map((touch: XRTransientInputHitTestResult) => {
81
+ if (!this._referenceSpaceBuffer) return undefined;
82
+ if (!touch.results[0]) return undefined;
83
+ if (!touch.results[0].getPose) return undefined;
84
+
85
+ const pose = touch.results[0].getPose(this._referenceSpaceBuffer);
86
+ if (!pose) return undefined;
87
+
88
+ this._hitMatrixBuffer.fromArray(pose.transform.matrix);
89
+ const position = new Vector3().setFromMatrixPosition(
90
+ this._hitMatrixBuffer,
91
+ );
92
+
93
+ return {
94
+ point: position,
95
+ matrix: this._hitMatrixBuffer,
96
+ object: undefined,
97
+ };
98
+ });
99
+
100
+ return hits.filter((hit) => hit !== undefined) as DIVEHitResult[];
101
+ }
102
+ }
@@ -0,0 +1,49 @@
1
+ import {
2
+ type Intersection,
3
+ type Mesh,
4
+ Raycaster,
5
+ type XRTargetRaySpace,
6
+ } from 'three';
7
+ import { type DIVERenderer } from '../../../../renderer/Renderer';
8
+ import { type DIVEScene } from '../../../../scene/Scene';
9
+ import { type DIVEHitResult } from '../WebXRRaycaster';
10
+
11
+ export class DIVEWebXRRaycasterTHREE {
12
+ private _renderer: DIVERenderer;
13
+ private _scene: DIVEScene;
14
+
15
+ private _controller: XRTargetRaySpace;
16
+
17
+ // internal raycaster
18
+ private _raycaster: Raycaster = new Raycaster();
19
+
20
+ constructor(renderer: DIVERenderer, scene: DIVEScene) {
21
+ this._renderer = renderer;
22
+ this._scene = scene;
23
+
24
+ this._controller = this._renderer.xr.getController(0);
25
+ }
26
+
27
+ public async Init(): Promise<this> {
28
+ console.log('DIVEWebXRRaycasterTHREE: Initialized');
29
+ return Promise.resolve(this);
30
+ }
31
+
32
+ public GetIntersections(): DIVEHitResult[] {
33
+ this._controller.updateMatrixWorld();
34
+ this._raycaster.setFromXRController(this._controller);
35
+ const intersections = this._raycaster.intersectObjects(
36
+ this._scene.XRRoot.XRModelRoot.children,
37
+ );
38
+
39
+ if (intersections.length === 0) return [];
40
+
41
+ return intersections.map((intersection: Intersection) => {
42
+ return {
43
+ point: intersection.point,
44
+ matrix: intersection.object.matrixWorld,
45
+ object: intersection.object as Mesh,
46
+ };
47
+ });
48
+ }
49
+ }
@@ -0,0 +1,356 @@
1
+ import { Vector2 } from 'three';
2
+ import { DIVEEventExecutor } from '../../../events/EventExecutor';
3
+
4
+ export type DIVETouchscreenEvents = {
5
+ TOUCH_START: {
6
+ touches: {
7
+ current: Vector2;
8
+ }[];
9
+ touchCount: number;
10
+ };
11
+ TOUCH_MOVE: {
12
+ touches: {
13
+ current: Vector2;
14
+ delta: Vector2;
15
+ }[];
16
+ touchCount: number;
17
+ };
18
+ TOUCH_END: {
19
+ touches: {
20
+ current: Vector2;
21
+ }[];
22
+ touchCount: number;
23
+ };
24
+ ROTATE_START: {
25
+ current: number;
26
+ };
27
+ ROTATE_MOVE: {
28
+ current: number;
29
+ delta: number;
30
+ };
31
+ ROTATE_END: {
32
+ current: number;
33
+ };
34
+ PINCH_START: {
35
+ current: number;
36
+ };
37
+ PINCH_MOVE: {
38
+ current: number;
39
+ delta: number;
40
+ };
41
+ PINCH_END: {
42
+ current: number;
43
+ };
44
+ };
45
+
46
+ type DIVETouch = {
47
+ start: Vector2;
48
+ current: Vector2;
49
+ delta: Vector2;
50
+ };
51
+
52
+ export class DIVEWebXRTouchscreenControls extends DIVEEventExecutor<DIVETouchscreenEvents> {
53
+ // general members
54
+ private _session: XRSession;
55
+
56
+ // touch members
57
+ private _touchCount: number = 0;
58
+ private _touches: DIVETouch[] = [];
59
+
60
+ // rotate members
61
+ private _handleRotateStarted: boolean = false;
62
+ private _handleRotateMoved: boolean = false;
63
+ private _handleRotateEnded: boolean = false;
64
+
65
+ private _startAngle: number = 0;
66
+ private _lastAngle: number = 0;
67
+ private _angleDelta: number = 0;
68
+
69
+ // scale members
70
+ private _handlePinchStarted: boolean = false;
71
+ private _handlePinchMoved: boolean = false;
72
+ private _handlePinchEnded: boolean = false;
73
+
74
+ private _scaleDistanceStart: number = 0;
75
+ private _currentDistance: number = 1;
76
+ private _deltaDistance: number = 0;
77
+
78
+ constructor(session: XRSession) {
79
+ super();
80
+
81
+ this._session = session;
82
+
83
+ this._touches = [
84
+ {
85
+ start: new Vector2(),
86
+ current: new Vector2(),
87
+ delta: new Vector2(),
88
+ },
89
+ {
90
+ start: new Vector2(),
91
+ current: new Vector2(),
92
+ delta: new Vector2(),
93
+ },
94
+ ];
95
+
96
+ this._handleRotateStarted = false;
97
+
98
+ window.addEventListener('touchstart', (e: TouchEvent) =>
99
+ this.onTouchStart(e),
100
+ );
101
+ window.addEventListener('touchmove', (e: TouchEvent) =>
102
+ this.onTouchMove(e),
103
+ );
104
+ window.addEventListener('touchend', (e: TouchEvent) =>
105
+ this.onTouchEnd(e),
106
+ );
107
+
108
+ this._session.addEventListener('selectstart', () =>
109
+ this.onSessionSelectStart(),
110
+ );
111
+ this._session.addEventListener('selectend', () =>
112
+ this.onSessionSelectEnd(),
113
+ );
114
+ }
115
+
116
+ public Dispose(): void {
117
+ window.removeEventListener('touchstart', (e: TouchEvent) =>
118
+ this.onTouchStart(e),
119
+ );
120
+ window.removeEventListener('touchmove', (e: TouchEvent) =>
121
+ this.onTouchMove(e),
122
+ );
123
+ window.removeEventListener('touchend', (e: TouchEvent) =>
124
+ this.onTouchEnd(e),
125
+ );
126
+
127
+ this._session.removeEventListener('selectstart', () =>
128
+ this.onSessionSelectStart(),
129
+ );
130
+ this._session.removeEventListener('selectend', () =>
131
+ this.onSessionSelectEnd(),
132
+ );
133
+ }
134
+
135
+ private onTouchStart(event: TouchEvent): void {
136
+ this._touchCount = event.touches.length;
137
+
138
+ this._touches[0].start.set(
139
+ event.touches[0].clientX,
140
+ event.touches[0].clientY,
141
+ );
142
+ this._touches[0].current.set(
143
+ event.touches[0].clientX,
144
+ event.touches[0].clientY,
145
+ );
146
+ this._touches[0].delta.set(0, 0);
147
+
148
+ if (this._touchCount > 1) {
149
+ this._touches[1].start.set(
150
+ event.touches[1].clientX,
151
+ event.touches[1].clientY,
152
+ );
153
+ this._touches[1].current.set(
154
+ event.touches[1].clientX,
155
+ event.touches[1].clientY,
156
+ );
157
+ this._touches[1].delta.set(0, 0);
158
+ }
159
+
160
+ if (this._touchCount === 2) {
161
+ this.handleRotateStart();
162
+ this.handlePinchStart();
163
+ }
164
+
165
+ if (this._handleRotateStarted) {
166
+ this.dispatch('ROTATE_START', {
167
+ current: 0,
168
+ });
169
+ this._handleRotateStarted = false;
170
+ }
171
+
172
+ if (this._handlePinchStarted) {
173
+ this.dispatch('PINCH_START', {
174
+ current: 0,
175
+ });
176
+ this._handlePinchStarted = false;
177
+ }
178
+ }
179
+
180
+ private onTouchMove(event: TouchEvent): void {
181
+ this._touchCount = event.touches.length;
182
+
183
+ this._touches[0].start.set(
184
+ event.touches[0].clientX,
185
+ event.touches[0].clientY,
186
+ );
187
+ this._touches[0].current.set(
188
+ event.touches[0].clientX,
189
+ event.touches[0].clientY,
190
+ );
191
+ this._touches[0].delta.copy(
192
+ this._touches[0].current.clone().sub(this._touches[0].start),
193
+ );
194
+
195
+ if (this._touchCount > 1) {
196
+ this._touches[1].start.set(
197
+ event.touches[1].clientX,
198
+ event.touches[1].clientY,
199
+ );
200
+ this._touches[1].current.set(
201
+ event.touches[1].clientX,
202
+ event.touches[1].clientY,
203
+ );
204
+ this._touches[1].delta.copy(
205
+ this._touches[1].current.clone().sub(this._touches[1].start),
206
+ );
207
+ }
208
+
209
+ if (this._touchCount === 2) {
210
+ this.handleRotateMoved();
211
+ this.handlePinchMoved();
212
+ }
213
+
214
+ if (this._touchCount === 1) {
215
+ this.dispatch('TOUCH_MOVE', {
216
+ touches: [
217
+ {
218
+ current: this._touches[0].current.clone(),
219
+ delta: this._touches[0].delta.clone(),
220
+ },
221
+ {
222
+ current: this._touches[1].current.clone(),
223
+ delta: this._touches[1].delta.clone(),
224
+ },
225
+ ],
226
+ touchCount: this._touchCount,
227
+ });
228
+ }
229
+
230
+ if (this._touchCount === 2) {
231
+ if (this._handleRotateMoved) {
232
+ this.dispatch('ROTATE_MOVE', {
233
+ current: this._lastAngle,
234
+ delta: this._angleDelta,
235
+ });
236
+ this._handleRotateMoved = false;
237
+ }
238
+
239
+ if (this._handlePinchMoved) {
240
+ this.dispatch('PINCH_MOVE', {
241
+ current: this._currentDistance,
242
+ delta: this._deltaDistance,
243
+ });
244
+ this._handlePinchMoved = false;
245
+ }
246
+ }
247
+ }
248
+
249
+ private onTouchEnd(event: TouchEvent): void {
250
+ this._touchCount = event.touches.length;
251
+
252
+ if (this._touchCount === 0) {
253
+ this._touches[0].start.set(0, 0);
254
+ this._touches[0].current.set(0, 0);
255
+ this._touches[0].delta.set(0, 0);
256
+ }
257
+
258
+ if (this._touchCount === 1) {
259
+ this.handleRotateEnded();
260
+ this.handlePinchEnded();
261
+
262
+ this._touches[1].start.set(0, 0);
263
+ this._touches[1].current.set(0, 0);
264
+ this._touches[1].delta.set(0, 0);
265
+ }
266
+
267
+ if (this._handleRotateEnded) {
268
+ this.dispatch('ROTATE_END', {
269
+ current: this._lastAngle,
270
+ });
271
+ this._handleRotateEnded = false;
272
+ }
273
+
274
+ if (this._handlePinchEnded) {
275
+ this.dispatch('PINCH_END', {
276
+ current: this._currentDistance,
277
+ });
278
+ this._handlePinchEnded = false;
279
+ }
280
+ }
281
+
282
+ private onSessionSelectStart(): void {
283
+ this.dispatch('TOUCH_START', {
284
+ touches: [
285
+ {
286
+ current: this._touches[0].current.clone(),
287
+ },
288
+ {
289
+ current: this._touches[1].current.clone(),
290
+ },
291
+ ],
292
+ touchCount: this._touchCount,
293
+ });
294
+ }
295
+
296
+ private onSessionSelectEnd(): void {
297
+ this.dispatch('TOUCH_END', {
298
+ touches: [
299
+ {
300
+ current: this._touches[0].current.clone(),
301
+ },
302
+ {
303
+ current: this._touches[1].current.clone(),
304
+ },
305
+ ],
306
+ touchCount: this._touchCount,
307
+ });
308
+ }
309
+
310
+ // rotation handler
311
+ private handleRotateStart(): void {
312
+ this._handleRotateStarted = true;
313
+ this._startAngle = this._touches[1].start
314
+ .clone()
315
+ .sub(this._touches[0].current)
316
+ .angle();
317
+ }
318
+
319
+ private handleRotateMoved(): void {
320
+ this._handleRotateMoved = true;
321
+ const currentAngle = this._touches[1].current
322
+ .clone()
323
+ .sub(this._touches[0].current)
324
+ .angle();
325
+ this._angleDelta = currentAngle - this._startAngle;
326
+ this._lastAngle = this._angleDelta * -1;
327
+ }
328
+
329
+ private handleRotateEnded(): void {
330
+ this._handleRotateEnded = true;
331
+ }
332
+
333
+ // pinch handler
334
+ private handlePinchStart(): void {
335
+ this._handlePinchStarted = true;
336
+
337
+ this._scaleDistanceStart = this._touches[1].start.distanceTo(
338
+ this._touches[0].current,
339
+ );
340
+ }
341
+
342
+ private handlePinchMoved(): void {
343
+ this._handlePinchMoved = true;
344
+
345
+ const beforeDistance = this._currentDistance;
346
+ const distance = this._touches[1].current.distanceTo(
347
+ this._touches[0].current,
348
+ );
349
+ this._currentDistance = distance / this._scaleDistanceStart;
350
+ this._deltaDistance = this._currentDistance - beforeDistance;
351
+ }
352
+
353
+ private handlePinchEnded(): void {
354
+ this._handlePinchEnded = true;
355
+ }
356
+ }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AxesHelper,
3
3
  Color,
4
- Material,
4
+ type Material,
5
5
  Matrix4,
6
6
  OrthographicCamera,
7
7
  Vector4,
@@ -16,9 +16,9 @@ import {
16
16
  AxesColorGreenLetter,
17
17
  AxesColorBlueLetter,
18
18
  } from '../constant/AxisHelperColors.ts';
19
- import { DIVERenderer } from '../renderer/Renderer.ts';
20
- import { DIVEScene } from '../scene/Scene.ts';
21
- import DIVEOrbitControls from '../controls/OrbitControls.ts';
19
+ import { type DIVERenderer } from '../renderer/Renderer.ts';
20
+ import { type DIVEScene } from '../scene/Scene.ts';
21
+ import type DIVEOrbitControls from '../controls/OrbitControls.ts';
22
22
 
23
23
  /**
24
24
  * Shows the scene axes in the bottom left corner of the screen.
@@ -118,6 +118,10 @@ const mockScene = {
118
118
  Grid: {
119
119
  SetVisibility: jest.fn(),
120
120
  },
121
+ HelperRoot: {
122
+ add: jest.fn(),
123
+ remove: jest.fn(),
124
+ },
121
125
  },
122
126
  } as unknown as DIVEScene;
123
127