@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,6 +1,6 @@
1
- import DIVEOrbitControls from '../../controls/OrbitControls';
2
- import DIVEScene from '../../scene/Scene';
3
- import DIVEToolbox from '../Toolbox';
1
+ import DIVEToolbox, { type ToolType } from '../Toolbox';
2
+ import type DIVEOrbitControls from '../../controls/OrbitControls';
3
+ import type DIVEScene from '../../scene/Scene';
4
4
 
5
5
  /**
6
6
  * @jest-environment jsdom
@@ -21,25 +21,20 @@ const mock_Canvas = {
21
21
  offsetTop: 0,
22
22
  };
23
23
 
24
- const mock_Activate = jest.fn();
25
- const mock_Deactivate = jest.fn();
26
- const mock_onPointerDown = jest.fn();
27
- const mock_onPointerMove = jest.fn();
28
- const mock_onPointerUp = jest.fn();
29
- const mock_onWheel = jest.fn();
30
- const mock_SetGizmoMode = jest.fn();
31
-
32
24
  jest.mock('../select/SelectTool.ts', () => {
33
- return jest.fn(function () {
34
- this.Activate = mock_Activate;
35
- this.Deactivate = mock_Deactivate;
36
- this.onPointerDown = mock_onPointerDown;
37
- this.onPointerMove = mock_onPointerMove;
38
- this.onPointerUp = mock_onPointerUp;
39
- this.onWheel = mock_onWheel;
40
- this.SetGizmoMode = mock_SetGizmoMode;
41
- return this;
42
- });
25
+ return {
26
+ DIVESelectTool: jest.fn(function () {
27
+ this.Activate = jest.fn();
28
+ this.Deactivate = jest.fn();
29
+ this.onPointerDown = jest.fn();
30
+ this.onPointerMove = jest.fn();
31
+ this.onPointerUp = jest.fn();
32
+ this.onWheel = jest.fn();
33
+ this.SetGizmoMode = jest.fn();
34
+ this.SetGizmoVisibility = jest.fn();
35
+ return this;
36
+ })
37
+ }
43
38
  });
44
39
 
45
40
  const mockController = {
@@ -56,52 +51,56 @@ describe('dive/toolbox/DIVEToolBox', () => {
56
51
  it('should instantiate', () => {
57
52
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
58
53
  expect(toolBox).toBeDefined();
59
- expect(mock_Activate).toHaveBeenCalledTimes(1);
60
- expect(mock_addEventListener).toHaveBeenCalled();
61
54
  });
62
55
 
63
56
  it('should dispose', () => {
64
57
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
65
- toolBox.dispose();
58
+ toolBox.Dispose();
66
59
  expect(mock_removeEventListener).toHaveBeenCalled();
67
60
  });
68
61
 
69
62
  it('should throw with incorrect tool', () => {
70
63
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
71
- expect(() => toolBox.UseTool('not a real tool')).toThrow();
72
- expect(mock_Deactivate).toHaveBeenCalledTimes(1);
64
+ expect(() => toolBox.UseTool('not a real tool' as unknown as ToolType)).toThrow();
65
+ });
66
+
67
+ it('should use no tool', () => {
68
+ const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
69
+ expect(() => toolBox.UseTool('select')).not.toThrow();
70
+ expect(() => toolBox.UseTool('none')).not.toThrow();
73
71
  });
74
72
 
75
73
  it('should use select tool', () => {
76
74
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
77
- expect(mock_Activate).toHaveBeenCalledTimes(1);
78
- toolBox.UseTool(DIVEToolbox.DefaultTool);
79
- expect(mock_Deactivate).toHaveBeenCalledTimes(1);
80
- expect(mock_Activate).toHaveBeenCalledTimes(2);
75
+ expect(() => toolBox.UseTool(DIVEToolbox.DefaultTool)).not.toThrow();
81
76
  });
82
77
 
83
78
  it('should execute pointer down event on tool', () => {
84
79
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
85
- toolBox.onPointerDown({ type: 'pointerdown' } as PointerEvent);
86
- expect(mock_onPointerDown).toHaveBeenCalledTimes(1);
80
+ expect(() => toolBox.onPointerDown({ type: 'pointerdown' } as PointerEvent)).not.toThrow();
81
+ expect(() => toolBox.UseTool('select')).not.toThrow();
82
+ expect(() => toolBox.onPointerDown({ type: 'pointerdown' } as PointerEvent)).not.toThrow();
87
83
  });
88
84
 
89
85
  it('should execute pointer move event on tool', () => {
90
86
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
91
- toolBox.onPointerMove({ type: 'pointermove' } as PointerEvent);
92
- expect(mock_onPointerMove).toHaveBeenCalledTimes(1);
87
+ expect(() => toolBox.onPointerMove({ type: 'pointermove' } as PointerEvent)).not.toThrow();
88
+ expect(() => toolBox.UseTool('select')).not.toThrow();
89
+ expect(() => toolBox.onPointerMove({ type: 'pointermove' } as PointerEvent)).not.toThrow();
93
90
  });
94
91
 
95
92
  it('should execute pointer up event on tool', () => {
96
93
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
97
- toolBox.onPointerUp({ type: 'pointerup' } as PointerEvent);
98
- expect(mock_onPointerUp).toHaveBeenCalledTimes(1);
94
+ expect(() => toolBox.onPointerUp({ type: 'pointerup' } as PointerEvent)).not.toThrow();
95
+ expect(() => toolBox.UseTool('select')).not.toThrow();
96
+ expect(() => toolBox.onPointerUp({ type: 'pointerup' } as PointerEvent)).not.toThrow();
99
97
  });
100
98
 
101
99
  it('should execute wheel event on tool', () => {
102
100
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
103
- toolBox.onWheel({ type: 'wheel' } as WheelEvent);
104
- expect(mock_onWheel).toHaveBeenCalledTimes(1);
101
+ expect(() => toolBox.onWheel({ type: 'wheel' } as WheelEvent)).not.toThrow();
102
+ expect(() => toolBox.UseTool('select')).not.toThrow();
103
+ expect(() => toolBox.onWheel({ type: 'wheel' } as WheelEvent)).not.toThrow();
105
104
  });
106
105
 
107
106
  it('should get active tool', () => {
@@ -111,7 +110,11 @@ describe('dive/toolbox/DIVEToolBox', () => {
111
110
 
112
111
  it('should set gizmo mode', () => {
113
112
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
114
- toolBox.SetGizmoMode('translate');
115
- expect(mock_SetGizmoMode).toHaveBeenCalledTimes(1);
113
+ expect(() => toolBox.SetGizmoMode('translate')).not.toThrow();
114
+ });
115
+
116
+ it('should set gizmo active', () => {
117
+ const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
118
+ expect(() => toolBox.SetGizmoVisibility(true)).not.toThrow();
116
119
  });
117
120
  });
@@ -1,9 +1,14 @@
1
- import { Object3D } from "three";
2
- import { DIVESelectable, isSelectable } from "../../interface/Selectable.ts";
1
+ import { type Object3D } from "three";
3
2
  import DIVEScene from "../../scene/Scene.ts";
4
- import { DIVEMoveable } from "../../interface/Moveable.ts";
5
- import DIVEOrbitControls from "../../controls/OrbitControls.ts";
6
3
  import DIVETransformTool from "../transform/TransformTool.ts";
4
+ import type DIVEOrbitControls from "../../controls/OrbitControls.ts";
5
+ import { type DIVESelectable, findSelectableInterface } from "../../interface/Selectable.ts";
6
+ import { type DIVEMoveable } from "../../interface/Moveable.ts";
7
+ import { type DIVEBaseTool } from "../BaseTool.ts";
8
+
9
+ export const isSelectTool = (tool: DIVEBaseTool): tool is DIVESelectTool => {
10
+ return (tool as DIVESelectTool).isSelectTool !== undefined;
11
+ }
7
12
 
8
13
  export interface DIVEObjectEventMap {
9
14
  select: object
@@ -17,7 +22,8 @@ export interface DIVEObjectEventMap {
17
22
  * @module
18
23
  */
