@shopware-ag/dive 1.3.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware-ag/dive",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "Shopware Spatial Framework",
5
5
  "type": "module",
6
6
  "main": "./build/dive.cjs",
@@ -161,6 +161,7 @@ describe('dive/DIVE', () => {
161
161
  const dive = new DIVE();
162
162
  expect(dive).toBeDefined();
163
163
  expect((window as any).DIVE.PrintScene).toBeDefined();
164
+ console.log = jest.fn();
164
165
  expect(() => (window as any).DIVE.PrintScene()).not.toThrow();
165
166
  });
166
167
 
@@ -100,6 +100,10 @@ export default class DIVECommunication {
100
100
  returnValue = this.selectObject(payload as Actions['SELECT_OBJECT']['PAYLOAD']);
101
101
  break;
102
102
  }
103
+ case 'DESELECT_OBJECT': {
104
+ returnValue = this.deselectObject(payload as Actions['DESELECT_OBJECT']['PAYLOAD']);
105
+ break;
106
+ }
103
107
  case 'SET_BACKGROUND': {
104
108
  returnValue = this.setBackground(payload as Actions['SET_BACKGROUND']['PAYLOAD']);
105
109
  break;
@@ -269,7 +273,25 @@ export default class DIVECommunication {
269
273
  if (!('isSelectable' in sceneObject)) return false;
270
274
 
271
275
  this.toolbox.UseTool('select');
272
- (this.toolbox.GetActiveTool() as DIVESelectTool).Select(sceneObject as DIVESelectable);
276
+ (this.toolbox.GetActiveTool() as DIVESelectTool).AttachGizmo(sceneObject as DIVESelectable);
277
+
278
+ // copy object to payload to use later
279
+ Object.assign(payload, object);
280
+
281
+ return true;
282
+ }
283
+
284
+ private deselectObject(payload: Actions['DESELECT_OBJECT']['PAYLOAD']): Actions['DESELECT_OBJECT']['RETURN'] {
285
+ const object = this.registered.get(payload.id);
286
+ if (!object) return false;
287
+
288
+ const sceneObject = this.scene.GetSceneObject(object);
289
+ if (!sceneObject) return false;
290
+
291
+ if (!('isSelectable' in sceneObject)) return false;
292
+
293
+ this.toolbox.UseTool('select');
294
+ (this.toolbox.GetActiveTool() as DIVESelectTool).DetachGizmo();
273
295
 
274
296
  // copy object to payload to use later
275
297
  Object.assign(payload, object);
@@ -113,11 +113,13 @@ const mockController = {
113
113
  RevertLast: jest.fn(),
114
114
  } as unknown as DIVEOrbitControls;
115
115
 
116
- const mockSelect = jest.fn();
116
+ const mockAttach = jest.fn();
117
+ const mockDetach = jest.fn();
117
118
  const mockToolBox = {
118
119
  UseTool: jest.fn(),
119
120
  GetActiveTool: jest.fn().mockReturnValue({
120
- Select: mockSelect,
121
+ AttachGizmo: mockAttach,
122
+ DetachGizmo: mockDetach,
121
123
  }),
122
124
  SetGizmoMode: jest.fn(),
123
125
  } as unknown as DIVEToolbox;
@@ -540,7 +542,33 @@ describe('dive/communication/DIVECommunication', () => {
540
542
  jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
541
543
  const success3 = testCom.PerformAction('SELECT_OBJECT', { id: 'test0' });
542
544
  expect(success3).toBe(true);
543
- expect(mockSelect).toHaveBeenCalledTimes(1);
545
+ expect(mockAttach).toHaveBeenCalledTimes(1);
546
+ });
547
+
548
+ it('should perform action DESELECT_OBJECT', () => {
549
+ const success0 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
550
+ expect(success0).toBe(false);
551
+
552
+ const mock0 = {
553
+ entityType: "pov",
554
+ id: "test0",
555
+ position: { x: 0, y: 0, z: 0 },
556
+ target: { x: 0, y: 0, z: 0 },
557
+ } as COMPov;
558
+ testCom.PerformAction('ADD_OBJECT', mock0);
559
+
560
+ jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce(undefined);
561
+ const success1 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
562
+ expect(success1).toBe(false);
563
+
564
+ jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({} as unknown as Object3D);
565
+ const success2 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
566
+ expect(success2).toBe(false);
567
+
568
+ jest.spyOn(mockScene, 'GetSceneObject').mockReturnValueOnce({ isSelectable: true } as unknown as Object3D);
569
+ const success3 = testCom.PerformAction('DESELECT_OBJECT', { id: 'test0' });
570
+ expect(success3).toBe(true);
571
+ expect(mockDetach).toHaveBeenCalledTimes(1);
544
572
  });
545
573
 
546
574
  it('should perform action SET_CAMERA_TRANSFORM', () => {
@@ -16,6 +16,7 @@ import UPDATE_SCENE from "./scene/updatescene.ts";
16
16
  import GENERATE_MEDIA from "./media/generatemedia.ts";
17
17
  import GET_ALL_SCENE_DATA from "./scene/getallscenedata.ts";
18
18
  import SELECT_OBJECT from "./object/selectobject.ts";
19
+ import DESELECT_OBJECT from "./object/deselectobject.ts";
19
20
  import GET_CAMERA_TRANSFORM from "./camera/getcameratransform.ts";
20
21
  import DROP_IT from "./object/model/dropit.ts";
21
22
 
@@ -27,6 +28,7 @@ export type Actions = {
27
28
  UPDATE_OBJECT: UPDATE_OBJECT,
28
29
  DELETE_OBJECT: DELETE_OBJECT,
29
30
  SELECT_OBJECT: SELECT_OBJECT,
31
+ DESELECT_OBJECT: DESELECT_OBJECT,
30
32
  SET_BACKGROUND: SET_BACKGROUND,
31
33
  DROP_IT: DROP_IT,
32
34
  PLACE_ON_FLOOR: PLACE_ON_FLOOR,
@@ -0,0 +1,6 @@
1
+ import { COMEntity } from "../../types.ts";
2
+
3
+ export default interface DESELECT_OBJECT {
4
+ 'PAYLOAD': Partial<COMEntity> & { id: string },
5
+ 'RETURN': boolean,
6
+ };
@@ -1,6 +1,6 @@
1
1
  import { PointLight, Color, SphereGeometry, MeshBasicMaterial, Mesh, FrontSide, Object3D } from 'three';
2
2
  import DIVECommunication from '../com/Communication';
3
- import { HELPER_LAYER_MASK, PRODUCT_LAYER_MASK } from '../constant/VisibilityLayerMask';
3
+ import { PRODUCT_LAYER_MASK, UI_LAYER_MASK } from '../constant/VisibilityLayerMask';
4
4
  import { DIVEMoveable } from '../interface/Moveable';
5
5
  import { DIVESelectable } from '../interface/Selectable';
6
6
  import type { TransformControls } from 'three/examples/jsm/Addons.js';
@@ -44,7 +44,7 @@ export default class DIVEPointLight extends Object3D implements DIVESelectable,
44
44
  const material = new MeshBasicMaterial({ color: this.light.color, transparent: true, opacity: 0.8, side: FrontSide });
45
45
 
46
46
  this.mesh = new Mesh(geometry, material);
47
- this.mesh.layers.mask = HELPER_LAYER_MASK;
47
+ this.mesh.layers.mask = UI_LAYER_MASK;
48
48
 
49
49
  this.add(this.mesh);
50
50
  }
@@ -68,4 +68,12 @@ export default class DIVEPointLight extends Object3D implements DIVESelectable,
68
68
  public onMove(): void {
69
69
  DIVECommunication.get(this.userData.id)?.PerformAction('UPDATE_OBJECT', { id: this.userData.id, position: this.position });
70
70
  }
71
+
72
+ public onSelect(): void {
73
+ DIVECommunication.get(this.userData.id)?.PerformAction('SELECT_OBJECT', { id: this.userData.id });
74
+ }
75
+
76
+ public onDeselect(): void {
77
+ DIVECommunication.get(this.userData.id)?.PerformAction('DESELECT_OBJECT', { id: this.userData.id });
78
+ }
71
79
  }
@@ -108,4 +108,22 @@ describe('dive/light/DIVEPointLight', () => {
108
108
  jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
109
109
  expect(() => testLight.onMove()).not.toThrow();
110
110
  });
111
+
112
+ it('should onSelect', () => {
113
+ const testLight = new DIVEPointLight();
114
+ testLight.userData.id = 'something';
115
+ expect(() => testLight.onSelect()).not.toThrow();
116
+
117
+ jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
118
+ expect(() => testLight.onSelect()).not.toThrow();
119
+ });
120
+
121
+ it('should onDeselect', () => {
122
+ const testLight = new DIVEPointLight();
123
+ testLight.userData.id = 'something';
124
+ expect(() => testLight.onDeselect()).not.toThrow();
125
+
126
+ jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
127
+ expect(() => testLight.onDeselect()).not.toThrow();
128
+ });
111
129
  });
@@ -103,4 +103,12 @@ export default class DIVEModel extends Object3D implements DIVESelectable, DIVEM
103
103
  public onMove(): void {
104
104
  DIVECommunication.get(this.userData.id)?.PerformAction('UPDATE_OBJECT', { id: this.userData.id, position: this.position, rotation: this.rotation, scale: this.scale });
105
105
  }
106
+
107
+ public onSelect(): void {
108
+ DIVECommunication.get(this.userData.id)?.PerformAction('SELECT_OBJECT', { id: this.userData.id });
109
+ }
110
+
111
+ public onDeselect(): void {
112
+ DIVECommunication.get(this.userData.id)?.PerformAction('DESELECT_OBJECT', { id: this.userData.id });
113
+ }
106
114
  }
