@shopware-ag/dive 1.3.1 → 1.4.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.
- package/README.md +15 -12
- package/build/dive.cjs +55 -6
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +9 -0
- package/build/dive.d.ts +9 -0
- package/build/dive.js +55 -6
- package/build/dive.js.map +1 -1
- package/package.json +1 -1
- package/src/__test__/DIVE.test.ts +1 -0
- package/src/com/Communication.ts +23 -1
- package/src/com/__test__/Communication.test.ts +31 -3
- package/src/com/actions/index.ts +2 -0
- package/src/com/actions/object/deselectobject.ts +6 -0
- package/src/light/PointLight.ts +10 -2
- package/src/light/__test__/PointLight.test.ts +18 -0
- package/src/model/Model.ts +8 -0
- package/src/model/__test__/Model.test.ts +20 -0
- package/src/toolbox/select/SelectTool.ts +13 -4
- package/src/toolbox/transform/TransformTool.ts +16 -0
- package/src/toolbox/transform/__test__/TransformTool.test.ts +132 -0
package/package.json
CHANGED
|
@@ -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
|
|
package/src/com/Communication.ts
CHANGED
|
@@ -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).
|
|
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
|
|
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
|
-
|
|
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(
|
|
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', () => {
|
package/src/com/actions/index.ts
CHANGED
|
@@ -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,
|
package/src/light/PointLight.ts
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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
|
});
|
package/src/model/Model.ts
CHANGED
|
@@ -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
|
});
|
|
@@ -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
|
-
|
|
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
|
+
});
|