19
24
 
20
- export default class DIVESelectTool extends DIVETransformTool {
25
+ export class DIVESelectTool extends DIVETransformTool {
26
+ readonly isSelectTool: boolean = true;
21
27
 
22
28
  constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
23
29
  super(scene, controller);
@@ -39,22 +45,23 @@ export default class DIVESelectTool extends DIVETransformTool {
39
45
  this.DetachGizmo();
40
46
  }
41
47
 
42
- public DetachGizmo(): void {
43
- this._gizmo.detach();
44
- }
45
-
46
48
  public AttachGizmo(selectable: DIVESelectable): void {
47
49
  if ('isMoveable' in selectable) {
48
50
  const movable = selectable as (Object3D & DIVESelectable & DIVEMoveable);
49
51
  this._gizmo.attach(movable);
52
+ this.SetGizmoVisibility(movable.visible);
50
53
  }
51
54
  }
52
55
 
56
+ public DetachGizmo(): void {
57
+ this._gizmo.detach();
58
+ }
59
+
53
60
  public onClick(e: PointerEvent): void {
54
61
  super.onClick(e);
55
62
 
56
- const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
57
- const selectable = this.findSelectableInterface(first?.object);
63
+ const first = this._raycaster.intersectObjects(this._scene.Root.children, true).filter((intersect) => intersect.object.visible)[0];
64
+ const selectable = findSelectableInterface(first?.object);
58
65
 
59
66
  // if nothing is hit
60
67
  if (!first || !selectable) {
@@ -76,21 +83,4 @@ export default class DIVESelectTool extends DIVETransformTool {
76
83
  // select clicked object
77
84
  this.Select(selectable);
78
85
  }
79
-
80
- private findSelectableInterface(child: Object3D): (Object3D & DIVESelectable) | undefined {
81
- if (child === undefined) return undefined;
82
-
83
- if (child.parent === null) {
84
- // in this case it is the scene itself
85
- return undefined;
86
- }
87
-
88
- if (isSelectable(child)) {
89
- // in this case it is the Selectable
90
- return child;
91
- }
92
-
93
- // search recursively in parent
94
- return this.findSelectableInterface(child.parent);
95
- }
96
86
  }
@@ -1,10 +1,11 @@
1
- import DIVESelectTool from '../SelectTool';
1
+ import { DIVESelectTool, isSelectTool } from '../SelectTool';
2
2
  import DIVEScene from '../../../scene/Scene';
3
3
  import DIVEOrbitControls from '../../../controls/OrbitControls';
4
- import DIVEPerspectiveCamera from '../../../camera/PerspectiveCamera';
5
- import DIVERenderer, { DIVERendererDefaultSettings } from '../../../renderer/Renderer';
6
- import { DIVESelectable, isSelectable } from '../../../interface/Selectable';
4
+ import { DIVERenderer, DIVERendererDefaultSettings } from '../../../renderer/Renderer';
5
+ import { DIVESelectable } from '../../../interface/Selectable';
6
+ import type DIVEPerspectiveCamera from '../../../camera/PerspectiveCamera';
7
7
  import { type Object3D } from 'three';
8
+ import { type DIVEBaseTool } from '../../BaseTool';
8
9
 
9
10
  jest.mock('../../../renderer/Renderer', () => {
10
11
  return jest.fn(function () {
@@ -41,6 +42,7 @@ jest.mock('../../../controls/OrbitControls', () => {
41
42
  jest.mock('../../../scene/Scene', () => {
42
43
  return jest.fn(function () {
43
44
  this.add = jest.fn();
45
+ this.children = [];
44
46
  this.Root = {
45
47
  children: [],
46
48
  }
@@ -108,11 +110,19 @@ jest.mock('three/examples/jsm/Addons.js', () => {
108
110
  });
109
111
 
110
112
  const mockCamera: DIVEPerspectiveCamera = {} as DIVEPerspectiveCamera;
111
- const mockRenderer: DIVERenderer = new DIVERenderer(DIVERendererDefaultSettings);
113
+ const mockRenderer = {
114
+ render: jest.fn(),
115
+ OnResize: jest.fn(),
116
+ } as unknown as DIVERenderer;
112
117
  const mockScene: DIVEScene = new DIVEScene();
113
118
  const mockController: DIVEOrbitControls = new DIVEOrbitControls(mockCamera, mockRenderer);
114
119
 
115
120
  describe('dive/toolbox/select/DIVESelectTool', () => {
121
+ it('should test if it is SelectTool', () => {
122
+ const selectTool = { isSelectTool: true } as unknown as DIVEBaseTool;
123
+ expect(isSelectTool(selectTool)).toBeDefined();
124
+ });
125
+
116
126
  it('should instantiate', () => {
117
127
  const selectTool = new DIVESelectTool(mockScene, mockController);
118
128
  expect(selectTool).toBeDefined();
@@ -130,7 +140,15 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
130
140
  });
131
141
 
132
142
  it('should execute onClick with hit', () => {
133
- mock_intersectObjects.mockReturnValueOnce([{ object: { uuid: 'test', parent: { name: 'this is the test scene root!!!', parent: null } } }]);
143
+ mock_intersectObjects.mockReturnValueOnce(
144
+ [{
145
+ object: {
146
+ uuid: 'test',
147
+ visible: true,
148
+ parent: { name: 'this is the test scene root!!!', parent: null }
149
+ }
150
+ }]
151
+ );
134
152
  const selectTool = new DIVESelectTool(mockScene, mockController);
135
153
  expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
136
154
  });
@@ -139,10 +157,10 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
139
157
  const mock_onSelect = jest.fn();
140
158
 
141
159
  mock_intersectObjects.mockReturnValueOnce([{
142
-
143
160
  object: {
144
161
  isSelectable: true,
145
162
  onSelect: mock_onSelect,
163
+ visible: true,
146
164
  parent: {
147
165
  name: 'this is the test scene root!!!',
148
166
  parent: null,
@@ -152,6 +170,7 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
152
170
  }]);
153
171
  const selectTool = new DIVESelectTool(mockScene, mockController);
154
172
  selectTool['_gizmo'].object = {
173
+ visible: true,
155
174
  isSelectable: true,
156
175
  uuid: 'test0',
157
176
  } as unknown as Object3D & DIVESelectable;
@@ -162,10 +181,10 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
162
181
  const mock_onSelect = jest.fn();
163
182
 
164
183
  mock_intersectObjects.mockReturnValueOnce([{
165
-
166
184
  object: {
167
185
  isSelectable: true,
168
186
  onSelect: mock_onSelect,
187
+ visible: true,
169
188
  parent: {
170
189
  name: 'this is the test scene root!!!',
171
190
  parent: null,
@@ -1,9 +1,13 @@
1
- import DIVEBaseTool from "../BaseTool.ts";
1
+ import { DIVEBaseTool } from "../BaseTool.ts";
2
2
  import DIVEScene from "../../scene/Scene.ts";
3
3
  import DIVEOrbitControls from "../../controls/OrbitControls.ts";
4
4
  import { TransformControls } from "three/examples/jsm/Addons";
5
5
  import { type DIVEMoveable } from "../../interface/Moveable.ts";
6
6
 
7
+ export const isTransformTool = (tool: DIVEBaseTool): tool is DIVETransformTool => {
8
+ return (tool as DIVETransformTool).isTransformTool !== undefined;
9
+ }
10
+
7
11
  export interface DIVEObjectEventMap {
8
12
  select: object
9
13
  }
@@ -17,6 +21,8 @@ export interface DIVEObjectEventMap {
17
21
  */
18
22
 
19
23
  export default class DIVETransformTool extends DIVEBaseTool {
24
+ readonly isTransformTool: boolean = true;
25
+
20
26
  protected _gizmo: TransformControls;
21
27
 
22
28
  constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
@@ -50,6 +56,15 @@ export default class DIVETransformTool extends DIVEBaseTool {
50
56
  this._gizmo.mode = mode;
51
57
  }
52
58
 
59
+ public SetGizmoVisibility(active: boolean): void {
60
+ const contains = this._scene.children.includes(this._gizmo);
61
+ if (active && !contains) {
62
+ this._scene.add(this._gizmo);
63
+ } else if (!active && contains) {
64
+ this._scene.remove(this._gizmo);
65
+ }
66
+ }
67
+
53
68
  // public onPointerDown(e: PointerEvent): void {
54
69
  // super.onPointerDown(e);
55
70
 
@@ -1,10 +1,9 @@
1
- import DIVETransformTool from '../TransformTool';
1
+ import DIVETransformTool, { isTransformTool } from '../TransformTool';
2
2
  import DIVEScene from '../../../scene/Scene';
3
3
  import DIVEOrbitControls from '../../../controls/OrbitControls';
4
4
  import DIVEPerspectiveCamera from '../../../camera/PerspectiveCamera';
5
- import DIVERenderer, { DIVERendererDefaultSettings } from '../../../renderer/Renderer';
6
- import { type Object3D } from 'three';
7
- import { type DIVEMoveable } from '../../../interface/Moveable';
5
+ import { DIVERenderer } from '../../../renderer/Renderer';
6
+ import { type DIVEBaseTool } from '../../BaseTool';
8
7
 
9
8
  jest.mock('../../../renderer/Renderer', () => {
10
9
  return jest.fn(function () {
@@ -41,9 +40,11 @@ jest.mock('../../../controls/OrbitControls', () => {
41
40
  jest.mock('../../../scene/Scene', () => {
42
41
  return jest.fn(function () {
43
42
  this.add = jest.fn();
43
+ this.remove = jest.fn();
44
44
  this.Root = {
45
45
  children: [],
46
46
  }
47
+ this.children = [];
47
48
  return this;
48
49
  });
49
50
  });
@@ -110,11 +111,29 @@ jest.mock('three/examples/jsm/Addons.js', () => {
110
111
  });
111
112
 
112
113
  const mockCamera: DIVEPerspectiveCamera = {} as DIVEPerspectiveCamera;
113
- const mockRenderer: DIVERenderer = new DIVERenderer(DIVERendererDefaultSettings);
114
+ const mockRenderer = {
115
+ render: jest.fn(),
116
+ OnResize: jest.fn(),
117
+ getViewport: jest.fn(),
118
+ setViewport: jest.fn(),
119
+ AddPreRenderCallback: jest.fn((callback) => {
120
+ callback();
121
+ }),
122
+ AddPostRenderCallback: jest.fn((callback) => {
123
+ callback();
124
+ }),
125
+ RemovePreRenderCallback: jest.fn(),
126
+ RemovePostRenderCallback: jest.fn(),
127
+ } as unknown as DIVERenderer;
114
128
  const mockScene: DIVEScene = new DIVEScene();
115
129
  const mockController: DIVEOrbitControls = new DIVEOrbitControls(mockCamera, mockRenderer);
116
130
 
117
131
  describe('dive/toolbox/select/DIVETransformTool', () => {
132
+ it('should test if it is SelectTool', () => {
133
+ const selectTool = { isTransformTool: true } as unknown as DIVEBaseTool;
134
+ expect(isTransformTool(selectTool)).toBeDefined();
135
+ });
136
+
118
137
  it('should instantiate', () => {
119
138
  const transformTool = new DIVETransformTool(mockScene, mockController);
120
139
  expect(transformTool).toBeDefined();
@@ -129,4 +148,14 @@ describe('dive/toolbox/select/DIVETransformTool', () => {
129
148
  const transformTool = new DIVETransformTool(mockScene, mockController);
130
149
  expect(() => transformTool.SetGizmoMode('translate')).not.toThrow();
131
150
  });
151
+
152
+ it('should set gizmo active', () => {
153
+ const transformTool = new DIVETransformTool(mockScene, mockController);
154
+ expect(() => transformTool.SetGizmoVisibility(true)).not.toThrow();
155
+
156
+ expect(mockScene.add).toBeCalled();
157
+
158
+ mockScene.children.includes = jest.fn().mockReturnValue(true);
159
+ expect(() => transformTool.SetGizmoVisibility(false)).not.toThrow();
160
+ });
132
161
  });