@@ -293,4 +293,24 @@ describe('dive/model/DIVEModel', () => {
293
293
  jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
294
294
  expect(() => model.onMove()).not.toThrow();
295
295
  });
296
+
297
+ it('should onSelect', () => {
298
+ const testLight = new Model();
299
+ testLight.userData.id = 'something';
300
+
301
+ expect(() => testLight.onSelect()).not.toThrow();
302
+
303
+ jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
304
+ expect(() => testLight.onSelect()).not.toThrow();
305
+ });
306
+
307
+ it('should onDeselect', () => {
308
+ const testLight = new Model();
309
+ testLight.userData.id = 'something';
310
+
311
+ expect(() => testLight.onDeselect()).not.toThrow();
312
+
313
+ jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
314
+ expect(() => testLight.onDeselect()).not.toThrow();
315
+ });
296
316
  });
@@ -21,16 +21,21 @@ export default class DIVEToolbox {
21
21
  constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
22
22
  this.selectTool = new DIVESelectTool(scene, controller);
23
23
 
24
- controller.domElement.addEventListener('pointermove', this.onPointerMove.bind(this));
25
- controller.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this));
26
- controller.domElement.addEventListener('pointerup', this.onPointerUp.bind(this));
27
- controller.domElement.addEventListener('wheel', this.onWheel.bind(this));
24
+ const pointerMove = this.onPointerMove.bind(this);
25
+ const pointerDown = this.onPointerDown.bind(this);
26
+ const pointerUp = this.onPointerUp.bind(this);
27
+ const wheel = this.onWheel.bind(this);
28
+
29
+ controller.domElement.addEventListener('pointermove', pointerMove);
30
+ controller.domElement.addEventListener('pointerdown', pointerDown);
31
+ controller.domElement.addEventListener('pointerup', pointerUp);
32
+ controller.domElement.addEventListener('wheel', wheel);
28
33
 
