@shopware-ag/dive 1.2.0 → 1.3.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/build/dive.cjs +288 -60
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +64 -6
- package/build/dive.d.ts +64 -6
- package/build/dive.js +289 -61
- package/build/dive.js.map +1 -1
- package/package.json +1 -1
- package/src/__test__/DIVE.test.ts +2 -0
- package/src/com/Communication.ts +6 -3
- package/src/com/__test__/Communication.test.ts +3 -6
- package/src/com/actions/object/getobjects.ts +2 -2
- package/src/dive.ts +7 -0
- package/src/gizmo/Gizmo.ts +130 -0
- package/src/gizmo/handles/AxisHandle.ts +124 -0
- package/src/gizmo/handles/RadialHandle.ts +119 -0
- package/src/gizmo/handles/ScaleHandle.ts +152 -0
- package/src/gizmo/plane/GizmoPlane.ts +85 -0
- package/src/gizmo/rotate/RotateGizmo.ts +95 -0
- package/src/gizmo/scale/ScaleGizmo.ts +97 -0
- package/src/gizmo/translate/TranslateGizmo.ts +88 -0
- package/src/interface/Draggable.ts +34 -0
- package/src/interface/Hoverable.ts +33 -0
- package/src/interface/Moveable.ts +0 -2
- package/src/interface/Selectable.ts +6 -0
- package/src/interface/__test__/Interfaces.test.ts +56 -0
- package/src/math/index.ts +3 -0
- package/src/math/signedAngleTo/__test__/signedAngleTo.test.ts +14 -0
- package/src/math/signedAngleTo/signedAngleTo.ts +13 -0
- package/src/scene/root/lightroot/LightRoot.ts +17 -3
- package/src/scene/root/lightroot/__test__/LightRoot.test.ts +12 -3
- package/src/scene/root/modelroot/ModelRoot.ts +17 -3
- package/src/scene/root/modelroot/__test__/ModelRoot.test.ts +13 -14
- package/src/toolbox/BaseTool.ts +254 -4
- package/src/toolbox/Toolbox.ts +6 -0
- package/src/toolbox/__test__/BaseTool.test.ts +389 -0
- package/src/toolbox/__test__/Toolbox.test.ts +8 -0
- package/src/toolbox/select/SelectTool.ts +29 -65
- package/src/toolbox/select/__test__/SelectTool.test.ts +57 -25
- package/src/toolbox/transform/TransformTool.ts +48 -0
|
@@ -24,6 +24,7 @@ const mock_Canvas = {
|
|
|
24
24
|
const mock_Activate = jest.fn();
|
|
25
25
|
const mock_Deactivate = jest.fn();
|
|
26
26
|
const mock_onPointerDown = jest.fn();
|
|
27
|
+
const mock_onPointerMove = jest.fn();
|
|
27
28
|
const mock_onPointerUp = jest.fn();
|
|
28
29
|
const mock_onWheel = jest.fn();
|
|
29
30
|
const mock_SetGizmoMode = jest.fn();
|
|
@@ -33,6 +34,7 @@ jest.mock('../select/SelectTool.ts', () => {
|
|
|
33
34
|
this.Activate = mock_Activate;
|
|
34
35
|
this.Deactivate = mock_Deactivate;
|
|
35
36
|
this.onPointerDown = mock_onPointerDown;
|
|
37
|
+
this.onPointerMove = mock_onPointerMove;
|
|
36
38
|
this.onPointerUp = mock_onPointerUp;
|
|
37
39
|
this.onWheel = mock_onWheel;
|
|
38
40
|
this.SetGizmoMode = mock_SetGizmoMode;
|
|
@@ -84,6 +86,12 @@ describe('dive/toolbox/DIVEToolBox', () => {
|
|
|
84
86
|
expect(mock_onPointerDown).toHaveBeenCalledTimes(1);
|
|
85
87
|
});
|
|
86
88
|
|
|
89
|
+
it('should execute pointer move event on tool', () => {
|
|
90
|
+
const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
|
|
91
|
+
toolBox.onPointerMove({ type: 'pointermove' } as PointerEvent);
|
|
92
|
+
expect(mock_onPointerMove).toHaveBeenCalledTimes(1);
|
|
93
|
+
});
|
|
94
|
+
|
|
87
95
|
it('should execute pointer up event on tool', () => {
|
|
88
96
|
const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
|
|
89
97
|
toolBox.onPointerUp({ type: 'pointerup' } as PointerEvent);
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import DIVEBaseTool from "../BaseTool.ts";
|
|
4
|
-
import { DIVESelectable } from "../../interface/Selectable.ts";
|
|
1
|
+
import { Object3D } from "three";
|
|
2
|
+
import { DIVESelectable, isSelectable } from "../../interface/Selectable.ts";
|
|
5
3
|
import DIVEScene from "../../scene/Scene.ts";
|
|
6
|
-
import { HELPER_LAYER_MASK, PRODUCT_LAYER_MASK, UI_LAYER_MASK } from "../../constant/VisibilityLayerMask.ts";
|
|
7
4
|
import { DIVEMoveable } from "../../interface/Moveable.ts";
|
|
8
5
|
import DIVEOrbitControls from "../../controls/OrbitControls.ts";
|
|
6
|
+
import DIVETransformTool from "../transform/TransformTool.ts";
|
|
9
7
|
|
|
10
8
|
export interface DIVEObjectEventMap {
|
|
11
9
|
select: object
|
|
@@ -19,82 +17,54 @@ export interface DIVEObjectEventMap {
|
|
|
19
17
|
* @module
|
|
20
18
|
*/
|
|
21
19
|
|
|
22
|
-
export default class DIVESelectTool extends
|
|
23
|
-
private canvas: HTMLElement;
|
|
24
|
-
private scene: DIVEScene;
|
|
25
|
-
private controller: DIVEOrbitControls;
|
|
26
|
-
private raycaster: Raycaster;
|
|
27
|
-
private gizmo: TransformControls;
|
|
28
|
-
|
|
20
|
+
export default class DIVESelectTool extends DIVETransformTool {
|
|
29
21
|
|
|
30
22
|
constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
|
|
31
|
-
super();
|
|
23
|
+
super(scene, controller);
|
|
32
24
|
this.name = "SelectTool";
|
|
33
|
-
|
|
34
|
-
this.canvas = controller.domElement;
|
|
35
|
-
this.scene = scene;
|
|
36
|
-
this.controller = controller;
|
|
37
|
-
this.raycaster = new Raycaster();
|
|
38
|
-
this.raycaster.layers.mask = PRODUCT_LAYER_MASK | HELPER_LAYER_MASK;
|
|
39
|
-
|
|
40
|
-
this.gizmo = new TransformControls(this.controller.object, this.canvas);
|
|
41
|
-
|
|
42
|
-
this.gizmo.layers.mask = UI_LAYER_MASK;
|
|
43
|
-
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & this.controller.object.layers.mask;
|
|
44
|
-
this.gizmo.traverse((child) => {
|
|
45
|
-
child.layers.mask = UI_LAYER_MASK;
|
|
46
|
-
});
|
|
47
|
-
this.gizmo.addEventListener('objectChange', () => {
|
|
48
|
-
if (!this.gizmo.object) return;
|
|
49
|
-
if (!('onMove' in this.gizmo.object)) return;
|
|
50
|
-
if (typeof this.gizmo.object.onMove !== 'function') return;
|
|
51
|
-
this.gizmo.object.onMove();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
this.controller.object.onSetCameraLayer = (mask: number) => {
|
|
55
|
-
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & mask;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
this.gizmo.addEventListener('dragging-changed', function (event) {
|
|
59
|
-
controller.enabled = !event.value;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
this.scene.add(this.gizmo);
|
|
63
25
|
}
|
|
64
26
|
|
|
65
27
|
public Activate(): void { }
|
|
66
28
|
|
|
67
|
-
public SetGizmoMode(mode: 'translate' | 'rotate' | 'scale'): void {
|
|
68
|
-
this.gizmo.setMode(mode);
|
|
69
|
-
}
|
|
70
29
|
|
|
71
30
|
public Select(selectable: DIVESelectable): void {
|
|
72
31
|
if (selectable.onSelect) selectable.onSelect();
|
|
73
32
|
|
|
74
33
|
if ('isMoveable' in selectable) {
|
|
75
34
|
const movable = selectable as (Object3D & DIVESelectable & DIVEMoveable);
|
|
76
|
-
|
|
77
|
-
this.gizmo.attach(movable);
|
|
35
|
+
this._gizmo.attach(movable);
|
|
78
36
|
}
|
|
79
37
|
}
|
|
80
38
|
|
|
81
39
|
public Deselect(selectable: DIVESelectable): void {
|
|
82
40
|
if (selectable.onDeselect) selectable.onDeselect();
|
|
83
|
-
|
|
84
|
-
this.gizmo.detach();
|
|
41
|
+
this._gizmo.detach();
|
|
85
42
|
}
|
|
86
43
|
|
|
87
|
-
public
|
|
88
|
-
|
|
89
|
-
this.raycaster.setFromCamera(pointerPos, this.controller.object);
|
|
44
|
+
public onClick(e: PointerEvent): void {
|
|
45
|
+
super.onClick(e);
|
|
90
46
|
|
|
91
|
-
const first = this.
|
|
47
|
+
const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
|
|
92
48
|
const selectable = this.findSelectableInterface(first?.object);
|
|
49
|
+
|
|
50
|
+
// if nothing is hit
|
|
93
51
|
if (!first || !selectable) {
|
|
94
|
-
if (this.
|
|
52
|
+
if (this._gizmo.object) {
|
|
53
|
+
this.Deselect(this._gizmo.object as Object3D & DIVESelectable);
|
|
54
|
+
}
|
|
95
55
|
return;
|
|
96
56
|
}
|
|
97
57
|
|
|
58
|
+
if (this._gizmo.object) {
|
|
59
|
+
// do not reselect if the same object was clicked
|
|
60
|
+
if (this._gizmo.object.uuid === selectable.uuid) return;
|
|
61
|
+
|
|
62
|
+
// deselect previous object
|
|
63
|
+
this.Deselect(this._gizmo.object as (Object3D & DIVESelectable));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// select clicked object
|
|
98
68
|
this.Select(selectable);
|
|
99
69
|
}
|
|
100
70
|
|
|
@@ -106,18 +76,12 @@ export default class DIVESelectTool extends DIVEBaseTool {
|
|
|
106
76
|
return undefined;
|
|
107
77
|
}
|
|
108
78
|
|
|
109
|
-
if (
|
|
110
|
-
|
|
79
|
+
if (isSelectable(child)) {
|
|
80
|
+
// in this case it is the Selectable
|
|
81
|
+
return child;
|
|
111
82
|
}
|
|
112
83
|
|
|
84
|
+
// search recursively in parent
|
|
113
85
|
return this.findSelectableInterface(child.parent);
|
|
114
86
|
}
|
|
115
|
-
|
|
116
|
-
private raycastFirst(): Intersection {
|
|
117
|
-
return this.raycastAll()[0];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private raycastAll(): Intersection[] {
|
|
121
|
-
return this.raycaster.intersectObjects(this.scene.Root.children, true);
|
|
122
|
-
}
|
|
123
87
|
}
|
|
@@ -3,7 +3,8 @@ import DIVEScene from '../../../scene/Scene';
|
|
|
3
3
|
import DIVEOrbitControls from '../../../controls/OrbitControls';
|
|
4
4
|
import DIVEPerspectiveCamera from '../../../camera/PerspectiveCamera';
|
|
5
5
|
import DIVERenderer, { DIVERendererDefaultSettings } from '../../../renderer/Renderer';
|
|
6
|
-
import { DIVESelectable } from '../../../interface/Selectable';
|
|
6
|
+
import { DIVESelectable, isSelectable } from '../../../interface/Selectable';
|
|
7
|
+
import { type Object3D } from 'three';
|
|
7
8
|
|
|
8
9
|
jest.mock('../../../renderer/Renderer', () => {
|
|
9
10
|
return jest.fn(function () {
|
|
@@ -11,14 +12,6 @@ jest.mock('../../../renderer/Renderer', () => {
|
|
|
11
12
|
});
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
jest.mock('../../../toolbox/BaseTool', () => {
|
|
15
|
-
return jest.fn(function (cam) {
|
|
16
|
-
this.cam = cam;
|
|
17
|
-
this.add = jest.fn();
|
|
18
|
-
return this;
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
15
|
jest.mock('../../../camera/PerspectiveCamera', () => {
|
|
23
16
|
return jest.fn(function () {
|
|
24
17
|
this.isPerspectiveCamera = true;
|
|
@@ -62,6 +55,9 @@ jest.mock('three', () => {
|
|
|
62
55
|
Vector2: jest.fn(function () {
|
|
63
56
|
return this;
|
|
64
57
|
}),
|
|
58
|
+
Vector3: jest.fn(function () {
|
|
59
|
+
return this;
|
|
60
|
+
}),
|
|
65
61
|
Raycaster: jest.fn(function () {
|
|
66
62
|
this.setFromCamera = jest.fn();
|
|
67
63
|
this.intersectObjects = mock_intersectObjects;
|
|
@@ -120,8 +116,6 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
|
|
|
120
116
|
it('should instantiate', () => {
|
|
121
117
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
122
118
|
expect(selectTool).toBeDefined();
|
|
123
|
-
expect(mockController.object.onSetCameraLayer).toBeDefined();
|
|
124
|
-
expect(() => mockController.object.onSetCameraLayer(0)).not.toThrow();
|
|
125
119
|
});
|
|
126
120
|
|
|
127
121
|
it('should activate', () => {
|
|
@@ -129,21 +123,23 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
|
|
|
129
123
|
expect(() => selectTool.Activate()).not.toThrow();
|
|
130
124
|
});
|
|
131
125
|
|
|
132
|
-
it('should execute
|
|
126
|
+
it('should execute onClick without hit', () => {
|
|
133
127
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
134
|
-
|
|
128
|
+
selectTool['_gizmo'].object = {} as unknown as Object3D & DIVESelectable;
|
|
129
|
+
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
|
|
135
130
|
});
|
|
136
131
|
|
|
137
|
-
it('should execute
|
|
138
|
-
mock_intersectObjects.mockReturnValueOnce([{ object: { parent: { name: 'this is the test scene root!!!', parent: null } } }]);
|
|
132
|
+
it('should execute onClick with hit', () => {
|
|
133
|
+
mock_intersectObjects.mockReturnValueOnce([{ object: { uuid: 'test', parent: { name: 'this is the test scene root!!!', parent: null } } }]);
|
|
139
134
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
140
|
-
expect(() => selectTool.
|
|
135
|
+
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
|
|
141
136
|
});
|
|
142
137
|
|
|
143
|
-
it('should execute
|
|
138
|
+
it('should execute onClick with same ISelectable hit', () => {
|
|
144
139
|
const mock_onSelect = jest.fn();
|
|
145
140
|
|
|
146
141
|
mock_intersectObjects.mockReturnValueOnce([{
|
|
142
|
+
|
|
147
143
|
object: {
|
|
148
144
|
isSelectable: true,
|
|
149
145
|
onSelect: mock_onSelect,
|
|
@@ -151,14 +147,41 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
|
|
|
151
147
|
name: 'this is the test scene root!!!',
|
|
152
148
|
parent: null,
|
|
153
149
|
},
|
|
150
|
+
uuid: 'test0',
|
|
154
151
|
},
|
|
155
152
|
}]);
|
|
156
153
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
selectTool['_gizmo'].object = {
|
|
155
|
+
isSelectable: true,
|
|
156
|
+
uuid: 'test0',
|
|
157
|
+
} as unknown as Object3D & DIVESelectable;
|
|
158
|
+
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it('should execute
|
|
161
|
+
it('should execute onClick with ISelectable hit', () => {
|
|
162
|
+
const mock_onSelect = jest.fn();
|
|
163
|
+
|
|
164
|
+
mock_intersectObjects.mockReturnValueOnce([{
|
|
165
|
+
|
|
166
|
+
object: {
|
|
167
|
+
isSelectable: true,
|
|
168
|
+
onSelect: mock_onSelect,
|
|
169
|
+
parent: {
|
|
170
|
+
name: 'this is the test scene root!!!',
|
|
171
|
+
parent: null,
|
|
172
|
+
},
|
|
173
|
+
uuid: 'test0',
|
|
174
|
+
},
|
|
175
|
+
}]);
|
|
176
|
+
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
177
|
+
selectTool['_gizmo'].object = {
|
|
178
|
+
isSelectable: true,
|
|
179
|
+
uuid: 'test1',
|
|
180
|
+
} as unknown as Object3D & DIVESelectable;
|
|
181
|
+
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should execute onClick with IMovable hit', () => {
|
|
162
185
|
const mock_onSelect = jest.fn();
|
|
163
186
|
|
|
164
187
|
mock_intersectObjects.mockReturnValueOnce([{
|
|
@@ -173,15 +196,24 @@ describe('dive/toolbox/select/DIVESelectTool', () => {
|
|
|
173
196
|
},
|
|
174
197
|
}]);
|
|
175
198
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
176
|
-
expect(() => selectTool.
|
|
177
|
-
expect(mock_attach).toHaveBeenCalledTimes(1);
|
|
199
|
+
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
|
|
178
200
|
});
|
|
179
201
|
|
|
180
|
-
it('should
|
|
202
|
+
it('should Select', () => {
|
|
181
203
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
204
|
+
const mock_onSelect = jest.fn();
|
|
205
|
+
expect(() => selectTool.Select({ isSelectable: true })).not.toThrow();
|
|
206
|
+
expect(() => selectTool.Select({ isMoveable: true, onSelect: mock_onSelect } as unknown as DIVESelectable)).not.toThrow();
|
|
207
|
+
expect(mock_onSelect).toHaveBeenCalledTimes(1);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should Deselect', () => {
|
|
211
|
+
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
212
|
+
const mock_onDeselect = jest.fn();
|
|
182
213
|
expect(() => selectTool.Deselect({ isSelectable: true })).not.toThrow();
|
|
183
|
-
expect(() => selectTool.Deselect({ isMoveable: true, onDeselect:
|
|
184
|
-
|
|
214
|
+
expect(() => selectTool.Deselect({ isMoveable: true, onDeselect: mock_onDeselect } as unknown as DIVESelectable)).not.toThrow();
|
|
215
|
+
expect(mock_onDeselect).toHaveBeenCalledTimes(1);
|
|
216
|
+
});
|
|
185
217
|
|
|
186
218
|
it('should set gizmo mode', () => {
|
|
187
219
|
const selectTool = new DIVESelectTool(mockScene, mockController);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import DIVEBaseTool from "../BaseTool.ts";
|
|
2
|
+
import DIVEScene from "../../scene/Scene.ts";
|
|
3
|
+
import DIVEOrbitControls from "../../controls/OrbitControls.ts";
|
|
4
|
+
import { TransformControls } from "three/examples/jsm/Addons";
|
|
5
|
+
|
|
6
|
+
export interface DIVEObjectEventMap {
|
|
7
|
+
select: object
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A Tool to select and move objects in the scene.
|
|
12
|
+
*
|
|
13
|
+
* Objects have to implement the DIVESelectable interface to be selectable and DIVEMoveable to be moveable.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export default class DIVETransformTool extends DIVEBaseTool {
|
|
19
|
+
protected _gizmo: TransformControls;
|
|
20
|
+
|
|
21
|
+
constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
|
|
22
|
+
super(scene, controller);
|
|
23
|
+
this.name = "DIVETransformTool";
|
|
24
|
+
|
|
25
|
+
this._gizmo = new TransformControls(this._controller.object, this._controller.domElement);
|
|
26
|
+
this._gizmo.mode = 'translate';
|
|
27
|
+
|
|
28
|
+
scene.add(this._gizmo);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public Activate(): void { }
|
|
32
|
+
|
|
33
|
+
public SetGizmoMode(mode: 'translate' | 'rotate' | 'scale'): void {
|
|
34
|
+
this._gizmo.mode = mode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// public onPointerDown(e: PointerEvent): void {
|
|
38
|
+
// super.onPointerDown(e);
|
|
39
|
+
|
|
40
|
+
// // if (this._hovered) {
|
|
41
|
+
// // this._dragRaycastOnObjects = this._gizmo.gizmoPlane.children;
|
|
42
|
+
// // }
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// protected raycast(): Intersection[] {
|
|
46
|
+
// return super.raycast(this._gizmo.gizmoNode.children);
|
|
47
|
+
// }
|
|
48
|
+
}
|