@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
package/src/com/Communication.ts
CHANGED
|
@@ -7,6 +7,7 @@ import DIVEMediaCreator from "../mediacreator/MediaCreator.ts";
|
|
|
7
7
|
import DIVEOrbitControls from "../controls/OrbitControls.ts";
|
|
8
8
|
import { DIVESelectable } from "../interface/Selectable.ts";
|
|
9
9
|
import DIVESelectTool from "../toolbox/select/SelectTool.ts";
|
|
10
|
+
import type DIVEModel from "../model/Model.ts";
|
|
10
11
|
|
|
11
12
|
type EventListener<Action extends keyof Actions> = (payload: Actions[Action]['PAYLOAD']) => void;
|
|
12
13
|
|
|
@@ -103,6 +104,10 @@ export default class DIVECommunication {
|
|
|
103
104
|
returnValue = this.setBackground(payload as Actions['SET_BACKGROUND']['PAYLOAD']);
|
|
104
105
|
break;
|
|
105
106
|
}
|
|
107
|
+
case 'DROP_IT': {
|
|
108
|
+
returnValue = this.dropIt(payload as Actions['DROP_IT']['PAYLOAD']);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
106
111
|
case 'PLACE_ON_FLOOR': {
|
|
107
112
|
returnValue = this.placeOnFloor(payload as Actions['PLACE_ON_FLOOR']['PAYLOAD']);
|
|
108
113
|
break;
|
|
@@ -205,12 +210,15 @@ export default class DIVECommunication {
|
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
private getObjects(payload: Actions['GET_OBJECTS']['PAYLOAD']): Actions['GET_OBJECTS']['RETURN'] {
|
|
213
|
+
if (payload.ids.length === 0) return [];
|
|
214
|
+
|
|
215
|
+
const objects: COMEntity[] = [];
|
|
208
216
|
this.registered.forEach((object) => {
|
|
209
|
-
if (
|
|
210
|
-
|
|
217
|
+
if (!payload.ids.includes(object.id)) return;
|
|
218
|
+
objects.push(object);
|
|
211
219
|
});
|
|
212
220
|
|
|
213
|
-
return
|
|
221
|
+
return objects;
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
private addObject(payload: Actions['ADD_OBJECT']['PAYLOAD']): Actions['ADD_OBJECT']['RETURN'] {
|
|
@@ -275,6 +283,16 @@ export default class DIVECommunication {
|
|
|
275
283
|
return true;
|
|
276
284
|
}
|
|
277
285
|
|
|
286
|
+
private dropIt(payload: Actions['DROP_IT']['PAYLOAD']): Actions['DROP_IT']['RETURN'] {
|
|
287
|
+
const object = this.registered.get(payload.id);
|
|
288
|
+
if (!object) return false;
|
|
289
|
+
|
|
290
|
+
const model = this.scene.GetSceneObject(object) as DIVEModel;
|
|
291
|
+
model.DropIt();
|
|
292
|
+
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
278
296
|
private placeOnFloor(payload: Actions['PLACE_ON_FLOOR']['PAYLOAD']): Actions['PLACE_ON_FLOOR']['RETURN'] {
|
|
279
297
|
if (!this.registered.get(payload.id)) return false;
|
|
280
298
|
|
|
@@ -289,6 +289,47 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
289
289
|
expect(successSet).toBe(true);
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
+
it('should perform action DROP_IT with existing model', () => {
|
|
293
|
+
const payload = {
|
|
294
|
+
entityType: "model",
|
|
295
|
+
id: "model",
|
|
296
|
+
position: { x: 0, y: 0, z: 0 },
|
|
297
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
298
|
+
scale: { x: 0.01, y: 0.01, z: 0.01 },
|
|
299
|
+
|
|
300
|
+
uri: "https://threejs.org/examples/models/gltf/LittlestTokyo.glb",
|
|
301
|
+
} as COMModel;
|
|
302
|
+
|
|
303
|
+
testCom.PerformAction('ADD_OBJECT', payload);
|
|
304
|
+
|
|
305
|
+
const placeSpy = jest.spyOn(mockScene, 'GetSceneObject').mockReturnValue({
|
|
306
|
+
DropIt: jest.fn(),
|
|
307
|
+
} as unknown as Object3D);
|
|
308
|
+
|
|
309
|
+
const successPlace = testCom.PerformAction('DROP_IT', payload);
|
|
310
|
+
expect(successPlace).toBe(true);
|
|
311
|
+
expect(placeSpy).toHaveBeenCalledTimes(1);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should perform action DROP_IT without existing model', () => {
|
|
315
|
+
const payload = {
|
|
316
|
+
entityType: "model",
|
|
317
|
+
id: "model",
|
|
318
|
+
position: { x: 0, y: 0, z: 0 },
|
|
319
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
320
|
+
scale: { x: 0.01, y: 0.01, z: 0.01 },
|
|
321
|
+
|
|
322
|
+
uri: "https://threejs.org/examples/models/gltf/LittlestTokyo.glb",
|
|
323
|
+
};
|
|
324
|
+
const placeSpy = jest.spyOn(mockScene, 'GetSceneObject').mockReturnValue({
|
|
325
|
+
DropIt: jest.fn(),
|
|
326
|
+
} as unknown as Object3D);
|
|
327
|
+
|
|
328
|
+
const successPlace = testCom.PerformAction('DROP_IT', payload);
|
|
329
|
+
expect(successPlace).toBe(false);
|
|
330
|
+
expect(placeSpy).toHaveBeenCalledTimes(0);
|
|
331
|
+
});
|
|
332
|
+
|
|
292
333
|
it('should perform action PLACE_ON_FLOOR with existing model', () => {
|
|
293
334
|
const payload = {
|
|
294
335
|
entityType: "model",
|
|
@@ -469,13 +510,10 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
469
510
|
} as COMPov;
|
|
470
511
|
testCom.PerformAction('ADD_OBJECT', mock1);
|
|
471
512
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const successWithoutIds = testCom.PerformAction('GET_OBJECTS', { map });
|
|
475
|
-
expect(successWithoutIds).toStrictEqual(map);
|
|
513
|
+
const successWithoutIds = testCom.PerformAction('GET_OBJECTS', { ids: [] });
|
|
514
|
+
expect(Array.from(successWithoutIds.values())).toStrictEqual([]);
|
|
476
515
|
|
|
477
|
-
|
|
478
|
-
const successWithIds = testCom.PerformAction('GET_OBJECTS', { map, ids: ['test1'] });
|
|
516
|
+
const successWithIds = testCom.PerformAction('GET_OBJECTS', { ids: ['test1'] });
|
|
479
517
|
expect(Array.from(successWithIds.values())).toStrictEqual([{ entityType: "pov", id: "test1", position: { x: 0, y: 0, z: 0 }, target: { x: 0, y: 0, z: 0 } }]);
|
|
480
518
|
});
|
|
481
519
|
|
package/src/com/actions/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ 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
19
|
import GET_CAMERA_TRANSFORM from "./camera/getcameratransform.ts";
|
|
20
|
+
import DROP_IT from "./object/model/dropit.ts";
|
|
20
21
|
|
|
21
22
|
export type Actions = {
|
|
22
23
|
GET_ALL_SCENE_DATA: GET_ALL_SCENE_DATA,
|
|
@@ -27,6 +28,7 @@ export type Actions = {
|
|
|
27
28
|
DELETE_OBJECT: DELETE_OBJECT,
|
|
28
29
|
SELECT_OBJECT: SELECT_OBJECT,
|
|
29
30
|
SET_BACKGROUND: SET_BACKGROUND,
|
|
31
|
+
DROP_IT: DROP_IT,
|
|
30
32
|
PLACE_ON_FLOOR: PLACE_ON_FLOOR,
|
|
31
33
|
SET_CAMERA_TRANSFORM: SET_CAMERA_TRANSFORM,
|
|
32
34
|
GET_CAMERA_TRANSFORM: GET_CAMERA_TRANSFORM,
|
package/src/dive.ts
CHANGED
|
@@ -163,6 +163,13 @@ export default class DIVE {
|
|
|
163
163
|
|
|
164
164
|
// whene everything is done, start the renderer
|
|
165
165
|
this.renderer.StartRenderer(this.scene, this.perspectiveCamera);
|
|
166
|
+
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
|
+
(window as any).DIVE = {
|
|
169
|
+
PrintScene: () => {
|
|
170
|
+
console.log(this.scene);
|
|
171
|
+
},
|
|
172
|
+
}
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
// methods
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Euler, Object3D, Vector3 } from "three";
|
|
2
|
+
import { DIVERotateGizmo } from "./rotate/RotateGizmo";
|
|
3
|
+
import { DIVETranslateGizmo } from "./translate/TranslateGizmo";
|
|
4
|
+
import DIVEOrbitControls from "../controls/OrbitControls";
|
|
5
|
+
import { DIVEScaleGizmo } from "./scale/ScaleGizmo";
|
|
6
|
+
import { DIVEGizmoPlane as DIVEGizmoPlane } from "./plane/GizmoPlane";
|
|
7
|
+
import { DIVESelectable } from "../interface/Selectable";
|
|
8
|
+
|
|
9
|
+
export type DIVEGizmoMode = ('translate' | 'rotate' | 'scale');
|
|
10
|
+
|
|
11
|
+
export type DIVEGizmoAxis = 'x' | 'y' | 'z';
|
|
12
|
+
|
|
13
|
+
export class DIVEGizmo extends Object3D {
|
|
14
|
+
private _mode: DIVEGizmoMode;
|
|
15
|
+
public get mode(): DIVEGizmoMode {
|
|
16
|
+
return this._mode;
|
|
17
|
+
}
|
|
18
|
+
public set mode(value: DIVEGizmoMode) {
|
|
19
|
+
this._mode = value;
|
|
20
|
+
this.assemble();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private _gizmoNode: Object3D;
|
|
24
|
+
public get gizmoNode(): Object3D {
|
|
25
|
+
return this._gizmoNode;
|
|
26
|
+
}
|
|
27
|
+
private _translateGizmo: DIVETranslateGizmo;
|
|
28
|
+
private _rotateGizmo: DIVERotateGizmo;
|
|
29
|
+
private _scaleGizmo: DIVEScaleGizmo;
|
|
30
|
+
|
|
31
|
+
private _gizmoPlane: DIVEGizmoPlane;
|
|
32
|
+
public get gizmoPlane(): DIVEGizmoPlane {
|
|
33
|
+
return this._gizmoPlane;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// attachment stuff
|
|
37
|
+
private _object: (Object3D & DIVESelectable) | null;
|
|
38
|
+
public get object(): (Object3D & DIVESelectable) | null {
|
|
39
|
+
return this._object;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
constructor(controller: DIVEOrbitControls) {
|
|
43
|
+
super();
|
|
44
|
+
this.name = "DIVEGizmo";
|
|
45
|
+
|
|
46
|
+
controller.addEventListener('change', () => {
|
|
47
|
+
const size = controller.getDistance() / 2.5;
|
|
48
|
+
this.scale.set(size, size, size);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
this._mode = 'translate';
|
|
52
|
+
|
|
53
|
+
this._gizmoNode = new Object3D();
|
|
54
|
+
this.add(this._gizmoNode);
|
|
55
|
+
|
|
56
|
+
this._translateGizmo = new DIVETranslateGizmo(controller);
|
|
57
|
+
this._rotateGizmo = new DIVERotateGizmo(controller);
|
|
58
|
+
this._scaleGizmo = new DIVEScaleGizmo(controller);
|
|
59
|
+
|
|
60
|
+
this._gizmoPlane = new DIVEGizmoPlane();
|
|
61
|
+
this._gizmoPlane.visible = false;
|
|
62
|
+
|
|
63
|
+
this._object = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public attach(object: (Object3D & DIVESelectable)): this {
|
|
67
|
+
this._object = object;
|
|
68
|
+
this.assemble();
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public detach(): this {
|
|
73
|
+
this._object = null;
|
|
74
|
+
this.assemble();
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public onHover(mode: DIVEGizmoMode, axis: DIVEGizmoAxis, value: boolean): void {
|
|
79
|
+
if (!value) return;
|
|
80
|
+
this._gizmoPlane.assemble(mode, axis);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public onChange(position?: Vector3, rotation?: Euler, scale?: Vector3): void {
|
|
84
|
+
if (this.object === null) return;
|
|
85
|
+
|
|
86
|
+
if (position) {
|
|
87
|
+
this.position.copy(position);
|
|
88
|
+
this.object.position.copy(position);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (rotation) {
|
|
92
|
+
this.object.rotation.copy(rotation);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (scale) {
|
|
96
|
+
this.object.scale.copy(scale);
|
|
97
|
+
this._scaleGizmo.update(scale);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private assemble(): void {
|
|
102
|
+
// clear all children
|
|
103
|
+
this._gizmoNode.clear();
|
|
104
|
+
this._gizmoPlane.clear();
|
|
105
|
+
|
|
106
|
+
// reset all gizmos
|
|
107
|
+
this._translateGizmo.reset();
|
|
108
|
+
this._rotateGizmo.reset();
|
|
109
|
+
this._scaleGizmo.reset();
|
|
110
|
+
|
|
111
|
+
// check for object
|
|
112
|
+
if (this.object === null) return;
|
|
113
|
+
|
|
114
|
+
// add gizmos
|
|
115
|
+
if (this._mode === 'translate') {
|
|
116
|
+
this._gizmoNode.add(this._translateGizmo);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (this._mode === 'rotate') {
|
|
120
|
+
this._gizmoNode.add(this._rotateGizmo);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this._mode === 'scale') {
|
|
124
|
+
this._gizmoNode.add(this._scaleGizmo);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// add plane for raycasting properly while dragging
|
|
128
|
+
this.add(this._gizmoPlane);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Color, ColorRepresentation, CylinderGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3 } from "three";
|
|
2
|
+
import { UI_LAYER_MASK } from "../../constant/VisibilityLayerMask";
|
|
3
|
+
import { DIVEHoverable } from "../../interface/Hoverable";
|
|
4
|
+
import { DIVETranslateGizmo } from "../translate/TranslateGizmo";
|
|
5
|
+
import { DIVEDraggable } from "../../interface/Draggable";
|
|
6
|
+
import { DraggableEvent } from "../../toolbox/BaseTool";
|
|
7
|
+
|
|
8
|
+
export class DIVEAxisHandle extends Object3D implements DIVEHoverable, DIVEDraggable {
|
|
9
|
+
readonly isHoverable: true = true;
|
|
10
|
+
readonly isDraggable: true = true;
|
|
11
|
+
|
|
12
|
+
public parent: DIVETranslateGizmo | null = null;
|
|
13
|
+
|
|
14
|
+
public axis: 'x' | 'y' | 'z';
|
|
15
|
+
|
|
16
|
+
private _color: Color = new Color(0xff00ff);
|
|
17
|
+
private _colorHover: Color;
|
|
18
|
+
private _hovered: boolean;
|
|
19
|
+
private _highlight: boolean;
|
|
20
|
+
public get highlight(): boolean {
|
|
21
|
+
return this._highlight;
|
|
22
|
+
}
|
|
23
|
+
public set highlight(highlight: boolean) {
|
|
24
|
+
this._highlight = highlight;
|
|
25
|
+
this._lineMaterial.color = this._highlight || this._hovered ? this._colorHover : this._color;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private _lineMaterial: MeshBasicMaterial;
|
|
29
|
+
|
|
30
|
+
public get forwardVector(): Vector3 {
|
|
31
|
+
return new Vector3(0, 0, 1).applyQuaternion(this.quaternion).normalize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get rightVector(): Vector3 {
|
|
35
|
+
return new Vector3(1, 0, 0).applyQuaternion(this.quaternion).normalize();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get upVector(): Vector3 {
|
|
39
|
+
return new Vector3(0, 1, 0).applyQuaternion(this.quaternion).normalize();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
constructor(axis: 'x' | 'y' | 'z', length: number, direction: Vector3, color: ColorRepresentation) {
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
this.name = "DIVEAxisHandle";
|
|
46
|
+
this.axis = axis;
|
|
47
|
+
|
|
48
|
+
this._color.set(color);
|
|
49
|
+
this._colorHover = this._color.clone().multiplyScalar(2);
|
|
50
|
+
|
|
51
|
+
this._highlight = false;
|
|
52
|
+
this._hovered = false;
|
|
53
|
+
|
|
54
|
+
// create line
|
|
55
|
+
const lineGeo = new CylinderGeometry(0.01, 0.01, length, 13);
|
|
56
|
+
this._lineMaterial = new MeshBasicMaterial({
|
|
57
|
+
color: color,
|
|
58
|
+
depthTest: false,
|
|
59
|
+
depthWrite: false,
|
|
60
|
+
});
|
|
61
|
+
const lineMesh = new Mesh(lineGeo, this._lineMaterial);
|
|
62
|
+
lineMesh.layers.mask = UI_LAYER_MASK;
|
|
63
|
+
lineMesh.renderOrder = Infinity;
|
|
64
|
+
lineMesh.rotateX(Math.PI / 2);
|
|
65
|
+
lineMesh.translateY(length / 2);
|
|
66
|
+
this.add(lineMesh);
|
|
67
|
+
|
|
68
|
+
// create collider
|
|
69
|
+
const collider = new CylinderGeometry(0.1, 0.1, length, 3);
|
|
70
|
+
const colliderMaterial = new MeshBasicMaterial({
|
|
71
|
+
color: 0xff00ff,
|
|
72
|
+
transparent: true,
|
|
73
|
+
opacity: 0.15,
|
|
74
|
+
depthTest: false,
|
|
75
|
+
depthWrite: false,
|
|
76
|
+
});
|
|
77
|
+
const colliderMesh = new Mesh(collider, colliderMaterial);
|
|
78
|
+
colliderMesh.visible = false;
|
|
79
|
+
colliderMesh.layers.mask = UI_LAYER_MASK;
|
|
80
|
+
colliderMesh.renderOrder = Infinity;
|
|
81
|
+
colliderMesh.rotateX(Math.PI / 2);
|
|
82
|
+
colliderMesh.translateY(length / 2);
|
|
83
|
+
this.add(colliderMesh);
|
|
84
|
+
|
|
85
|
+
this.rotateX(direction.y * -Math.PI / 2);
|
|
86
|
+
this.rotateY(direction.x * Math.PI / 2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public reset(): void {
|
|
90
|
+
this._lineMaterial.color = this._color;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public onPointerEnter(): void {
|
|
94
|
+
this._hovered = true;
|
|
95
|
+
if (this.parent) {
|
|
96
|
+
this.parent.onHandleHover(this, true);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public onPointerLeave(): void {
|
|
101
|
+
this._hovered = false;
|
|
102
|
+
if (this.parent) {
|
|
103
|
+
this.parent.onHandleHover(this, false);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public onDragStart(): void {
|
|
108
|
+
if (this.parent) {
|
|
109
|
+
this.parent.onHandleDragStart(this);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public onDrag(e: DraggableEvent): void {
|
|
114
|
+
if (this.parent) {
|
|
115
|
+
this.parent.onHandleDrag(this, e);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public onDragEnd(): void {
|
|
120
|
+
if (this.parent) {
|
|
121
|
+
this.parent.onHandleDragEnd(this);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Color, ColorRepresentation, Mesh, MeshBasicMaterial, Object3D, TorusGeometry, Vector3 } from "three";
|
|
2
|
+
import { UI_LAYER_MASK } from "../../constant/VisibilityLayerMask";
|
|
3
|
+
import { DIVEHoverable } from "../../interface/Hoverable";
|
|
4
|
+
import { DraggableEvent } from "../../toolbox/BaseTool";
|
|
5
|
+
import { DIVERotateGizmo } from "../rotate/RotateGizmo";
|
|
6
|
+
import { DIVEDraggable } from "../../interface/Draggable";
|
|
7
|
+
|
|
8
|
+
export class DIVERadialHandle extends Object3D implements DIVEHoverable, DIVEDraggable {
|
|
9
|
+
readonly isHoverable: true = true;
|
|
10
|
+
readonly isDraggable: true = true;
|
|
11
|
+
|
|
12
|
+
public parent: DIVERotateGizmo | null = null;
|
|
13
|
+
|
|
14
|
+
public axis: 'x' | 'y' | 'z';
|
|
15
|
+
|
|
16
|
+
private _color: Color = new Color(0xff00ff);
|
|
17
|
+
private _colorHover: Color;
|
|
18
|
+
private _hovered: boolean;
|
|
19
|
+
private _highlight: boolean;
|
|
20
|
+
public get highlight(): boolean {
|
|
21
|
+
return this._highlight;
|
|
22
|
+
}
|
|
23
|
+
public set highlight(highlight: boolean) {
|
|
24
|
+
this._highlight = highlight;
|
|
25
|
+
this._lineMaterial.color = this._highlight || this._hovered ? this._colorHover : this._color;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private _lineMaterial: MeshBasicMaterial;
|
|
29
|
+
|
|
30
|
+
public get forwardVector(): Vector3 {
|
|
31
|
+
return new Vector3(0, 0, 1).applyQuaternion(this.quaternion).normalize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get rightVector(): Vector3 {
|
|
35
|
+
return new Vector3(1, 0, 0).applyQuaternion(this.quaternion).normalize();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get upVector(): Vector3 {
|
|
39
|
+
return new Vector3(0, 1, 0).applyQuaternion(this.quaternion).normalize();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
constructor(axis: 'x' | 'y' | 'z', radius: number, arc: number, direction: Vector3, color: ColorRepresentation) {
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
this.name = "DIVERadialHandle";
|
|
46
|
+
this.axis = axis;
|
|
47
|
+
|
|
48
|
+
this._color.set(color);
|
|
49
|
+
this._colorHover = this._color.clone().multiplyScalar(2);
|
|
50
|
+
this._hovered = false;
|
|
51
|
+
this._highlight = false;
|
|
52
|
+
|
|
53
|
+
// create line
|
|
54
|
+
const lineGeo = new TorusGeometry(radius, 0.01, 13, 48, arc);
|
|
55
|
+
this._lineMaterial = new MeshBasicMaterial({
|
|
56
|
+
color: color,
|
|
57
|
+
depthTest: false,
|
|
58
|
+
depthWrite: false,
|
|
59
|
+
});
|
|
60
|
+
const lineMesh = new Mesh(lineGeo, this._lineMaterial);
|
|
61
|
+
lineMesh.layers.mask = UI_LAYER_MASK;
|
|
62
|
+
lineMesh.renderOrder = Infinity;
|
|
63
|
+
this.add(lineMesh);
|
|
64
|
+
|
|
65
|
+
// create collider
|
|
66
|
+
const collider = new TorusGeometry(radius, 0.1, 3, 48, arc);
|
|
67
|
+
const colliderMaterial = new MeshBasicMaterial({
|
|
68
|
+
color: 0xff00ff,
|
|
69
|
+
transparent: true,
|
|
70
|
+
opacity: 0.15,
|
|
71
|
+
depthTest: false,
|
|
72
|
+
depthWrite: false,
|
|
73
|
+
});
|
|
74
|
+
const colliderMesh = new Mesh(collider, colliderMaterial);
|
|
75
|
+
colliderMesh.visible = false;
|
|
76
|
+
colliderMesh.layers.mask = UI_LAYER_MASK;
|
|
77
|
+
colliderMesh.renderOrder = Infinity;
|
|
78
|
+
|
|
79
|
+
this.add(colliderMesh);
|
|
80
|
+
|
|
81
|
+
this.lookAt(direction);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public reset(): void {
|
|
85
|
+
this._lineMaterial.color = this._color;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public onPointerEnter(): void {
|
|
89
|
+
this._hovered = true;
|
|
90
|
+
if (this.parent) {
|
|
91
|
+
this.parent.onHandleHover(this, true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public onPointerLeave(): void {
|
|
96
|
+
this._hovered = false;
|
|
97
|
+
if (this.parent) {
|
|
98
|
+
this.parent.onHandleHover(this, false);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public onDragStart(): void {
|
|
103
|
+
if (this.parent) {
|
|
104
|
+
this.parent.onHandleDragStart(this);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public onDrag(e: DraggableEvent): void {
|
|
109
|
+
if (this.parent) {
|
|
110
|
+
this.parent.onHandleDrag(this, e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public onDragEnd(): void {
|
|
115
|
+
if (this.parent) {
|
|
116
|
+
this.parent.onHandleDragEnd(this);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { BoxGeometry, Color, ColorRepresentation, CylinderGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3 } from "three";
|
|
2
|
+
import { UI_LAYER_MASK } from "../../constant/VisibilityLayerMask";
|
|
3
|
+
import { DIVEHoverable } from "../../interface/Hoverable";
|
|
4
|
+
import { DIVEScaleGizmo } from "../scale/ScaleGizmo";
|
|
5
|
+
import { DIVEDraggable } from "../../interface/Draggable";
|
|
6
|
+
import { DraggableEvent } from "../../toolbox/BaseTool";
|
|
7
|
+
|
|
8
|
+
export class DIVEScaleHandle extends Object3D implements DIVEHoverable, DIVEDraggable {
|
|
9
|
+
readonly isHoverable: true = true;
|
|
10
|
+
readonly isDraggable: true = true;
|
|
11
|
+
|
|
12
|
+
public parent: DIVEScaleGizmo | null = null;
|
|
13
|
+
|
|
14
|
+
public axis: 'x' | 'y' | 'z';
|
|
15
|
+
|
|
16
|
+
private _color: Color = new Color(0xff00ff);
|
|
17
|
+
private _colorHover: Color;
|
|
18
|
+
private _hovered: boolean;
|
|
19
|
+
private _highlight: boolean;
|
|
20
|
+
public get highlight(): boolean {
|
|
21
|
+
return this._highlight;
|
|
22
|
+
}
|
|
23
|
+
public set highlight(highlight: boolean) {
|
|
24
|
+
this._highlight = highlight;
|
|
25
|
+
this._lineMaterial.color = this._highlight || this._hovered ? this._colorHover : this._color;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private _lineMaterial: MeshBasicMaterial;
|
|
29
|
+
|
|
30
|
+
private _box: Mesh;
|
|
31
|
+
private _boxSize: number;
|
|
32
|
+
|
|
33
|
+
public get forwardVector(): Vector3 {
|
|
34
|
+
return new Vector3(0, 0, 1).applyQuaternion(this.quaternion).normalize();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get rightVector(): Vector3 {
|
|
38
|
+
return new Vector3(1, 0, 0).applyQuaternion(this.quaternion).normalize();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public get upVector(): Vector3 {
|
|
42
|
+
return new Vector3(0, 1, 0).applyQuaternion(this.quaternion).normalize();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor(axis: 'x' | 'y' | 'z', length: number, direction: Vector3, color: ColorRepresentation, boxSize: number = 0.05) {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
this.name = "DIVEScaleHandle";
|
|
49
|
+
this.axis = axis;
|
|
50
|
+
|
|
51
|
+
this._color.set(color);
|
|
52
|
+
this._colorHover = this._color.clone().multiplyScalar(2);
|
|
53
|
+
this._hovered = false;
|
|
54
|
+
this._highlight = false;
|
|
55
|
+
|
|
56
|
+
this._boxSize = boxSize;
|
|
57
|
+
|
|
58
|
+
// create line
|
|
59
|
+
const lineGeo = new CylinderGeometry(0.01, 0.01, length - boxSize / 2, 13);
|
|
60
|
+
this._lineMaterial = new MeshBasicMaterial({
|
|
61
|
+
color: color,
|
|
62
|
+
depthTest: false,
|
|
63
|
+
depthWrite: false,
|
|
64
|
+
});
|
|
65
|
+
const lineMesh = new Mesh(lineGeo, this._lineMaterial);
|
|
66
|
+
lineMesh.layers.mask = UI_LAYER_MASK;
|
|
67
|
+
lineMesh.renderOrder = Infinity;
|
|
68
|
+
lineMesh.rotateX(Math.PI / 2);
|
|
69
|
+
lineMesh.translateY(length / 2 - boxSize / 4);
|
|
70
|
+
this.add(lineMesh);
|
|
71
|
+
|
|
72
|
+
// create box
|
|
73
|
+
this._box = new Mesh(
|
|
74
|
+
new BoxGeometry(boxSize, boxSize, boxSize),
|
|
75
|
+
this._lineMaterial,
|
|
76
|
+
);
|
|
77
|
+
this._box.layers.mask = UI_LAYER_MASK;
|
|
78
|
+
this._box.renderOrder = Infinity;
|
|
79
|
+
this._box.rotateX(Math.PI / 2);
|
|
80
|
+
this._box.translateY(length - boxSize / 2);
|
|
81
|
+
this._box.rotateZ(direction.x * Math.PI / 2);
|
|
82
|
+
this._box.rotateX(direction.z * Math.PI / 2);
|
|
83
|
+
this.add(this._box);
|
|
84
|
+
|
|
85
|
+
// create collider
|
|
86
|
+
const collider = new CylinderGeometry(0.1, 0.1, length + boxSize / 2, 3);
|
|
87
|
+
const colliderMaterial = new MeshBasicMaterial({
|
|
88
|
+
color: 0xff00ff,
|
|
89
|
+
transparent: true,
|
|
90
|
+
opacity: 0.15,
|
|
91
|
+
depthTest: false,
|
|
92
|
+
depthWrite: false,
|
|
93
|
+
});
|
|
94
|
+
const colliderMesh = new Mesh(collider, colliderMaterial);
|
|
95
|
+
colliderMesh.visible = false;
|
|
96
|
+
colliderMesh.layers.mask = UI_LAYER_MASK;
|
|
97
|
+
colliderMesh.renderOrder = Infinity;
|
|
98
|
+
colliderMesh.rotateX(Math.PI / 2);
|
|
99
|
+
colliderMesh.translateY(length / 2);
|
|
100
|
+
this.add(colliderMesh);
|
|
101
|
+
|
|
102
|
+
this.rotateX(direction.y * -Math.PI / 2);
|
|
103
|
+
this.rotateY(direction.x * Math.PI / 2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public reset(): void {
|
|
107
|
+
this._lineMaterial.color = this._color;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public update(scale: Vector3): void {
|
|
111
|
+
this._box.scale.copy(
|
|
112
|
+
new Vector3(1, 1, 1) // identity scale ...
|
|
113
|
+
.sub(this.forwardVector) // subtracted the forward vector ...
|
|
114
|
+
.add( // to then add ...
|
|
115
|
+
scale.clone() // the scale ...
|
|
116
|
+
.multiply(this.forwardVector) // that is scaled by the forward vector again to get the forward vector as the only direction
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public onPointerEnter(): void {
|
|
122
|
+
this._hovered = true;
|
|
123
|
+
if (this.parent) {
|
|
124
|
+
this.parent.onHoverAxis(this, true);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public onPointerLeave(): void {
|
|
129
|
+
this._hovered = false;
|
|
130
|
+
if (this.parent) {
|
|
131
|
+
this.parent.onHoverAxis(this, false);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public onDragStart(): void {
|
|
136
|
+
if (this.parent) {
|
|
137
|
+
this.parent.onAxisDragStart(this);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public onDrag(e: DraggableEvent): void {
|
|
142
|
+
if (this.parent) {
|
|
143
|
+
this.parent.onAxisDrag(this, e);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public onDragEnd(): void {
|
|
148
|
+
if (this.parent) {
|
|
149
|
+
this.parent.onAxisDragEnd(this);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|