@shopware-ag/dive 1.1.2 → 1.3.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 +1 -0
- package/build/dive.cjs +333 -60
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +73 -6
- package/build/dive.d.ts +73 -6
- package/build/dive.js +335 -62
- 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 +21 -3
- package/src/com/__test__/Communication.test.ts +44 -6
- package/src/com/actions/index.ts +2 -0
- package/src/com/actions/object/getobjects.ts +2 -2
- package/src/com/actions/object/model/dropit.ts +4 -0
- 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/helper/findSceneRecursive/__test__/findSceneRecursive.test.ts +40 -0
- package/src/helper/findSceneRecursive/findSceneRecursive.ts +16 -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/model/Model.ts +35 -1
- package/src/model/__test__/Model.test.ts +141 -8
- 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
- /package/src/helper/getObjectDelta/__test__/{getObjectDelta.spec.ts → getObjectDelta.test.ts} +0 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
|
|
2
|
+
import { type Object3D, type Vector3 } from 'three';
|
|
3
|
+
import DIVEOrbitControls from '../../controls/OrbitControls';
|
|
4
|
+
import DIVEScene from '../../scene/Scene';
|
|
5
|
+
import DIVEBaseTool from '../BaseTool';
|
|
6
|
+
import DIVEToolbox from '../Toolbox';
|
|
7
|
+
import { type DIVEHoverable } from '../../interface/Hoverable';
|
|
8
|
+
import { DIVEDraggable } from '../../interface/Draggable';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @jest-environment jsdom
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const mock_Canvas = {
|
|
16
|
+
width: 0,
|
|
17
|
+
height: 0,
|
|
18
|
+
getContext: jest.fn(),
|
|
19
|
+
clientWidth: 1000,
|
|
20
|
+
clientHeight: 1000,
|
|
21
|
+
offsetLeft: 0,
|
|
22
|
+
offsetTop: 0,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const mockController = {
|
|
26
|
+
domElement: mock_Canvas,
|
|
27
|
+
object: {
|
|
28
|
+
isPerspectiveCamera: true,
|
|
29
|
+
type: 'cameraP'
|
|
30
|
+
}
|
|
31
|
+
} as unknown as DIVEOrbitControls;
|
|
32
|
+
|
|
33
|
+
const mockScene = {
|
|
34
|
+
children: [],
|
|
35
|
+
} as unknown as DIVEScene;
|
|
36
|
+
|
|
37
|
+
const abstractWrapper = class Wrapper extends DIVEBaseTool {
|
|
38
|
+
constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
|
|
39
|
+
super(scene, controller);
|
|
40
|
+
this.name = "DIVEBaseTool";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe('dive/toolbox/DIVEBaseTool', () => {
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should instantiate', () => {
|
|
50
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
51
|
+
expect(toolBox).toBeDefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should raycast', () => {
|
|
55
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
56
|
+
expect(() => toolBox['raycast']()).not.toThrow();
|
|
57
|
+
expect(() => toolBox['raycast']([])).not.toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should return correct pointerAnyDown', () => {
|
|
61
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
62
|
+
expect(toolBox).toBeDefined();
|
|
63
|
+
expect(toolBox['_pointerAnyDown']).toBeDefined();
|
|
64
|
+
expect(toolBox['_pointerAnyDown']).toBe(false);
|
|
65
|
+
|
|
66
|
+
toolBox['_pointerPrimaryDown'] = true;
|
|
67
|
+
expect(toolBox['_pointerAnyDown']).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should execute onPointerDown correctly', () => {
|
|
71
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
72
|
+
expect(() => toolBox.onPointerDown({ button: 0 } as PointerEvent)).not.toThrow();
|
|
73
|
+
expect(() => toolBox.onPointerDown({ button: 1 } as PointerEvent)).not.toThrow();
|
|
74
|
+
expect(() => toolBox.onPointerDown({ button: 2 } as PointerEvent)).not.toThrow();
|
|
75
|
+
|
|
76
|
+
toolBox['_intersects'] = [
|
|
77
|
+
{
|
|
78
|
+
distance: 1,
|
|
79
|
+
point: {
|
|
80
|
+
clone() {
|
|
81
|
+
return {
|
|
82
|
+
x: 1,
|
|
83
|
+
y: 1,
|
|
84
|
+
z: 1
|
|
85
|
+
} as unknown as Vector3;
|
|
86
|
+
},
|
|
87
|
+
x: 1,
|
|
88
|
+
y: 1,
|
|
89
|
+
z: 1
|
|
90
|
+
} as unknown as Vector3,
|
|
91
|
+
object: {
|
|
92
|
+
uuid: 'uuid2',
|
|
93
|
+
isHoverable: true,
|
|
94
|
+
onPointerEnter() {
|
|
95
|
+
return;
|
|
96
|
+
},
|
|
97
|
+
} as unknown as Object3D & DIVEHoverable
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
expect(() => toolBox.onPointerDown({ button: 0 } as PointerEvent)).not.toThrow();
|
|
102
|
+
expect(() => toolBox.onPointerDown({ button: 1 } as PointerEvent)).not.toThrow();
|
|
103
|
+
expect(() => toolBox.onPointerDown({ button: 2 } as PointerEvent)).not.toThrow();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should execute onPointerMove correctly', () => {
|
|
107
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
108
|
+
jest.spyOn(toolBox['_raycaster'], 'setFromCamera').mockImplementation();
|
|
109
|
+
|
|
110
|
+
// test with no hit with hovered object before
|
|
111
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce([]);
|
|
112
|
+
|
|
113
|
+
toolBox['_hovered'] = {
|
|
114
|
+
uuid: 'uuid',
|
|
115
|
+
onPointerLeave() {
|
|
116
|
+
return;
|
|
117
|
+
},
|
|
118
|
+
} as Object3D & DIVEHoverable;
|
|
119
|
+
|
|
120
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
121
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
122
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
123
|
+
|
|
124
|
+
// test with no hovered object
|
|
125
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce(
|
|
126
|
+
[
|
|
127
|
+
{
|
|
128
|
+
distance: 1,
|
|
129
|
+
point: {
|
|
130
|
+
x: 1,
|
|
131
|
+
y: 1,
|
|
132
|
+
z: 1
|
|
133
|
+
} as unknown as Vector3,
|
|
134
|
+
object: {
|
|
135
|
+
uuid: 'uuid',
|
|
136
|
+
isHoverable: true,
|
|
137
|
+
} as Object3D & DIVEHoverable
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
toolBox['_hovered'] = null;
|
|
143
|
+
|
|
144
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
145
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
146
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
147
|
+
|
|
148
|
+
// test with no hovered object with onPointerEnter
|
|
149
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce(
|
|
150
|
+
[
|
|
151
|
+
{
|
|
152
|
+
distance: 1,
|
|
153
|
+
point: {
|
|
154
|
+
x: 1,
|
|
155
|
+
y: 1,
|
|
156
|
+
z: 1
|
|
157
|
+
} as unknown as Vector3,
|
|
158
|
+
object: {
|
|
159
|
+
uuid: 'uuid',
|
|
160
|
+
isHoverable: true,
|
|
161
|
+
onPointerEnter() {
|
|
162
|
+
return;
|
|
163
|
+
},
|
|
164
|
+
} as unknown as Object3D & DIVEHoverable
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
170
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
171
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
172
|
+
|
|
173
|
+
// test with same hovered object
|
|
174
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce(
|
|
175
|
+
[
|
|
176
|
+
{
|
|
177
|
+
distance: 1,
|
|
178
|
+
point: {
|
|
179
|
+
x: 1,
|
|
180
|
+
y: 1,
|
|
181
|
+
z: 1
|
|
182
|
+
} as unknown as Vector3,
|
|
183
|
+
object: {
|
|
184
|
+
uuid: 'uuid',
|
|
185
|
+
isHoverable: true,
|
|
186
|
+
onPointerOver() {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
} as unknown as Object3D & DIVEHoverable
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
toolBox['_hovered'] = {
|
|
195
|
+
uuid: 'uuid',
|
|
196
|
+
onPointerLeave() {
|
|
197
|
+
return;
|
|
198
|
+
},
|
|
199
|
+
} as Object3D & DIVEHoverable;
|
|
200
|
+
|
|
201
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
202
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
203
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
204
|
+
|
|
205
|
+
// test with different hovered object
|
|
206
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce(
|
|
207
|
+
[
|
|
208
|
+
{
|
|
209
|
+
distance: 1,
|
|
210
|
+
point: {
|
|
211
|
+
x: 1,
|
|
212
|
+
y: 1,
|
|
213
|
+
z: 1
|
|
214
|
+
} as unknown as Vector3,
|
|
215
|
+
object: {
|
|
216
|
+
uuid: 'uuid2',
|
|
217
|
+
isHoverable: true,
|
|
218
|
+
onPointerEnter() {
|
|
219
|
+
return;
|
|
220
|
+
},
|
|
221
|
+
} as unknown as Object3D & DIVEHoverable
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
toolBox['_hovered'] = {
|
|
227
|
+
uuid: 'uuid',
|
|
228
|
+
onPointerLeave() {
|
|
229
|
+
return;
|
|
230
|
+
},
|
|
231
|
+
} as Object3D & DIVEHoverable;
|
|
232
|
+
|
|
233
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
234
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
235
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
236
|
+
|
|
237
|
+
// test with pointer down
|
|
238
|
+
toolBox['_pointerPrimaryDown'] = true;
|
|
239
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
240
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
241
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
242
|
+
|
|
243
|
+
// test with pointer down while already dragging
|
|
244
|
+
toolBox['_pointerPrimaryDown'] = true;
|
|
245
|
+
toolBox['_dragging'] = true;
|
|
246
|
+
expect(() => toolBox.onPointerMove({ button: 0, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
247
|
+
expect(() => toolBox.onPointerMove({ button: 1, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
248
|
+
expect(() => toolBox.onPointerMove({ button: 2, offsetX: 100, offsetY: 100 } as PointerEvent)).not.toThrow();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should execute onPointerUp correctly', () => {
|
|
252
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
253
|
+
expect(() => toolBox.onPointerUp({ button: 0 } as PointerEvent)).not.toThrow();
|
|
254
|
+
expect(() => toolBox.onPointerUp({ button: 1 } as PointerEvent)).not.toThrow();
|
|
255
|
+
expect(() => toolBox.onPointerUp({ button: 2 } as PointerEvent)).not.toThrow();
|
|
256
|
+
|
|
257
|
+
toolBox['pointerWasDragged'] = () => { return true; };
|
|
258
|
+
toolBox['_dragging'] = true;
|
|
259
|
+
toolBox['_intersects'] = [
|
|
260
|
+
{
|
|
261
|
+
distance: 1,
|
|
262
|
+
point: {
|
|
263
|
+
clone() {
|
|
264
|
+
return {
|
|
265
|
+
x: 1,
|
|
266
|
+
y: 1,
|
|
267
|
+
z: 1
|
|
268
|
+
} as unknown as Vector3;
|
|
269
|
+
},
|
|
270
|
+
x: 1,
|
|
271
|
+
y: 1,
|
|
272
|
+
z: 1
|
|
273
|
+
} as unknown as Vector3,
|
|
274
|
+
object: {
|
|
275
|
+
uuid: 'uuid2',
|
|
276
|
+
isHoverable: true,
|
|
277
|
+
onPointerEnter() {
|
|
278
|
+
return;
|
|
279
|
+
},
|
|
280
|
+
} as unknown as Object3D & DIVEHoverable
|
|
281
|
+
}
|
|
282
|
+
];
|
|
283
|
+
toolBox['_draggable'] = {
|
|
284
|
+
onDragEnd() {
|
|
285
|
+
return;
|
|
286
|
+
},
|
|
287
|
+
} as unknown as Object3D & DIVEDraggable;
|
|
288
|
+
expect(() => toolBox.onPointerUp({ button: 0 } as PointerEvent)).not.toThrow();
|
|
289
|
+
expect(() => toolBox.onPointerUp({ button: 1 } as PointerEvent)).not.toThrow();
|
|
290
|
+
expect(() => toolBox.onPointerUp({ button: 2 } as PointerEvent)).not.toThrow();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should execute onDragStart correctly', () => {
|
|
294
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
295
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
296
|
+
|
|
297
|
+
toolBox['_draggable'] = {
|
|
298
|
+
onDragStart() {
|
|
299
|
+
return;
|
|
300
|
+
},
|
|
301
|
+
} as unknown as Object3D & DIVEDraggable;
|
|
302
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
303
|
+
|
|
304
|
+
toolBox['_dragRaycastOnObjects'] = [];
|
|
305
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
306
|
+
|
|
307
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce([]);
|
|
308
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
309
|
+
|
|
310
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce([
|
|
311
|
+
{
|
|
312
|
+
distance: 1,
|
|
313
|
+
point: {
|
|
314
|
+
clone() {
|
|
315
|
+
return {
|
|
316
|
+
x: 1,
|
|
317
|
+
y: 1,
|
|
318
|
+
z: 1
|
|
319
|
+
} as unknown as Vector3;
|
|
320
|
+
},
|
|
321
|
+
x: 1,
|
|
322
|
+
y: 1,
|
|
323
|
+
z: 1
|
|
324
|
+
} as unknown as Vector3,
|
|
325
|
+
object: {
|
|
326
|
+
uuid: 'uuid2',
|
|
327
|
+
isHoverable: true,
|
|
328
|
+
onPointerEnter() {
|
|
329
|
+
return;
|
|
330
|
+
},
|
|
331
|
+
} as unknown as Object3D & DIVEHoverable
|
|
332
|
+
}
|
|
333
|
+
]);
|
|
334
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
335
|
+
|
|
336
|
+
toolBox['_draggable'] = {
|
|
337
|
+
onDragStart() {
|
|
338
|
+
return;
|
|
339
|
+
},
|
|
340
|
+
} as unknown as Object3D & DIVEDraggable;
|
|
341
|
+
expect(() => toolBox.onDragStart({} as PointerEvent)).not.toThrow();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should execute onDrag correctly', () => {
|
|
345
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
346
|
+
expect(() => toolBox.onDrag({} as PointerEvent)).not.toThrow();
|
|
347
|
+
|
|
348
|
+
toolBox['_dragRaycastOnObjects'] = [];
|
|
349
|
+
expect(() => toolBox.onDrag({} as PointerEvent)).not.toThrow();
|
|
350
|
+
|
|
351
|
+
toolBox['_draggable'] = {
|
|
352
|
+
onDrag() {
|
|
353
|
+
return;
|
|
354
|
+
},
|
|
355
|
+
} as unknown as Object3D & DIVEDraggable;
|
|
356
|
+
jest.spyOn(toolBox['_raycaster'], 'intersectObjects').mockReturnValueOnce(
|
|
357
|
+
[
|
|
358
|
+
{
|
|
359
|
+
distance: 1,
|
|
360
|
+
point: {
|
|
361
|
+
clone() {
|
|
362
|
+
return {
|
|
363
|
+
x: 1,
|
|
364
|
+
y: 1,
|
|
365
|
+
z: 1
|
|
366
|
+
} as unknown as Vector3;
|
|
367
|
+
},
|
|
368
|
+
x: 1,
|
|
369
|
+
y: 1,
|
|
370
|
+
z: 1
|
|
371
|
+
} as unknown as Vector3,
|
|
372
|
+
object: {
|
|
373
|
+
uuid: 'uuid2',
|
|
374
|
+
isHoverable: true,
|
|
375
|
+
onPointerEnter() {
|
|
376
|
+
return;
|
|
377
|
+
},
|
|
378
|
+
} as unknown as Object3D & DIVEHoverable
|
|
379
|
+
}
|
|
380
|
+
]
|
|
381
|
+
);
|
|
382
|
+
expect(() => toolBox.onDrag({} as PointerEvent)).not.toThrow();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should execute onDragEnd correctly', () => {
|
|
386
|
+
const toolBox = new abstractWrapper(mockScene, mockController);
|
|
387
|
+
expect(() => toolBox.onDragEnd({} as PointerEvent)).not.toThrow();
|
|
388
|
+
});
|
|
389
|
+
});
|
|
@@ -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
|
}
|