@shopware-ag/dive 1.4.2 → 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 (46) hide show
  1. package/README.md +38 -24
  2. package/build/dive.cjs +734 -492
  3. package/build/dive.cjs.map +1 -1
  4. package/build/dive.d.cts +182 -113
  5. package/build/dive.d.ts +182 -113
  6. package/build/dive.js +733 -478
  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 +59 -16
  15. package/src/com/__test__/Communication.test.ts +83 -24
  16. package/src/com/actions/camera/computeencompassingview.ts +9 -0
  17. package/src/com/actions/index.ts +4 -0
  18. package/src/com/actions/scene/updatescene.ts +1 -0
  19. package/src/com/actions/toolbox/transform/setgizmovisible.ts +4 -0
  20. package/src/controls/OrbitControls.ts +14 -2
  21. package/src/controls/__test__/OrbitControls.test.ts +31 -4
  22. package/src/dive.ts +93 -33
  23. package/src/grid/Grid.ts +4 -0
  24. package/src/grid/__test__/Grid.test.ts +7 -0
  25. package/src/interface/Selectable.ts +17 -0
  26. package/src/interface/__test__/Interfaces.test.ts +18 -0
  27. package/src/mediacreator/MediaCreator.ts +2 -2
  28. package/src/mediacreator/__test__/MediaCreator.test.ts +12 -10
  29. package/src/model/Model.ts +6 -0
  30. package/src/model/__test__/Model.test.ts +9 -0
  31. package/src/renderer/Renderer.ts +7 -1
  32. package/src/renderer/__test__/Renderer.test.ts +14 -5
  33. package/src/scene/Scene.ts +8 -2
  34. package/src/scene/__test__/Scene.test.ts +6 -0
  35. package/src/scene/root/Root.ts +11 -1
  36. package/src/scene/root/__test__/Root.test.ts +68 -2
  37. package/src/scene/root/modelroot/ModelRoot.ts +1 -1
  38. package/src/scene/root/modelroot/__test__/ModelRoot.test.ts +1 -0
  39. package/src/toolbox/BaseTool.ts +3 -3
  40. package/src/toolbox/Toolbox.ts +57 -37
  41. package/src/toolbox/__test__/BaseTool.test.ts +49 -7
  42. package/src/toolbox/__test__/Toolbox.test.ts +43 -40
  43. package/src/toolbox/select/SelectTool.ts +18 -28
  44. package/src/toolbox/select/__test__/SelectTool.test.ts +27 -8
  45. package/src/toolbox/transform/TransformTool.ts +16 -1
  46. package/src/toolbox/transform/__test__/TransformTool.test.ts +34 -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';
@@ -23,10 +20,15 @@ import '../actions/scene/getallscenedata';
23
20
  import '../actions/scene/setbackground';
24
21
  import '../actions/scene/updatescene';
25
22
  import '../actions/toolbox/select/setgizmomode';
23
+ import '../actions/toolbox/transform/setgizmovisible';
26
24
  import '../actions/camera/getcameratransform';
27
- import DIVEOrbitControls from '../../controls/OrbitControls';
28
- import { COMLight, COMModel, COMPov } from '../types';
29
- 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';
30
32
 
31
33
  jest.mock('three/src/math/MathUtils', () => {
32
34
  return {
@@ -34,6 +36,33 @@ jest.mock('three/src/math/MathUtils', () => {
34
36
  }
35
37
  });
36
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
+
37
66
  const mockScene = {
38
67
  SetBackground: jest.fn(),
39
68
  AddSceneObject: jest.fn(),
@@ -55,8 +84,12 @@ const mockScene = {
55
84
  },
56
85
  SetVisibility: jest.fn(),
57
86
  SetColor: jest.fn(),
58
- }
87
+ },
88
+ Grid: {
89
+ SetVisibility: jest.fn(),
90
+ },
59
91
  },
92
+ ComputeSceneBB: jest.fn(),
60
93
  } as unknown as DIVEScene;
61
94
 
62
95
  const mockController = {
@@ -107,32 +140,44 @@ const mockController = {
107
140
  clone: jest.fn().mockReturnValue({ x: 1, y: 2, z: 3 }),
108
141
  copy: jest.fn(),
109
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
+ },
110
151
  SetCameraLayer: jest.fn(),
152
+ OnResize: jest.fn(),
153
+ layers: {
154
+ mask: 1,
155
+ },
111
156
  },
112
157
  MoveTo: jest.fn(),
113
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
+ }),
114
163
  } as unknown as DIVEOrbitControls;
115
164
 
116
- const mockAttach = jest.fn();
117
- const mockDetach = jest.fn();
118
165
  const mockToolBox = {
119
166
  UseTool: jest.fn(),
120
167
  GetActiveTool: jest.fn().mockReturnValue({
121
- AttachGizmo: mockAttach,
122
- DetachGizmo: mockDetach,
168
+ AttachGizmo: jest.fn(),
169
+ DetachGizmo: jest.fn(),
123
170
  }),
124
171
  SetGizmoMode: jest.fn(),
172
+ SetGizmoVisibility: jest.fn(),
125
173
  } as unknown as DIVEToolbox;
126
174
 
127
- const mockMediaCreator = {
128
- GenerateMedia: jest.fn(),
129
- } as unknown as DIVEMediaCreator;
130
175
  let testCom: DIVECommunication;
131
176
 
132
177
 
