@shopware-ag/dive 1.5.0 → 1.6.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 (41) hide show
  1. package/README.md +38 -25
  2. package/build/dive.cjs +717 -500
  3. package/build/dive.cjs.map +1 -1
  4. package/build/dive.d.cts +174 -113
  5. package/build/dive.d.ts +174 -113
  6. package/build/dive.js +716 -486
  7. package/build/dive.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/__test__/DIVE.test.ts +66 -22
  10. package/src/animation/AnimationSystem.ts +16 -0
  11. package/src/animation/__test__/AnimationSystem.test.ts +23 -2
  12. package/src/axiscamera/AxisCamera.ts +40 -2
  13. package/src/axiscamera/__test__/AxisCamera.test.ts +178 -5
  14. package/src/com/Communication.ts +50 -16
  15. package/src/com/__test__/Communication.test.ts +73 -24
  16. package/src/com/actions/camera/computeencompassingview.ts +9 -0
  17. package/src/com/actions/index.ts +2 -0
  18. package/src/com/actions/scene/updatescene.ts +1 -0
  19. package/src/controls/OrbitControls.ts +14 -2
  20. package/src/controls/__test__/OrbitControls.test.ts +31 -4
  21. package/src/dive.ts +93 -33
  22. package/src/grid/Grid.ts +4 -0
  23. package/src/grid/__test__/Grid.test.ts +7 -0
  24. package/src/interface/Selectable.ts +17 -0
  25. package/src/interface/__test__/Interfaces.test.ts +18 -0
  26. package/src/mediacreator/MediaCreator.ts +2 -2
  27. package/src/mediacreator/__test__/MediaCreator.test.ts +12 -10
  28. package/src/renderer/Renderer.ts +7 -1
  29. package/src/renderer/__test__/Renderer.test.ts +14 -5
  30. package/src/scene/Scene.ts +8 -2
  31. package/src/scene/__test__/Scene.test.ts +6 -0
  32. package/src/scene/root/Root.ts +11 -1
  33. package/src/scene/root/__test__/Root.test.ts +68 -2
  34. package/src/toolbox/BaseTool.ts +1 -1
  35. package/src/toolbox/Toolbox.ts +53 -37
  36. package/src/toolbox/__test__/BaseTool.test.ts +43 -7
  37. package/src/toolbox/__test__/Toolbox.test.ts +39 -44
  38. package/src/toolbox/select/SelectTool.ts +17 -28
  39. package/src/toolbox/select/__test__/SelectTool.test.ts +21 -12
  40. package/src/toolbox/transform/TransformTool.ts +7 -1
  41. package/src/toolbox/transform/__test__/TransformTool.test.ts +22 -5
@@ -1,7 +1,4 @@
1
1
  import DIVECommunication from '../Communication';
2
- import DIVEScene from '../../scene/Scene';
3
- import DIVEToolbox from '../../toolbox/Toolbox';
4
- import DIVEMediaCreator from '../../mediacreator/MediaCreator';
5
2
  import '..';
6
3
  import '../types';
7
4
  import '../actions';
@@ -25,9 +22,13 @@ import '../actions/scene/updatescene';
25
22
  import '../actions/toolbox/select/setgizmomode';
26
23
  import '../actions/toolbox/transform/setgizmovisible';
27
24
  import '../actions/camera/getcameratransform';
28
- import DIVEOrbitControls from '../../controls/OrbitControls';
29
- import { COMLight, COMModel, COMPov } from '../types';
30
- import { Object3D } from 'three';
25
+ import type DIVEScene from '../../scene/Scene';
26
+ import type DIVEToolbox from '../../toolbox/Toolbox';
27
+ import type DIVEOrbitControls from '../../controls/OrbitControls';
28
+ import { type DIVERenderer } from '../../renderer/Renderer';
29
+ import { type COMLight, type COMModel, type COMPov } from '../types';
30
+ import { type Object3D } from 'three';
31
+ import { DIVESelectTool, isSelectTool } from '../../toolbox/select/SelectTool';
31
32
 