29
34
  this.removeListenersCallback = () => {
30
- controller.domElement.removeEventListener('pointermove', this.onPointerMove.bind(this));
31
- controller.domElement.removeEventListener('pointerdown', this.onPointerDown.bind(this));
32
- controller.domElement.removeEventListener('pointerup', this.onPointerUp.bind(this));
33
- controller.domElement.removeEventListener('wheel', this.onWheel.bind(this));
35
+ controller.domElement.removeEventListener('pointermove', pointerMove);
36
+ controller.domElement.removeEventListener('pointerdown', pointerDown);
37
+ controller.domElement.removeEventListener('pointerup', pointerUp);
38
+ controller.domElement.removeEventListener('wheel', wheel);
34
39
  };
35
40
 
36
41
  // default tool
@@ -30,17 +30,26 @@ export default class DIVESelectTool extends DIVETransformTool {
30
30
  public Select(selectable: DIVESelectable): void {
31
31
  if (selectable.onSelect) selectable.onSelect();
32
32
 
33
- if ('isMoveable' in selectable) {
34
- const movable = selectable as (Object3D & DIVESelectable & DIVEMoveable);
35
- this._gizmo.attach(movable);
36
- }
33
+ this.AttachGizmo(selectable);
37
34
  }
38
35
 
39
36
  public Deselect(selectable: DIVESelectable): void {
40
37
  if (selectable.onDeselect) selectable.onDeselect();
38
+
39
+ this.DetachGizmo();
40
+ }
41
+
42
+ public DetachGizmo(): void {
41
43
  this._gizmo.detach();
42
44
  }
43
45
 
46
+ public AttachGizmo(selectable: DIVESelectable): void {
47
+ if ('isMoveable' in selectable) {
48
+ const movable = selectable as (Object3D & DIVESelectable & DIVEMoveable);
49
+ this._gizmo.attach(movable);
50
+ }
51
+ }
52
+
44
53
  public onClick(e: PointerEvent): void {
45
54
  super.onClick(e);
46
55
 
@@ -2,6 +2,7 @@ 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
+ import { type DIVEMoveable } from "../../interface/Moveable.ts";
5
6
 
6
7
  export interface DIVEObjectEventMap {
7
8
  select: object
@@ -25,6 +26,21 @@ export default class DIVETransformTool extends DIVEBaseTool {
25
26
  this._gizmo = new TransformControls(this._controller.object, this._controller.domElement);
26
27
  this._gizmo.mode = 'translate';
27
28
 
29
+ this._gizmo.addEventListener('mouseDown', () => {
30
+ controller.enabled = false;
31
+ });
32
+
33
+ this._gizmo.addEventListener('mouseUp', () => {
34
+ controller.enabled = true;
35
+ });
36
+
37
+ this._gizmo.addEventListener('objectChange', () => {
38
+ if (!this._gizmo.object) return;
39
+ if (!('isMoveable' in this._gizmo.object)) return;
40
+ if (!('onMove' in this._gizmo.object)) return;
41
+ (this._gizmo.object as DIVEMoveable).onMove!();
42
+ });
43
+
28
44
  scene.add(this._gizmo);
29
45
  }
30
46
 
@@ -0,0 +1,132 @@
1
+ import DIVETransformTool from '../TransformTool';
2
+ import DIVEScene from '../../../scene/Scene';
3
+ import DIVEOrbitControls from '../../../controls/OrbitControls';
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';
8
+
9
+ jest.mock('../../../renderer/Renderer', () => {
10
+ return jest.fn(function () {
11
+ return this;
12
+ });
13
+ });
14
+
15
+ jest.mock('../../../camera/PerspectiveCamera', () => {
16
+ return jest.fn(function () {
17
+ this.isPerspectiveCamera = true;
18
+ this.layers = {
19
+ mask: 0,
20
+ };
21
+ return this;
22
+ });
23
+ });
24
+
25
+ jest.mock('../../../controls/OrbitControls', () => {
26
+ return jest.fn(function () {
27
+ this.enabled = true;
28
+ this.domElement = {
29
+ clientWIdth: 0,
30
+ clientHeight: 0,
31
+ };
32
+ this.object = {
33
+ layers: {
34
+ mask: 0,
35
+ }
36
+ };
37
+ return this;
38
+ });
39
+ });
40
+
41
+ jest.mock('../../../scene/Scene', () => {
42
+ return jest.fn(function () {
43
+ this.add = jest.fn();
44
+ this.Root = {
45
+ children: [],
46
+ }
47
+ return this;
48
+ });
49
+ });
50
+
51
+ const mock_intersectObjects = jest.fn().mockReturnValue([]);
52
+
53
+ jest.mock('three', () => {
54
+ return {
55
+ Vector2: jest.fn(function () {
56
+ return this;
57
+ }),
58
+ Vector3: jest.fn(function () {
59
+ return this;
60
+ }),
61
+ Raycaster: jest.fn(function () {
62
+ this.setFromCamera = jest.fn();
63
+ this.intersectObjects = mock_intersectObjects;
64
+ this.layers = {
65
+ mask: 0,
66
+ };
67
+ return this;
68
+ }),
69
+ };
70
+ });
71
+
72
+ const mock_attach = jest.fn();
73
+ const mock_detach = jest.fn();
74
+
75
+ jest.mock('three/examples/jsm/Addons.js', () => {
76
+ return {
77
+ TransformControls: jest.fn(function () {
78
+ this.addEventListener = (type: string, callback: (e: object) => void) => {
79
+ this.object = null;
80
+ callback({ value: false });
81
+ this.object = {};
82
+ callback({ value: false });
83
+ this.object = {
84
+ isMoveable: true
85
+ };
86
+ callback({ value: false });
87
+ this.object = {
88
+ isMoveable: true,
89
+ onMove: jest.fn(),
90
+ };
91
+ callback({ value: false });
92
+ },
93
+ this.attach = mock_attach,
94
+ this.detach = mock_detach,
95
+ this.traverse = function (callback: (obj: object) => void) {
96
+ callback(this);
97
+ };
98
+ this.setMode = jest.fn();
99
+ this.getRaycaster = jest.fn().mockReturnValue({
100
+ layers: {
101
+ mask: 0,
102
+ },
103
+ });
104
+ this.layers = {
105
+ mask: 0,
106
+ };
107
+ return this;
108
+ }),
109
+ };
110
+ });
111
+
112
+ const mockCamera: DIVEPerspectiveCamera = {} as DIVEPerspectiveCamera;
113
+ const mockRenderer: DIVERenderer = new DIVERenderer(DIVERendererDefaultSettings);
114
+ const mockScene: DIVEScene = new DIVEScene();
115
+ const mockController: DIVEOrbitControls = new DIVEOrbitControls(mockCamera, mockRenderer);
116
+
117
+ describe('dive/toolbox/select/DIVETransformTool', () => {
118
+ it('should instantiate', () => {
119
+ const transformTool = new DIVETransformTool(mockScene, mockController);
120
+ expect(transformTool).toBeDefined();
121
+ });
122
+
123
+ it('should activate', () => {
124
+ const transformTool = new DIVETransformTool(mockScene, mockController);
125
+ expect(() => transformTool.Activate()).not.toThrow();
126
+ });
127
+
128
+ it('should set gizmo mode', () => {
129
+ const transformTool = new DIVETransformTool(mockScene, mockController);
130
+ expect(() => transformTool.SetGizmoMode('translate')).not.toThrow();
131
+ });
132
+ });