133
178
  describe('dive/communication/DIVECommunication', () => {
134
179
  beforeEach(() => {
135
- testCom = new DIVECommunication(mockScene, mockController, mockToolBox, mockMediaCreator);
180
+ testCom = new DIVECommunication(mockRenderer, mockScene, mockController, mockToolBox);
136
181
  });
137
182
 
138
183
  afterEach(() => {
@@ -191,13 +236,6 @@ describe('dive/communication/DIVECommunication', () => {
191
236
  expect(() => testCom.PerformAction('ADD_OBJECT', payload)).not.toThrow();
192
237
  });
193
238
 
194
- // it('should not dispatch with invalid action type', () => {
195
- // const listener = jest.fn();
196
- // testCom.Subscribe('GET_ALL_OBJECTS', listener);
197
- // testCom.dispatch('INVALID_ACTION_TYPE', {});
198
- // expect(listener).toHaveBeenCalledTimes(0);
199
- // });
200
-
201
239
  it('should perform action ADD_OBJECT', () => {
202
240
  const payload = {
203
241
  entityType: "light",
@@ -432,6 +470,19 @@ describe('dive/communication/DIVECommunication', () => {
432
470
  expect(successSet).toBe(true);
433
471
  });
434
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
+
435
486
  it('should perform action GET_ALL_SCENE_DATA', () => {
436
487
  testCom.PerformAction('ADD_OBJECT', {
437
488
  entityType: "pov",
@@ -542,7 +593,6 @@ describe('dive/communication/DIVECommunication', () => {
542
593
  jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
543
594
  const success3 = testCom.PerformAction('SELECT_OBJECT', { id: 'test0' });
544
595
  expect(success3).toBe(true);
545
- expect(mockAttach).toHaveBeenCalledTimes(1);
546
596
  });
547
597
 
548
598
  it('should perform action DESELECT_OBJECT', () => {
@@ -568,7 +618,6 @@ describe('dive/communication/DIVECommunication', () => {
568
618
  jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
569
619
  const success3 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
570
620
  expect(success3).toBe(true);
571
- expect(mockDetach).toHaveBeenCalledTimes(1);
572
621
  });
573
622
 
574
623
  it('should perform action SET_CAMERA_TRANSFORM', () => {
@@ -614,6 +663,14 @@ describe('dive/communication/DIVECommunication', () => {
614
663
  expect(success).toBe(true);
615
664
  });
616
665
 
666
+ it('should perform action SET_GIZMO_VISIBILITY', () => {
667
+ let visibility = testCom.PerformAction('SET_GIZMO_VISIBILITY', true);
668
+ expect(visibility).toBe(true);
669
+
670
+ visibility = testCom.PerformAction('SET_GIZMO_VISIBILITY', false);
671
+ expect(visibility).toBe(false);
672
+ });
673
+
617
674
  it('should perform action MODEL_LOADED', () => {
618
675
  const payload = {
619
676
  entityType: "model",
@@ -637,6 +694,7 @@ describe('dive/communication/DIVECommunication', () => {
637
694
  backgroundColor: 'ffffff',
638
695
  floorEnabled: true,
639
696
  floorColor: 'ffffff',
697
+ gridEnabled: true,
640
698
  });
641
699
  expect(success0).toBe(true);
642
700
 
@@ -645,6 +703,7 @@ describe('dive/communication/DIVECommunication', () => {
645
703
  backgroundColor: undefined,
646
704
  floorEnabled: undefined,
647
705
  floorColor: undefined,
706
+ gridEnabled: undefined,
648
707
  });
649
708
  expect(success1).toBe(true);
650
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
+ };
@@ -19,6 +19,8 @@ import SELECT_OBJECT from "./object/selectobject.ts";
19
19
  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
+ import SET_GIZMO_VISIBILITY from "./toolbox/transform/setgizmovisible.js";
23
+ import COMPUTE_ENCOMPASSING_VIEW from "./camera/computeencompassingview.ts";
22
24
 
23
25
  export type Actions = {
24
26
  GET_ALL_SCENE_DATA: GET_ALL_SCENE_DATA,
@@ -36,9 +38,11 @@ export type Actions = {
36
38
  GET_CAMERA_TRANSFORM: GET_CAMERA_TRANSFORM,
37
39
  MOVE_CAMERA: MOVE_CAMERA,
38
40
  RESET_CAMERA: RESET_CAMERA,
41
+ COMPUTE_ENCOMPASSING_VIEW: COMPUTE_ENCOMPASSING_VIEW,
39
42
  SET_CAMERA_LAYER: SET_CAMERA_LAYER,
40
43
  ZOOM_CAMERA: ZOOM_CAMERA,
41
44
  SET_GIZMO_MODE: SET_GIZMO_MODE,
45
+ SET_GIZMO_VISIBILITY: SET_GIZMO_VISIBILITY,
42
46
  MODEL_LOADED: MODEL_LOADED,
43
47
  UPDATE_SCENE: UPDATE_SCENE,
44
48
  GENERATE_MEDIA: GENERATE_MEDIA,
@@ -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
  },
@@ -0,0 +1,4 @@
1
+ export default interface SET_GIZMO_VISIBILITY {
2
+ 'PAYLOAD': boolean,
3
+ 'RETURN': boolean,
4
+ }
@@ -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;
@@ -57,6 +57,12 @@ export default class DIVEModel extends Object3D implements DIVESelectable, DIVEM
57
57
  this.scale.set(scale.x, scale.y, scale.z);
58
58
  }
59
59
 
60
+ public SetVisibility(visible: boolean): void {
61
+ this.traverse((child) => {
62
+ child.visible = visible;
63
+ });
64
+ }
65
+
60
66
  public SetToWorldOrigin(): void {
61
67
  this.position.set(0, 0, 0);
62
68
  DIVECommunication.get(this.userData.id)?.PerformAction('UPDATE_OBJECT', { id: this.userData.id, position: this.position, rotation: this.rotation, scale: this.scale });