32
33
  jest.mock('three/src/math/MathUtils', () => {
33
34
  return {
@@ -35,6 +36,33 @@ jest.mock('three/src/math/MathUtils', () => {
35
36
  }
36
37
  });
37
38
 
39
+ jest.mock('../../mediacreator/MediaCreator', () => {
40
+ return {
41
+ default: jest.fn(function () {
42
+ this.GenerateMedia = jest.fn();
43
+
44
+ return this;
45
+ }),
46
+ }
47
+ });
48
+
49
+ jest.mock('../../toolbox/select/SelectTool', () => {
50
+ return {
51
+ isSelectTool: jest.fn().mockReturnValue(true),
52
+ DIVESelectTool: jest.fn().mockImplementation(() => {
53
+ return {
54
+ AttachGizmo: jest.fn(),
55
+ DetachGizmo: jest.fn(),
56
+ };
57
+ }),
58
+ }
59
+ });
60
+
61
+ const mockRenderer = {
62
+ render: jest.fn(),
63
+ OnResize: jest.fn(),
64
+ } as unknown as DIVERenderer;
65
+
38
66
  const mockScene = {
39
67
  SetBackground: jest.fn(),
40
68
  AddSceneObject: jest.fn(),
@@ -56,8 +84,12 @@ const mockScene = {
56
84
  },
57
85
  SetVisibility: jest.fn(),
58
86
  SetColor: jest.fn(),
59
- }
87
+ },
88
+ Grid: {
89
+ SetVisibility: jest.fn(),
90
+ },
60
91
  },
92
+ ComputeSceneBB: jest.fn(),
61
93
  } as unknown as DIVEScene;
62
94
 
63
95
  const mockController = {
@@ -108,33 +140,44 @@ const mockController = {
108
140
  clone: jest.fn().mockReturnValue({ x: 1, y: 2, z: 3 }),
109
141
  copy: jest.fn(),
110
142
  },
143
+ quaternion: {
144
+ x: 1,
145
+ y: 2,
146
+ z: 3,
147
+ w: 4,
148
+ clone: jest.fn().mockReturnValue({ x: 1, y: 2, z: 3, w: 4 }),
149
+ copy: jest.fn(),
150
+ },
111
151
  SetCameraLayer: jest.fn(),
152
+ OnResize: jest.fn(),
153
+ layers: {
154
+ mask: 1,
155
+ },
112
156
  },
113
157
  MoveTo: jest.fn(),
114
158
  RevertLast: jest.fn(),
159
+ ComputeEncompassingView: jest.fn().mockReturnValue({
160
+ position: { x: 1, y: 2, z: 3 },
161
+ target: { x: 4, y: 5, z: 6 }
162
+ }),
115
163
  } as unknown as DIVEOrbitControls;
116
164
 
117
- const mockAttach = jest.fn();
118
- const mockDetach = jest.fn();
119
165
  const mockToolBox = {
120
166
  UseTool: jest.fn(),
121
167
  GetActiveTool: jest.fn().mockReturnValue({
122
- AttachGizmo: mockAttach,
123
- DetachGizmo: mockDetach,
168
+ AttachGizmo: jest.fn(),
169
+ DetachGizmo: jest.fn(),
124
170
  }),
125
171
  SetGizmoMode: jest.fn(),
126
172
  SetGizmoVisibility: jest.fn(),
127
173
  } as unknown as DIVEToolbox;
128
174
 
129
- const mockMediaCreator = {
130
- GenerateMedia: jest.fn(),
131
- } as unknown as DIVEMediaCreator;
132
175
  let testCom: DIVECommunication;
133
176
 
134
177
 
135
178
  describe('dive/communication/DIVECommunication', () => {
136
179
  beforeEach(() => {
137
- testCom = new DIVECommunication(mockScene, mockController, mockToolBox, mockMediaCreator);
180
+ testCom = new DIVECommunication(mockRenderer, mockScene, mockController, mockToolBox);
138
181
  });
139
182
 
140
183
  afterEach(() => {
@@ -193,13 +236,6 @@ describe('dive/communication/DIVECommunication', () => {
193
236
  expect(() => testCom.PerformAction('ADD_OBJECT', payload)).not.toThrow();
194
237
  });
195
238
 
196
- // it('should not dispatch with invalid action type', () => {
197
- // const listener = jest.fn();
198
- // testCom.Subscribe('GET_ALL_OBJECTS', listener);
199
- // testCom.dispatch('INVALID_ACTION_TYPE', {});
200
- // expect(listener).toHaveBeenCalledTimes(0);
201
- // });
202
-
203
239
  it('should perform action ADD_OBJECT', () => {
204
240
  const payload = {
205
241
  entityType: "light",
@@ -434,6 +470,19 @@ describe('dive/communication/DIVECommunication', () => {
434
470
  expect(successSet).toBe(true);
435
471
  });
436
472
 
473
+ it('should perform action COMPUTE_ENCOMPASSING_VIEW', () => {
474
+ const payload = {};
475
+ const transform = testCom.PerformAction('COMPUTE_ENCOMPASSING_VIEW', payload);
476
+ expect(transform).toStrictEqual({
477
+ position: { x: 1, y: 2, z: 3 },
478
+ target: { x: 4, y: 5, z: 6 }
479
+ });
480
+ expect(payload).toStrictEqual({
481
+ position: { x: 1, y: 2, z: 3 },
482
+ target: { x: 4, y: 5, z: 6 }
483
+ });
484
+ });
485
+
437
486
  it('should perform action GET_ALL_SCENE_DATA', () => {
438
487
  testCom.PerformAction('ADD_OBJECT', {
439
488
  entityType: "pov",
@@ -544,7 +593,6 @@ describe('dive/communication/DIVECommunication', () => {
544
593
  jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
545
594
  const success3 = testCom.PerformAction('SELECT_OBJECT', { id: 'test0' });
546
595
  expect(success3).toBe(true);
547
- expect(mockAttach).toHaveBeenCalledTimes(1);
548
596
  });
549
597
 
550
598
  it('should perform action DESELECT_OBJECT', () => {
@@ -570,7 +618,6 @@ describe('dive/communication/DIVECommunication', () => {
570
618
  jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
571
619
  const success3 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
572
620
  expect(success3).toBe(true);
573
- expect(mockDetach).toHaveBeenCalledTimes(1);
574
621
  });
575
622
 
576
623
  it('should perform action SET_CAMERA_TRANSFORM', () => {
@@ -647,6 +694,7 @@ describe('dive/communication/DIVECommunication', () => {
647
694
  backgroundColor: 'ffffff',
648
695
  floorEnabled: true,
649
696
  floorColor: 'ffffff',
697
+ gridEnabled: true,
650
698
  });
651
699
  expect(success0).toBe(true);
652
700
 
@@ -655,6 +703,7 @@ describe('dive/communication/DIVECommunication', () => {
655
703
  backgroundColor: undefined,
656
704
  floorEnabled: undefined,
657
705
  floorColor: undefined,
706
+ gridEnabled: undefined,
658
707
  });
659
708
  expect(success1).toBe(true);
660
709
  });
@@ -0,0 +1,9 @@
1
+ import { Vector3Like } from "three";
2
+
3
+ export default interface COMPUTE_ENCOMPASSING_VIEW {
4
+ 'PAYLOAD': object,
5
+ 'RETURN': {
6
+ position: Vector3Like,
7
+ target: Vector3Like
8
+ },
9
+ };
@@ -20,6 +20,7 @@ import DESELECT_OBJECT from "./object/deselectobject.ts";
20
20
  import GET_CAMERA_TRANSFORM from "./camera/getcameratransform.ts";
21
21
  import DROP_IT from "./object/model/dropit.ts";
22
22
  import SET_GIZMO_VISIBILITY from "./toolbox/transform/setgizmovisible.js";
23
+ import COMPUTE_ENCOMPASSING_VIEW from "./camera/computeencompassingview.ts";
23
24
 
24
25
  export type Actions = {
25
26
  GET_ALL_SCENE_DATA: GET_ALL_SCENE_DATA,
@@ -37,6 +38,7 @@ export type Actions = {
37
38
  GET_CAMERA_TRANSFORM: GET_CAMERA_TRANSFORM,
38
39
  MOVE_CAMERA: MOVE_CAMERA,
39
40
  RESET_CAMERA: RESET_CAMERA,
41
+ COMPUTE_ENCOMPASSING_VIEW: COMPUTE_ENCOMPASSING_VIEW,
40
42
  SET_CAMERA_LAYER: SET_CAMERA_LAYER,
41
43
  ZOOM_CAMERA: ZOOM_CAMERA,
42
44
  SET_GIZMO_MODE: SET_GIZMO_MODE,
@@ -2,6 +2,7 @@ export default interface UPDATE_SCENE {
2
2
  'PAYLOAD': {
3
3
  name?: string,
4
4
  backgroundColor?: string | number,
5
+ gridEnabled?: boolean,
5
6
  floorEnabled?: boolean,
6
7
  floorColor?: string | number
7
8
  },
@@ -1,7 +1,7 @@
1
1
  import { OrbitControls } from "three/examples/jsm/Addons.js";
2
2
  import DIVEPerspectiveCamera from "../camera/PerspectiveCamera.ts";
3
- import DIVERenderer from "../renderer/Renderer.ts";
4
- import { MathUtils, Vector3Like } from "three";
3
+ import { DIVERenderer } from "../renderer/Renderer.ts";
4
+ import { type Box3, MathUtils, Vector3, Vector3Like } from "three";
5
5
  import { Easing, Tween } from "@tweenjs/tween.js";
6
6
 
7
7
  export type DIVEOrbitControlsSettings = {
@@ -49,6 +49,18 @@ export default class DIVEOrbitControls extends OrbitControls {
49
49
  this.dampingFactor = settings.dampingFactor;
50
50
  }
51
51
 
52
+ public ComputeEncompassingView(bb: Box3): { position: Vector3Like, target: Vector3Like } {
53
+ const center = bb.getCenter(new Vector3());
54
+ const size = bb.getSize(new Vector3());
55
+ const distance = Math.max(size.x, size.y, size.z) * 1.25;
56
+ const direction = this.object.position.clone().normalize();
57
+
58
+ return {
59
+ position: direction.multiplyScalar(distance),
60
+ target: center,
61
+ };
62
+ }
63
+
52
64
  public ZoomIn(by?: number): void {
53
65
  const zoomBy = by || DIVEOrbitControls.DEFAULT_ZOOM_FACTOR;
54
66
  const { minDistance, maxDistance } = this;
@@ -1,6 +1,7 @@
1
1
  import DIVEOrbitControls from '../OrbitControls';
2
- import DIVEPerspectiveCamera from '../../camera/PerspectiveCamera';
3
- import DIVERenderer, { DIVERendererDefaultSettings } from '../../renderer/Renderer';
2
+ import type DIVEPerspectiveCamera from '../../camera/PerspectiveCamera';
3
+ import { DIVERenderer } from '../../renderer/Renderer';
4
+ import { Box3 } from 'three';
4
5
 
5
6
  jest.mock('three/examples/jsm/Addons.js', () => {
6
7
  return {
@@ -88,11 +89,32 @@ const moveToDuration = 1000;
88
89
 
89
90
  const mockCamera = {
90
91
  position: {
91
- clone: jest.fn(),
92
+ clone: jest.fn(() => {
93
+ return mockCamera.position;
94
+ }),
95
+ normalize: jest.fn(() => {
96
+ return mockCamera.position;
97
+ }),
98
+ multiplyScalar: jest.fn(() => {
99
+ return mockCamera.position;
100
+ }),
92
101
  },
93
102
  lookAt: jest.fn(),
94
103
  } as unknown as DIVEPerspectiveCamera;
95
- const mockRenderer: DIVERenderer = new DIVERenderer(DIVERendererDefaultSettings);
104
+ const mockRenderer = {
105
+ render: jest.fn(),
106
+ OnResize: jest.fn(),
107
+ getViewport: jest.fn(),
108
+ setViewport: jest.fn(),
109
+ AddPreRenderCallback: jest.fn((callback) => {
110
+ callback();
111
+ }),
112
+ AddPostRenderCallback: jest.fn((callback) => {
113
+ callback();
114
+ }),
115
+ RemovePreRenderCallback: jest.fn(),
116
+ RemovePostRenderCallback: jest.fn(),
117
+ } as unknown as DIVERenderer;
96
118
 
97
119
  describe('dive/controls/DIVEOrbitControls', () => {
98
120
  afterEach(() => {
@@ -104,6 +126,11 @@ describe('dive/controls/DIVEOrbitControls', () => {
104
126
  expect(controller).toBeDefined();
105
127
  });
106
128
 
129
+ it('should compute encompassing view', () => {
130
+ const controller = new DIVEOrbitControls(mockCamera, mockRenderer);
131
+ expect(() => controller.ComputeEncompassingView(new Box3())).not.toThrow();
132
+ });
133
+
107
134
  it('should zoom in with default value', () => {
108
135
  const controller = new DIVEOrbitControls(mockCamera, mockRenderer);
109
136
  expect(() => controller.ZoomIn()).not.toThrow();
package/src/dive.ts CHANGED
@@ -1,9 +1,7 @@
1
- import { Vector4 } from "three";
2
- import DIVERenderer, { DIVERendererDefaultSettings, DIVERendererSettings } from "./renderer/Renderer.ts";
1
+ import { DIVERenderer, DIVERendererDefaultSettings, DIVERendererSettings } from "./renderer/Renderer.ts";
3
2
  import DIVEScene from "./scene/Scene.ts";
4
3
  import DIVEPerspectiveCamera, { DIVEPerspectiveCameraDefaultSettings, DIVEPerspectiveCameraSettings } from "./camera/PerspectiveCamera.ts";
5
4
  import DIVEOrbitControls, { DIVEOrbitControlsDefaultSettings, DIVEOrbitControlsSettings } from "./controls/OrbitControls.ts";
6
- import DIVEMediaCreator from "./mediacreator/MediaCreator.ts";
7
5
  import DIVEToolbox from "./toolbox/Toolbox.ts";
8
6
  import DIVECommunication from "./com/Communication.ts";
9
7
  import DIVEAnimationSystem from "./animation/AnimationSystem.ts";
@@ -13,9 +11,11 @@ import { getObjectDelta } from "./helper/getObjectDelta/getObjectDelta.ts";
13
11
  import type { Actions } from './com/actions/index.ts';
14
12
  import type { COMPov, COMLight, COMModel, COMEntity } from './com/types.ts';
15
13
  import { DIVEMath } from './math/index.ts';
14
+ import { generateUUID } from "three/src/math/MathUtils";
16
15
 
17
16
  export type DIVESettings = {
18
17
  autoResize: boolean;
18
+ displayAxes: boolean;
19
19
  renderer: DIVERendererSettings;
20
20
  perspectiveCamera: DIVEPerspectiveCameraSettings;
21
21
  orbitControls: DIVEOrbitControlsSettings;
@@ -23,6 +23,7 @@ export type DIVESettings = {
23
23
 
24
24
  export const DIVEDefaultSettings: DIVESettings = {
25
25
  autoResize: true,
26
+ displayAxes: false,
26
27
  renderer: DIVERendererDefaultSettings,
27
28
  perspectiveCamera: DIVEPerspectiveCameraDefaultSettings,
28
29
  orbitControls: DIVEOrbitControlsDefaultSettings,
@@ -52,6 +53,71 @@ export const DIVEDefaultSettings: DIVESettings = {
52
53
  */
53
54
 
54
55
  export default class DIVE {
56
+ // static members
57
+ public static QuickView(uri: string): DIVE {
58
+ const dive = new DIVE();
59
+
60
+ dive.Communication.PerformAction('SET_CAMERA_TRANSFORM', {
61
+ position: { x: 0, y: 2, z: 2 },
62
+ target: { x: 0, y: 0.5, z: 0 },
63
+ });
64
+
65
+ // generate scene light id
66
+ const lightid = generateUUID();
67
+
68
+ // add scene light
69
+ dive.Communication.PerformAction('ADD_OBJECT', {
70
+ entityType: 'light',
71
+ type: 'scene',
72
+ name: 'light',
73
+ id: lightid,
74
+ enabled: true,
75
+ visible: true,
76
+ intensity: 1,
77
+ color: 0xffffff,
78
+ });
79
+
80
+ // generate model id
81
+ const modelid = generateUUID();
82
+
83
+ // add loaded listener
84
+ dive.Communication.Subscribe('MODEL_LOADED', (data) => {
85
+ if (data.id !== modelid) return;
86
+ dive.Communication.PerformAction('PLACE_ON_FLOOR', {
87
+ id: modelid,
88
+ });
89
+
90
+ const transform = dive.Communication.PerformAction('COMPUTE_ENCOMPASSING_VIEW', {});
91
+
92
+ dive.Communication.PerformAction('SET_CAMERA_TRANSFORM', {
93
+ position: transform.position,
94
+ target: transform.target,
95
+ });
96
+ });
97
+
98
+ // instantiate model
99
+ dive.Communication.PerformAction('ADD_OBJECT', {
100
+ entityType: 'model',
101
+ name: 'object',
102
+ id: modelid,
103
+ position: { x: 0, y: 0, z: 0 },
104
+ rotation: { x: 0, y: 0, z: 0 },
105
+ scale: { x: 1, y: 1, z: 1 },
106
+ uri: uri,
107
+ visible: true,
108
+ loaded: false,
109
+ });
110
+
111
+ // set scene properties
112
+ dive.Communication.PerformAction('UPDATE_SCENE', {
113
+ backgroundColor: 0xffffff,
114
+ gridEnabled: false,
115
+ floorColor: 0xffffff,
116
+ });
117
+
118
+ return dive;
119
+ }
120
+
55
121
  // descriptive members
56
122
  private _settings: DIVESettings;
57
123
  private _resizeObserverId: string;
@@ -63,13 +129,12 @@ export default class DIVE {
63
129
  private scene: DIVEScene;
64
130
  private perspectiveCamera: DIVEPerspectiveCamera;
65
131
  private orbitControls: DIVEOrbitControls;
66
- private mediaCreator: DIVEMediaCreator;
67
132
  private toolbox: DIVEToolbox;
68
133
  private communication: DIVECommunication;
69
134
 
70
135
  // additional components
71
- private animationSystem: DIVEAnimationSystem;
72
- private axisCamera: DIVEAxisCamera;
136
+ private animationSystem: DIVEAnimationSystem | null;
137
+ private axisCamera: DIVEAxisCamera | null;
73
138
 
74
139
  // getters
75
140
  public get Communication(): DIVECommunication {
@@ -108,6 +173,13 @@ export default class DIVE {
108
173
  }
109
174
  }
110
175
 
176
+ if (settingsDelta.displayAxes) {
177
+ this.axisCamera = new DIVEAxisCamera(this.renderer, this.scene, this.orbitControls);
178
+ } else {
179
+ this.axisCamera?.Dispose();
180
+ this.axisCamera = null;
181
+ }
182
+
111
183
  Object.assign(this._settings, settings);
112
184
  }
113
185
 
@@ -123,38 +195,18 @@ export default class DIVE {
123
195
  this.scene = new DIVEScene();
124
196
  this.perspectiveCamera = new DIVEPerspectiveCamera(this._settings.perspectiveCamera);
125
197
  this.orbitControls = new DIVEOrbitControls(this.perspectiveCamera, this.renderer, this._settings.orbitControls);
126
- this.mediaCreator = new DIVEMediaCreator(this.renderer, this.scene, this.orbitControls);
127
198
  this.toolbox = new DIVEToolbox(this.scene, this.orbitControls);
128
- this.communication = new DIVECommunication(this.scene, this.orbitControls, this.toolbox, this.mediaCreator);
199
+ this.communication = new DIVECommunication(this.renderer, this.scene, this.orbitControls, this.toolbox);
129
200
 
130
201
  // initialize animation system
131
- this.animationSystem = new DIVEAnimationSystem();
132
- this.renderer.AddPreRenderCallback(() => {
133
- this.animationSystem.update();
134
- })
202
+ this.animationSystem = null;
135
203
 
136
204
  // initialize axis camera
137
- this.axisCamera = new DIVEAxisCamera();
138
- this.scene.add(this.axisCamera);
139
- const restoreViewport = new Vector4();
140
-
141
- this.renderer.AddPostRenderCallback(() => {
142
- const restoreBackground = this.scene.background;
143
- this.scene.background = null;
144
-
145
- this.renderer.getViewport(restoreViewport);
146
- this.renderer.setViewport(0, 0, 150, 150);
147
- this.renderer.autoClear = false;
148
-
149
- this.axisCamera.SetFromCameraMatrix(this.perspectiveCamera.matrix);
150
-
151
- this.renderer.render(this.scene, this.axisCamera);
152
-
153
- this.renderer.setViewport(restoreViewport);
154
- this.renderer.autoClear = true;
155
-
156
- this.scene.background = restoreBackground;
157
- });
205
+ if (this._settings.displayAxes) {
206
+ this.axisCamera = new DIVEAxisCamera(this.renderer, this.scene, this.orbitControls);
207
+ } else {
208
+ this.axisCamera = null;
209
+ }
158
210
 
159
211
  // add resize observer if autoResize is enabled
160
212
  if (this._settings.autoResize) {
@@ -172,6 +224,14 @@ export default class DIVE {
172
224
  }
173
225
  }
174
226
 
227
+ public Dispose(): void {
228
+ this.removeResizeObserver();
229
+ this.renderer.Dispose();
230
+ this.axisCamera?.Dispose();
231
+ this.toolbox.Dispose();
232
+ this.communication.DestroyInstance();
233
+ }
234
+
175
235
  // methods
176
236
  public OnResize(width: number, height: number): void {
177
237
  // resize renderer
package/src/grid/Grid.ts CHANGED
@@ -19,4 +19,8 @@ export default class DIVEGrid extends Object3D {
19
19
 
20
20
  this.add(grid);
21
21
  }
22
+
23
+ public SetVisibility(visible: boolean): void {
24
+ this.visible = visible;
25
+ }
22
26
  }
@@ -16,4 +16,11 @@ describe('dive/grid/DIVEGrid', () => {
16
16
  expect((grid.children[0] as GridHelper).material.depthTest).toBe(false);
17
17
  expect((grid.children[0] as GridHelper).layers.mask).toBe(HELPER_LAYER_MASK);
18
18
  });
19
+
20
+ it('should set visibility', () => {
21
+ grid.SetVisibility(false);
22
+ expect(grid.visible).toBe(false);
23
+ grid.SetVisibility(true);
24
+ expect(grid.visible).toBe(true);
25
+ });
19
26
  });
@@ -14,4 +14,21 @@ export interface DIVESelectable {
14
14
 
15
15
  export function isSelectable(object: Object3D): object is Object3D & DIVESelectable {
16
16
  return 'isSelectable' in object;
17
+ }
18
+
19
+ export function findSelectableInterface(child: Object3D): (Object3D & DIVESelectable) | undefined {
20
+ if (child === undefined) return undefined;
21
+
22
+ if (child.parent === null) {
23
+ // in this case it is the scene itself
24
+ return undefined;
25
+ }
26
+
27
+ if (isSelectable(child)) {
28
+ // in this case it is the Selectable
29
+ return child;
30
+ }
31
+
32
+ // search recursively in parent
33
+ return findSelectableInterface(child.parent);
17
34
  }
@@ -21,6 +21,24 @@ describe('interfaces', () => {
21
21
  expect(Selectable_DEF.isSelectable(Selectable as unknown as Object3D)).toBe(true);
22
22
  });
23
23
 
24
+ it('should find Selectable', () => {
25
+ let Selectable = {
26
+ isSelectable: true,
27
+ } as unknown as Object3D & Selectable_DEF.DIVESelectable;
28
+ expect(Selectable_DEF.findSelectableInterface(Selectable as unknown as Object3D)).toBe(Selectable);
29
+
30
+ let parent = {
31
+ isSelectable: true,
32
+ }
33
+ Selectable = {
34
+ parent: parent,
35
+ } as unknown as Object3D & Selectable_DEF.DIVESelectable;
36
+ expect(Selectable_DEF.findSelectableInterface(Selectable as unknown as Object3D)).toBe(parent);
37
+
38
+ Selectable = { isSelectable: true, parent: null } as unknown as Object3D & Selectable_DEF.DIVESelectable;
39
+ expect(Selectable_DEF.findSelectableInterface(Selectable as unknown as Object3D)).toBe(undefined);
40
+ });
41
+
24
42
  it('should identify Draggable', () => {
25
43
  const Draggable = { isDraggable: true };
26
44
  expect(Draggable_DEF.isDraggable(Draggable as unknown as Object3D)).toBe(true);
@@ -1,6 +1,6 @@
1
1
  import DIVEPerspectiveCamera from "../camera/PerspectiveCamera.ts";
2
2
  import DIVEScene from "../scene/Scene.ts";
3
- import DIVERenderer from "../renderer/Renderer.ts";
3
+ import { DIVERenderer } from "../renderer/Renderer.ts";
4
4
  import DIVEOrbitControls from "../controls/OrbitControls.ts";
5
5
  import { Vector3Like } from "three";
6
6
 
@@ -10,7 +10,7 @@ import { Vector3Like } from "three";
10
10
  * @module
11
11
  */
12
12
 
13
- export default class DIVEMediaCreator {
13
+ export class DIVEMediaCreator {
14
14
  private renderer: DIVERenderer;
15
15
  private scene: DIVEScene;
16
16
  private controller: DIVEOrbitControls;
@@ -1,5 +1,5 @@
1
- import DIVEMediaCreator from '../MediaCreator';
2
- import DIVERenderer from '../../renderer/Renderer';
1
+ import { DIVEMediaCreator } from '../MediaCreator';
2
+ import { DIVERenderer } from '../../renderer/Renderer';
3
3
  import DIVEScene from '../../scene/Scene';
4
4
  import DIVEPerspectiveCamera, { DIVEPerspectiveCameraDefaultSettings } from '../../camera/PerspectiveCamera';
5
5
  import { COMPov } from '../../com';
@@ -71,14 +71,16 @@ jest.mock('../../controls/OrbitControls', () => {
71
71
  });
72
72
 
73
73
  jest.mock('../../renderer/Renderer', () => {
74
- return jest.fn(function () {
75
- this.domElement = {
76
- toDataURL: mock_toDataURL,
77
- }
78
- this.render = mock_render;
79
- this.OnResize = jest.fn();
80
- return this;
81
- });
74
+ return {
75
+ DIVERenderer: jest.fn(function () {
76
+ this.domElement = {
77
+ toDataURL: mock_toDataURL,
78
+ }
79
+ this.render = mock_render;
80
+ this.OnResize = jest.fn();
81
+ return this;
82
+ })
83
+ }
82
84
  });
83
85
 
84
86
  let mediaCreator: DIVEMediaCreator;
@@ -26,7 +26,7 @@ export const DIVERendererDefaultSettings: DIVERendererSettings = {
26
26
  * @module
27
27
  */
28
28
 
29
- export default class DIVERenderer extends WebGLRenderer {
29
+ export class DIVERenderer extends WebGLRenderer {
30
30
  // basic functionality members
31
31
  private paused: boolean = false;
32
32
  private running: boolean = false;
@@ -52,6 +52,12 @@ export default class DIVERenderer extends WebGLRenderer {
52
52
  this.debug.checkShaderErrors = false;
53
53
  }
54
54
 
55
+ // Stops renderings and disposes the renderer.
56
+ public Dispose(): void {
57
+ this.StopRenderer();
58
+ this.dispose();
59
+ }
60
+
55
61
  // Starts the renderer with the given scene and camera.
56
62
  public StartRenderer(scene: Scene, cam: Camera): void {
57
63
  this.setAnimationLoop(() => { this.internal_render(scene, cam) });