@sapui5/sap.ui.vbm 1.103.0 → 1.104.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.
@@ -0,0 +1,972 @@
1
+ /*
2
+ * ! SAP UI development toolkit for HTML5 (SAPUI5) (c) Copyright 2009-2012 SAP AG. All rights reserved
3
+ */
4
+
5
+ // Provides class sap.ui.vbm.adapter3d.DragDropHandler
6
+ sap.ui.define([
7
+ "sap/ui/base/Object",
8
+ "./Utilities",
9
+ "./thirdparty/three",
10
+ "sap/base/Log"
11
+ ], function(BaseObject, Utilities, THREE, Log) {
12
+ "use strict";
13
+
14
+ // Aliases
15
+ var Color = THREE.Color;
16
+ var Vector2 = THREE.Vector2;
17
+ var Vector3 = THREE.Vector3;
18
+ var Matrix4 = THREE.Matrix4;
19
+ var toColor = Utilities.toColor;
20
+ var toBoolean = Utilities.toBoolean;
21
+ var vbToThreeJs = Utilities.vbToThreeJs;
22
+ var threeJsToVb = Utilities.threeJsToVb;
23
+ var thisModule = "sap.ui.vbm.DragDropHandler";
24
+
25
+ // Constants
26
+ var RED = new Color(1,0,0);
27
+ var GREEN = new Color(0,1,0);
28
+ var BLUE = new Color(0,0,1);
29
+ var BLACK = new Color(0,0,0);
30
+ var CYAN = new Color(0,1,1);
31
+
32
+ var X_DIR = new Vector3(1,0,0);
33
+ var Y_DIR = new Vector3(0,1,0);
34
+ var Z_DIR = new Vector3(0,0,1);
35
+
36
+ var CONFIG_SNAP_X_COLOR = "SNAP_X_COLOR";
37
+ var CONFIG_SNAP_Y_COLOR = "SNAP_Y_COLOR";
38
+ var CONFIG_SNAP_Z_COLOR = "SNAP_Z_COLOR";
39
+
40
+ var CONFIG_SNAP_COLOR = "SNAP_COLOR";
41
+ var CONFIG_SNAP_HIGHLIGHT_COLOR = "SNAP_HIGHLIGHT_COLOR";
42
+
43
+ var DIR_LENGTH = 10000.0;
44
+ var COLLISION_DISTANCE = 0.01;
45
+ var AXIS_SNAP_DISTANCE = 0.04;
46
+
47
+ var SNAP_POINTS_COUNT = 8; // max amount of snapping points
48
+
49
+ // To avoid allocation with every function call
50
+ var _p1 = new Vector3();
51
+ var _p2 = new Vector3();
52
+ var _p3 = new Vector3();
53
+ var _p4 = new Vector3();
54
+ var _p5 = new Vector3();
55
+ var _matrix = new Matrix4();
56
+ var _pointer = new Vector2();
57
+ var _box = new THREE.Box3();
58
+ var _ray = new THREE.Ray();
59
+ var _line = new THREE.Line3();
60
+ var _raycaster = new THREE.Raycaster();
61
+ var _quaternion = new THREE.Quaternion();
62
+
63
+ var STATE = {
64
+ PICK: 0,
65
+ DRAG: 1
66
+ };
67
+
68
+ // Viewport event delegate. 'this' object in all methods is DragDropHandler instance
69
+ var viewportEventDelegate = {
70
+ onBeforeRendering: function(event) {
71
+ this._unsubscribe();
72
+ },
73
+ onAfterRendering: function(event) {
74
+ this._subscribe();
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Constructor for a new drag and drop handler.
80
+ *
81
+ * @class
82
+ * Provides a class for handling drag and drop operation.
83
+ *
84
+ * @private
85
+ * @author SAP SE
86
+ * @version 1.104.0
87
+ * @alias sap.ui.vbm.adapter3d.DragDropHandler
88
+ */
89
+ var DragDropHandler = BaseObject.extend("sap.ui.vbm.adapter3d.DragDropHandler", /** @lends sap.ui.vbm.adapter3d.DragDropHandler.prototype */ {
90
+
91
+ constructor: function(adapter) {
92
+ BaseObject.call(this);
93
+
94
+ this._adapter = adapter; // fire submit event
95
+ this._context = adapter._context; // access evaluated data
96
+ this._viewport = adapter._viewport; // events
97
+ this._root = this._viewport._root;
98
+ this._scene = this._viewport._scene;
99
+ this._camera = this._viewport._camera;
100
+ this._cameraControls = this._viewport._cameraController;
101
+
102
+ // current action state
103
+ this._state = STATE.PICK;
104
+
105
+ // indicate whether snapping enabled during drag and drop
106
+ this._snapping = false;
107
+
108
+ // flag indicates movement is done by X, Y, Z axes only i.e always snapping
109
+ this._snapAlways = false;
110
+
111
+ // flag indicates whether movement is locked to axis or not
112
+ this._lockToAxis = false;
113
+
114
+ // snapped axis (Vector3) and Color or null if it's not snapped to axis
115
+ this._snapAxis = null;
116
+
117
+ // set of instances which marked as changeable and which may participate in drag and drop action, updated with every payload
118
+ this._changeables = new Set();
119
+
120
+ // array of changeable 3d objects for hit test, updated with every payload and with every drag and drop action
121
+ this._changeables3d = [];
122
+
123
+ // Instances participating in drag and drop action, updated with every drag and drop action
124
+ this._instances = [];
125
+
126
+ // drag and drop origin instance
127
+ this._origin = null;
128
+
129
+ // hovered instance
130
+ this._hovered = null;
131
+
132
+ // touch point where Drag and Drop started (world CRD)
133
+ this._touch = null;
134
+
135
+ // offset between touch point and touch object origin
136
+ this._offset = new Vector3();
137
+
138
+ // movement plane
139
+ this._plane = new THREE.Plane();
140
+
141
+ // drag and drop origin object's parent world inverse
142
+ this._inverseMatrix = new Matrix4();
143
+
144
+ // evaluated colors for axes
145
+ this._xAxisColor = RED;
146
+ this._yAxisColor = BLUE;
147
+ this._zAxisColor = GREEN;
148
+
149
+ // Snap line object & materials
150
+ this._lineMaterialSolid = new THREE.LineBasicMaterial({
151
+ depthTest: false,
152
+ depthWrite: false,
153
+ color: 0x000000,
154
+ linewidth: 1,
155
+ transparent: true,
156
+ opacity: 0.99
157
+ });
158
+
159
+ this._lineMaterialDashed = new THREE.LineDashedMaterial({
160
+ depthTest: false,
161
+ depthWrite: false,
162
+ color: 0x000000,
163
+ linewidth: 1,
164
+ scale: 1,
165
+ dashSize: 0.1,
166
+ gapSize: 0.06,
167
+ transparent: true,
168
+ opacity: 0.99
169
+ });
170
+
171
+ this._snapLine = new THREE.Line(
172
+ new THREE.BufferGeometry().setFromPoints([new Vector3(), new Vector3()]),
173
+ this._lineMaterialDashed
174
+ );
175
+ // invisible, layer #1 (disable hit test), render order #1000 (always on top)
176
+ this._addToScene(this._snapLine, this._root, false, 1, 1000);
177
+
178
+ // position and scale for snap points to update when necessary (to avoid matrix decomposition)
179
+ this._snapPointsData = [];
180
+
181
+ for (var i = 0; i < SNAP_POINTS_COUNT; ++i) {
182
+ this._snapPointsData.push({
183
+ pos: new Vector3(),
184
+ scale: new Vector3()
185
+ });
186
+ }
187
+
188
+ this._snapPoints = new THREE.InstancedMesh(
189
+ new THREE.SphereGeometry(1, 16, 16),
190
+ new THREE.MeshBasicMaterial({
191
+ depthTest: false,
192
+ depthWrite: false,
193
+ color: 0x00ffff,
194
+ transparent: true,
195
+ opacity: 0.99
196
+ }),
197
+ SNAP_POINTS_COUNT
198
+ );
199
+
200
+ // invisible, layer #1 (disable hit test), render order #1010 (always on top)
201
+ this._addToScene(this._snapPoints, this._scene, false, 1, 1010);
202
+
203
+ this._snapHighlightPoint = new THREE.Mesh(
204
+ new THREE.SphereGeometry(1, 16, 16),
205
+ new THREE.MeshBasicMaterial({
206
+ depthTest: false,
207
+ depthWrite: true,
208
+ color: 0xff0000,
209
+ transparent: true,
210
+ opacity: 0.99
211
+ })
212
+ );
213
+ // invisible, layer #1 (disable hit test), render order #1020 (always on top)
214
+ this._addToScene(this._snapHighlightPoint, this._scene, false, 1, 1020);
215
+
216
+ this._snapBox = new THREE.BoxHelper(undefined, 0x00ffff);
217
+ // invisible, layer #1 (disable hit test)
218
+ this._addToScene(this._snapBox, this._scene, false, 1);
219
+
220
+ this._viewport.addEventDelegate(viewportEventDelegate, this);
221
+ }
222
+ });
223
+
224
+ /**
225
+ * Destroys drag and drop handler object.
226
+ * @public
227
+ */
228
+ DragDropHandler.prototype.destroy = function() {
229
+ this._unsubscribe();
230
+ this._viewport.removeEventDelegate(viewportEventDelegate);
231
+
232
+ this._snapBox.geometry.dispose();
233
+ this._snapBox.material.dispose();
234
+
235
+ this._snapPoints.geometry.dispose();
236
+ this._snapPoints.material.dispose();
237
+
238
+ this._snapHighlightPoint.geometry.dispose();
239
+ this._snapHighlightPoint.material.dispose();
240
+
241
+ this._snapLine.geometry.dispose();
242
+ this._lineMaterialSolid.dispose();
243
+ this._lineMaterialDashed.dispose();
244
+
245
+ this._root.remove(this._snapLine);
246
+ this._scene.remove(this._snapBox, this._snapPoints, this._snapHighlightPoint);
247
+
248
+ // reset all
249
+ this._adapter = null;
250
+ this._context = null;
251
+ this._viewport = null;
252
+ this._root = null;
253
+ this._scene = null;
254
+ this._camera = null;
255
+ this._cameraControls = null;
256
+ this._lineMaterialSolid = null;
257
+ this._lineMaterialDashed = null;
258
+ this._snapBox = null;
259
+ this._snapLine = null;
260
+ this._snapPoints = null;
261
+ this._snapHighlightPoint = null;
262
+
263
+ BaseObject.prototype.destroy.call(this);
264
+ };
265
+
266
+ /**
267
+ * Updates list of changeable instances.
268
+ * @public
269
+ */
270
+ DragDropHandler.prototype.update = function() {
271
+
272
+ this._cancel(); // cancel active drag and drop action if any
273
+
274
+ var scene = this._context.scene;
275
+ var queues = this._context.voQueues;
276
+
277
+ // Visual object instances split by types of changes
278
+ (queues.toAdd.get(scene) || []).forEach(function(instance) {
279
+ if (instance["VB:c"] && (instance.isBox || instance.isCylinder) && toBoolean(instance["VB:c"])) {
280
+ this._changeables.add(instance);
281
+ }
282
+ }, this);
283
+
284
+ (queues.toRemove.get(scene) || []).forEach(function(instance) {
285
+ this._changeables.delete(instance);
286
+ }, this);
287
+
288
+ (queues.toUpdate.get(scene) || []).forEach(function(instance) {
289
+ if (instance["VB:c"] && (instance.isBox || instance.isCylinder) && Utilities.propertyChanged(instance, "VB:c")) {
290
+ if (toBoolean(instance["VB:c"])) {
291
+ this._changeables.add(instance)
292
+ } else {
293
+ this._changeables.delete(instance);
294
+ }
295
+ Utilities.updateProperty(instance, "VB:c");
296
+ }
297
+ }, this);
298
+
299
+ // make array of 3d objects
300
+ this._changeables3d = Array.from(this._changeables, function(instance) {
301
+ return instance.object3D;
302
+ });
303
+
304
+ var cfg = this._context.config;
305
+
306
+ this._xAxisColor = cfg.has(CONFIG_SNAP_X_COLOR) ? toColor(cfg.get(CONFIG_SNAP_X_COLOR)).rgb : RED;
307
+ this._yAxisColor = cfg.has(CONFIG_SNAP_Y_COLOR) ? toColor(cfg.get(CONFIG_SNAP_Y_COLOR)).rgb : BLUE;
308
+ this._zAxisColor = cfg.has(CONFIG_SNAP_Z_COLOR) ? toColor(cfg.get(CONFIG_SNAP_Z_COLOR)).rgb : GREEN;
309
+
310
+ var snapColor = cfg.has(CONFIG_SNAP_COLOR) ? toColor(cfg.get(CONFIG_SNAP_COLOR)).rgb : CYAN;
311
+ var snapHighlightColor = cfg.has(CONFIG_SNAP_HIGHLIGHT_COLOR) ? toColor(cfg.get(CONFIG_SNAP_HIGHLIGHT_COLOR)).rgb : RED;
312
+
313
+ this._snapBox.material.color.copy(snapColor);
314
+ this._snapBox.material.needsUpdate = true;
315
+
316
+ this._snapPoints.material.color.copy(snapColor);
317
+ this._snapPoints.material.needsUpdate = true;
318
+
319
+ this._snapHighlightPoint.material.color.copy(snapHighlightColor);
320
+ this._snapHighlightPoint.material.needsUpdate = true;
321
+ };
322
+
323
+ DragDropHandler.prototype._onKeyUp = function(event) {
324
+ if (!event.repeat) {
325
+ this._handleKeyPress(event, false);
326
+ }
327
+ };
328
+
329
+ DragDropHandler.prototype._onKeyDown = function(event) {
330
+ if (!event.repeat) {
331
+ this._handleKeyPress(event, true);
332
+ }
333
+ };
334
+
335
+ DragDropHandler.prototype._onPointerDown = function(event) {
336
+ this._mouseDown = true;
337
+
338
+ if (this._hovered) {
339
+ var point;
340
+
341
+ if (this._snapHighlightPoint.visible) {
342
+ this._snapping = true; // drag and drop starts from snapping point -> snapping enabled
343
+ point = this._snapHighlightPoint.position;
344
+ } else {
345
+ _raycaster.layers.set(0);
346
+ _raycaster.setFromCamera(this._updatePointer(event, _pointer), this._camera);
347
+ var intersections = _raycaster.intersectObject(this._hovered, false);
348
+
349
+ if (intersections.length > 0) {
350
+ point = intersections[0].point;
351
+ }
352
+ }
353
+
354
+ if (point) {
355
+ this._dom.style.cursor = "move";
356
+ this._dragStart(point);
357
+ }
358
+ }
359
+ };
360
+
361
+ DragDropHandler.prototype._onPointerMove = function(event) {
362
+ // nothing to do as camera is operating
363
+ if (this._mouseDown && this._cameraControls.enabled) {
364
+ this._snapBox.visible = false;
365
+ this._snapLine.visible = false;
366
+ this._snapPoints.visible = false;
367
+ this._snapHighlightPoint.visible = false;
368
+ return;
369
+ }
370
+
371
+ this._updatePointer(event, _pointer);
372
+
373
+ // if drag started
374
+ if (this._state === STATE.DRAG) {
375
+ _raycaster.layers.set(0);
376
+ _raycaster.setFromCamera(_pointer, this._camera);
377
+ if (_raycaster.ray.intersectPlane(this._plane, _p1)) {
378
+ this._drag(_p1.sub(this._offset).applyMatrix4(this._inverseMatrix), _pointer, event.ctrlKey, event.shiftKey);
379
+ }
380
+ }
381
+
382
+ // hover support for PICK state or DRAG with snapping enabled
383
+ if (this._state === STATE.PICK || this._snapping) {
384
+ this._handleHover(_pointer);
385
+ }
386
+
387
+ // handle snapping highlight point only in PICK state
388
+ if (this._state === STATE.PICK && this._snapPoints.visible) {
389
+ _raycaster.layers.set(1);
390
+ var intersections = _raycaster.intersectObject(this._snapPoints, false);
391
+
392
+ if (intersections.length > 0) {
393
+ var instanceId = intersections[0].instanceId;
394
+ this._snapHighlightPoint.scale.copy(this._snapPointsData[instanceId].scale);
395
+ this._snapHighlightPoint.position.copy(this._snapPointsData[instanceId].pos);
396
+ this._snapHighlightPoint.updateMatrix();
397
+ this._snapHighlightPoint.visible = true;
398
+ } else {
399
+ this._snapHighlightPoint.visible = false;
400
+ }
401
+ }
402
+ };
403
+
404
+ DragDropHandler.prototype._onPointerCancel = function(event) {
405
+ this._mouseDown = false;
406
+ this._dom.style.cursor = this._hovered ? "pointer" : "auto";
407
+ this._dragEnd();
408
+ };
409
+
410
+ DragDropHandler.prototype._handleHover = function(pointer) {
411
+ var intersections = [], bbox = false;
412
+
413
+ _raycaster.layers.set(0);
414
+ _raycaster.setFromCamera(pointer, this._camera);
415
+ _raycaster.intersectObjects(this._changeables3d, false, intersections);
416
+
417
+ if (this._hovered !== null) {
418
+ if (!this._hovered.geometry.boundingBox) {
419
+ this._hovered.geometry.computeBoundingBox();
420
+ }
421
+ _box.copy(this._hovered.geometry.boundingBox);
422
+ _box.applyMatrix4(this._hovered.matrixWorld);
423
+
424
+ if (_raycaster.ray.intersectBox(_box, _p1)) {
425
+ bbox = true;
426
+ }
427
+ }
428
+
429
+ if (!bbox) {
430
+ if (intersections.length > 0) {
431
+ var object = intersections[0].object;
432
+
433
+ if (this._hovered !== object && this._hovered !== null) {
434
+ this._hovered = null;
435
+ this._dom.style.cursor = "auto";
436
+ this._hoverOff();
437
+ }
438
+ if (this._hovered !== object) {
439
+ this._hovered = object;
440
+ this._dom.style.cursor = "pointer";
441
+ this._hoverOn(this._hovered);
442
+ }
443
+ } else if (this._hovered !== null) {
444
+ this._hovered = null;
445
+ this._dom.style.cursor = "auto";
446
+ this._hoverOff();
447
+ }
448
+ }
449
+ };
450
+
451
+ DragDropHandler.prototype._handleKeyPress = function(event, down) {
452
+ if (event.keyCode === 16) { // Shift
453
+ this._lockToAxis = down;
454
+ this._updateSnapLine();
455
+ } else if (event.keyCode === 20) { // Caps Lock
456
+ this._snapAlways = event.getModifierState("CapsLock");
457
+ this._updateSnapLine();
458
+ } else if (event.keyCode === 27 && down) { // Esc
459
+ this._cancel();
460
+ }
461
+ };
462
+
463
+ DragDropHandler.prototype._dragStart = function(intersection) {
464
+ this._plane.setFromNormalAndCoplanarPoint(this._camera.getWorldDirection(this._plane.normal), intersection);
465
+ this._inverseMatrix.copy(this._hovered.parent.matrixWorld).invert();
466
+ this._offset.copy(intersection).sub(_p1.setFromMatrixPosition(this._hovered.matrixWorld));
467
+ this._touch = Utilities.threeJsToVb(intersection);
468
+
469
+ this._instances.length = 0;
470
+ // if drag and drop starts from selected instance then all selected and changeable instances participate in drag and drop
471
+ // otherwise only that instance participate in drag and drop
472
+ var hoveredSelected = toBoolean(this._hovered._sapInstance["VB:s"]);
473
+ this._changeables.forEach(function(instance) {
474
+ if (instance === this._hovered._sapInstance || (hoveredSelected && toBoolean(instance["VB:s"]))) {
475
+ this._instances.push(instance);
476
+ instance._last.dragStart = instance.object3D.position.clone();
477
+ // check if instance has Decals on it -> prepare them as well
478
+ var decals = this._adapter._sceneBuilder._instanceDecals.get(instance);
479
+ if (decals) {
480
+ decals.forEach(function(decal) {
481
+ decal._last.dragStart = decal.object3D.position.clone();
482
+ });
483
+ }
484
+ // remove drag and drop participating object from the list of changeable objects for hit test
485
+ this._changeables3d.splice(this._changeables3d.indexOf(instance.object3D), 1);
486
+ }
487
+ }, this);
488
+
489
+ this._origin = this._hovered;
490
+ this._hovered = null;
491
+ this._state = STATE.DRAG;
492
+
493
+ this._snapBox.visible = false;
494
+ this._snapPoints.visible = false;
495
+ this._snapHighlightPoint.visible = false;
496
+
497
+ this._cameraControls.setEnabled(false);
498
+ };
499
+
500
+ DragDropHandler.prototype._drag = function(position, pointer, ctrl, shift) {
501
+ var i, proceed = true, snapped = false, distance, minDistance = Number.MAX_VALUE;
502
+ var delta = position.clone().sub(this._origin._sapInstance._last.dragStart);
503
+
504
+ // if drag with snapping initiated then try to snap to closest snapping point
505
+ _p1.set(pointer.x, pointer.y, 0);
506
+
507
+ if (this._snapping && this._snapPoints.visible) {
508
+ for (i = 0; i < SNAP_POINTS_COUNT; ++i) {
509
+ _p2.copy(this._snapPointsData[i].pos);
510
+ _p3.copy(_p2);
511
+ _p2.project(this._camera);
512
+ _p2.z = 0;
513
+ distance = _p1.distanceTo(_p2);
514
+ if (distance < AXIS_SNAP_DISTANCE && distance < minDistance) {
515
+ snapped = true;
516
+ minDistance = distance;
517
+ threeJsToVb(_p3, _p3);
518
+ threeJsToVb(this._offset, _p4);
519
+ _p5.copy(_p3.sub(this._origin._sapInstance._last.dragStart));
520
+ _p5.sub(_p4);
521
+ }
522
+ }
523
+ }
524
+
525
+ if (snapped) {
526
+ delta.copy(_p5);
527
+ } else {
528
+ var snapAxis = [
529
+ {dir: X_DIR, color: this._xAxisColor},
530
+ {dir: Y_DIR, color: this._yAxisColor},
531
+ {dir: Z_DIR, color: this._zAxisColor}
532
+ ];
533
+
534
+ if (!this._lockToAxis) {
535
+ // calc snapping distance for each axis
536
+ snapAxis.forEach(function(item) {
537
+ item.distance = this._getAxisSnapDistance(this._touch, delta, item.dir);
538
+ }, this);
539
+
540
+ // find min distance
541
+ snapAxis.sort(function(a, b) {
542
+ return a.distance < b.distance ? -1 : 1;
543
+ });
544
+
545
+ // correct delta if snapping threshold reached
546
+ if (snapAxis[0].distance < AXIS_SNAP_DISTANCE || this._snapAlways) {
547
+ this._snapAxis = snapAxis[0];
548
+ } else {
549
+ this._snapAxis = null;
550
+ }
551
+ }
552
+
553
+ this._updateSnapLine(); // update type & color
554
+
555
+ if (this._snapAxis) {
556
+ this._getAxisSnapPosition(this._touch, delta, this._snapAxis.dir, delta);
557
+ }
558
+ }
559
+
560
+ // if collision detection enabled
561
+ if (ctrl) {
562
+ for (i = 0; i < this._instances.length; ++i) {
563
+ var instance = this._instances[i];
564
+ var pos = instance._last.dragStart.clone().add(delta);
565
+ var world = new Matrix4().compose(pos, instance.object3D.quaternion, instance.object3D.scale);
566
+ world.multiplyMatrices(instance.object3D.parent.matrixWorld, world);
567
+
568
+ if (!instance.object3D.geometry.boundingBox) {
569
+ instance.object3D.geometry.computeBoundingBox();
570
+ }
571
+ var box = new THREE.Box3().copy(instance.object3D.geometry.boundingBox);
572
+ box.applyMatrix4(world);
573
+
574
+ if (this._isCollide(this._scene, box)) {
575
+ proceed = false;
576
+ break;
577
+ }
578
+ }
579
+ }
580
+
581
+ if (proceed) {
582
+ this._updateSnapLineGeometry(this._touch, delta);
583
+
584
+ for (i = 0; i < this._instances.length; ++i) {
585
+ this._instances[i].object3D.position.copy(this._instances[i]._last.dragStart).add(delta);
586
+ this._instances[i].object3D.updateMatrix();
587
+
588
+ var decals = this._adapter._sceneBuilder._instanceDecals.get(this._instances[i]);
589
+ if (decals) {
590
+ vbToThreeJs(delta, _p1); // convert delta from VB to ThreeJs as Decals are not in VB CRS
591
+ decals.forEach(function(decal) {
592
+ decal.object3D.position.copy(decal._last.dragStart).add(_p1);
593
+ decal.object3D.updateMatrix();
594
+ });
595
+ }
596
+ }
597
+ // objects have moved -> adjust camera clipping planes
598
+ this._viewport._resetBBox();
599
+ this._viewport._updateCamera();
600
+ }
601
+ this._snapLine.visible = true;
602
+ };
603
+
604
+ DragDropHandler.prototype._dragEnd = function() {
605
+ if (this._state !== STATE.DRAG) {
606
+ return;
607
+ }
608
+
609
+ // generate submit event
610
+ var dataMap = new Map();
611
+
612
+ var payload = {
613
+ version : "2.0",
614
+ "xmlns:VB" : "VB",
615
+ Data: {
616
+ Merge: {
617
+ N: []
618
+ }
619
+ }
620
+ };
621
+
622
+ this._instances.forEach(function(instance) {
623
+ if (instance.id) {
624
+ if (instance.voGroup.isDataBound) {
625
+ var dataType = instance.voGroup.datasource; // VO group data source linked to a DataType by name
626
+ var data = dataMap.get(dataType);
627
+
628
+ if (!data) {
629
+ data = {
630
+ name: dataType,
631
+ E: []
632
+ };
633
+ dataMap.set(dataType, data);
634
+ }
635
+
636
+ var attr = instance.voGroup.template.pos;
637
+ var posAttr = attr.path[attr.path.length - 1]; // last entry on path is attribute name
638
+ var posAlias = this._adapter._parser.getAttributeAlias(dataType, posAttr);
639
+ var keyAlias = this._adapter._parser.getAttributeAlias(dataType, instance.voGroup.keyAttributeName);
640
+ var pos = instance.object3D.position;
641
+ var value = pos.x.toFixed(5) + ";" + pos.y.toFixed(5) + ";" + pos.z.toFixed(5);
642
+
643
+ // make sure position is updated in all 3 places:
644
+ // evaluated instance
645
+ // instance last changed values
646
+ // and in instance's data instance
647
+ instance.dataInstance[posAttr] = instance._last.pos = instance.pos = value;
648
+
649
+ var obj = {};
650
+ obj[keyAlias] = instance.id;
651
+ obj[posAlias] = value;
652
+
653
+ data.E.push(obj);
654
+ } else {
655
+ Log.error("Only data bound instance can be included in Data.Merge section", instance.id, thisModule);
656
+ }
657
+ } else {
658
+ Log.error("Cannot process instance without id", instance, thisModule);
659
+ }
660
+ }, this);
661
+
662
+ // don't fire empty events
663
+ if (dataMap.size > 0) {
664
+ dataMap.forEach(function(item) {
665
+ payload.Data.Merge.N.push(item);
666
+ });
667
+
668
+ this._adapter.fireSubmit({
669
+ data: JSON.stringify(payload)
670
+ });
671
+ }
672
+
673
+ this._state = STATE.PICK;
674
+ this._hovered = null;
675
+ this._origin = null;
676
+ this._touch = null;
677
+ this._snapping = false;
678
+ this._snapLine.visible = false;
679
+ this._instances.length = 0;
680
+ this._changeables3d = Array.from(this._changeables, function(instance) {
681
+ return instance.object3D;
682
+ });
683
+ this._cameraControls.setEnabled(true);
684
+ };
685
+
686
+ DragDropHandler.prototype._hoverOn = function(obj) {
687
+ var that = this;
688
+
689
+ if (!obj.geometry.boundingBox) {
690
+ obj.geometry.computeBoundingBox();
691
+ }
692
+
693
+ _box.copy(obj.geometry.boundingBox);
694
+ _box.applyMatrix4(obj.matrixWorld);
695
+
696
+ function pos(index, x, y, z) {
697
+ that._snapPointsData[index].pos.set(x, y, z);
698
+ }
699
+
700
+ pos(0, _box.min.x, _box.min.y, _box.min.z);
701
+ pos(1, _box.min.x, _box.min.y, _box.max.z);
702
+ pos(2, _box.min.x, _box.max.y, _box.min.z);
703
+ pos(3, _box.min.x, _box.max.y, _box.max.z);
704
+ pos(4, _box.max.x, _box.min.y, _box.min.z);
705
+ pos(5, _box.max.x, _box.min.y, _box.max.z);
706
+ pos(6, _box.max.x, _box.max.y, _box.min.z);
707
+ pos(7, _box.max.x, _box.max.y, _box.max.z);
708
+
709
+ this._snapPoints.visible = true;
710
+ this._updateSnapPoints();
711
+
712
+ this._snapBox.visible = true;
713
+ this._snapBox.setFromObject(obj);
714
+ };
715
+
716
+ DragDropHandler.prototype._hoverOff = function() {
717
+ this._snapBox.visible = false;
718
+ this._snapPoints.visible = false;
719
+ this._snapHighlightPoint.visible = false;
720
+ };
721
+
722
+ DragDropHandler.prototype._isCollide = function(object, box) {
723
+ var i;
724
+ if (object.visible && object.geometry && Utilities.layersEnabled(object.layers, 0) && this._instances.indexOf(object._sapInstance) < 0) {
725
+ object.updateWorldMatrix(false, false);
726
+ if (!object.geometry.boundingBox) {
727
+ object.geometry.computeBoundingBox();
728
+ }
729
+ if (object.isInstancedMesh) {
730
+ for (i = 0; i < object.count; ++i) {
731
+ object.getMatrixAt(i, _matrix);
732
+ _box.copy(object.geometry.boundingBox);
733
+ _box.applyMatrix4(_matrix);
734
+
735
+ if (_box.intersectsBox(box)) {
736
+ return true;
737
+ }
738
+ }
739
+ } else {
740
+ _box.copy(object.geometry.boundingBox);
741
+ _box.applyMatrix4(object.matrixWorld);
742
+
743
+ if (_box.intersectsBox(box)) {
744
+ return true;
745
+ }
746
+ }
747
+ }
748
+
749
+ var children = object.children;
750
+ for (i = 0; i < children.length; ++i) {
751
+ if (this._isCollide(children[i], box)) {
752
+ return true;
753
+ }
754
+ }
755
+ return false;
756
+ };
757
+
758
+ DragDropHandler.prototype._updateSnapLineGeometry = function(from, delta) {
759
+ var pos = this._snapLine.geometry.attributes.position;
760
+ pos.needsUpdate = true;
761
+
762
+ pos.array[0] = from.x;
763
+ pos.array[1] = from.y;
764
+ pos.array[2] = from.z;
765
+ pos.array[3] = from.x + delta.x;
766
+ pos.array[4] = from.y + delta.y;
767
+ pos.array[5] = from.z + delta.z;
768
+
769
+ this._snapLine.geometry.computeBoundingBox();
770
+ this._snapLine.geometry.computeBoundingSphere();
771
+
772
+ if (this._snapLine.material === this._lineMaterialDashed) {
773
+ this._snapLine.computeLineDistances();
774
+ }
775
+ };
776
+
777
+ DragDropHandler.prototype._updateSnapLine = function() {
778
+ this._updateSnapLineColor(this._snapAxis ? this._snapAxis.color : BLACK);
779
+ this._updateSnapLineType((this._snapAxis && this._lockToAxis) || this._snapAlways);
780
+ };
781
+
782
+ DragDropHandler.prototype._updateSnapLineColor = function(color) {
783
+ if (!this._snapLine.material.color.equals(color)) {
784
+ this._snapLine.material.color.copy(color);
785
+ this._snapLine.material.needsUpdate = true;
786
+ }
787
+ };
788
+
789
+ DragDropHandler.prototype._updateSnapLineType = function(solid) {
790
+ var old = this._snapLine.material;
791
+ this._snapLine.material = solid ? this._lineMaterialSolid : this._lineMaterialDashed;
792
+
793
+ if (old !== this._snapLine.material) {
794
+ this._snapLine.material.color.copy(old.color);
795
+ this._snapLine.material.needsUpdate = true;
796
+
797
+ if (this._snapLine.material === this._lineMaterialDashed) {
798
+ this._snapLine.computeLineDistances();
799
+ }
800
+ }
801
+ };
802
+
803
+ DragDropHandler.prototype._getAxisSnapDistance = function(org, delta, dir) {
804
+ vbToThreeJs(org, _p1); // origin of the movement (world CRD)
805
+ vbToThreeJs(_p2.copy(org).add(delta), _p2); // current position of the movement (world CRD)
806
+ _p3.copy(_p1).add(dir); // offset position to form a line in "dir" direction (world CRD)
807
+
808
+ var camera = this._viewport._camera;
809
+
810
+ // to device normalized CRD
811
+ _p1.project(camera);
812
+ _p2.project(camera);
813
+ _p3.project(camera);
814
+
815
+ _p1.z = _p2.z = _p3.z = 0; // reset depth to calculate closest point on a plane not within frustrum
816
+
817
+ _line.start.copy(_p1);
818
+ _line.end.copy(_p3);
819
+ _line.closestPointToPoint(_p2, false, _p1);
820
+
821
+ return _p2.distanceTo(_p1);
822
+ };
823
+
824
+ DragDropHandler.prototype._getAxisSnapPosition = function(org, delta, dir, out) {
825
+ vbToThreeJs(org, _p1); // origin of the movement (world CRD)
826
+ vbToThreeJs(_p2.copy(org).add(delta), _p2); // current position of the movement (world CRD)
827
+ _p3.copy(dir).multiplyScalar(DIR_LENGTH); // magnified direction (world CRD)
828
+ _p4.copy(_p1).sub(_p3); // offset position in -"dir" direction to form a line
829
+ _p5.copy(_p1).add(_p3); // offset position in +"dir" direction to form a line
830
+
831
+ var camera = this._viewport._camera;
832
+
833
+ // to normalized device CRD
834
+ _p2.project(camera);
835
+ _p1.copy(_p4).project(camera);
836
+ _p3.copy(_p5).project(camera);
837
+
838
+ _p1.z = _p2.z = _p3.z = 0; // reset depth to calculate closest point on a plane not within frustrum
839
+
840
+ _line.start.copy(_p1);
841
+ _line.end.copy(_p3);
842
+ _line.closestPointToPoint(_p2, false, _p1);
843
+
844
+ // "project" back to find snapping point in world CRD
845
+ _ray.origin.setFromMatrixPosition(camera.matrixWorld);
846
+ _ray.direction.set(_p1.x, _p1.y, 0.5).unproject(camera).sub(_ray.origin).normalize();
847
+
848
+ if (_ray.distanceSqToSegment(_p4, _p5, _p1, _p2) < COLLISION_DISTANCE) {
849
+ threeJsToVb(_p2, _p2); // back to VB CRD
850
+ out.copy(_p2.sub(org)); // output is delta to final position
851
+ } else {
852
+ Log.error("failed to calculate snapping point", "", thisModule);
853
+ }
854
+ };
855
+
856
+ DragDropHandler.prototype._cancel = function() {
857
+ if (this._touch) {
858
+ // move objects back to original position
859
+ for (var i = 0; i < this._instances.length; ++i) {
860
+ this._instances[i].object3D.position.copy(this._instances[i]._last.dragStart);
861
+ this._instances[i].object3D.updateMatrix();
862
+
863
+ // move Decals back if any
864
+ var decals = this._adapter._sceneBuilder._instanceDecals.get(this._instances[i]);
865
+ if (decals) {
866
+ decals.forEach(function(decal) {
867
+ decal.object3D.position.copy(decal._last.dragStart);
868
+ decal.object3D.updateMatrix();
869
+ });
870
+ }
871
+ }
872
+
873
+ this._touch = null; // reset touch point to indicate there is no active drag and drop
874
+ this._hovered = null; // reset hovered instance
875
+ this._snapLine.visible = false; // hide guide line
876
+ this._instances.length = 0; // clear to indicate current action has been cancelled
877
+ this._cameraControls.setEnabled(true); // enable camera controls
878
+ this._dom.style.cursor = "auto";
879
+
880
+ // objects have moved -> adjust camera clipping planes
881
+ this._viewport._resetBBox();
882
+ this._viewport._updateCamera();
883
+ }
884
+ };
885
+
886
+ DragDropHandler.prototype._addToScene = function(obj, parent, visible, layer, order) {
887
+ if (visible !== undefined) {
888
+ obj.visible = visible;
889
+ }
890
+ if (layer !== undefined) {
891
+ obj.layers.set(layer);
892
+ }
893
+ if (order !== undefined) {
894
+ obj.renderOrder = order;
895
+ }
896
+ parent.add(obj);
897
+ obj.matrixAutoUpdate = false;
898
+ };
899
+
900
+ DragDropHandler.prototype._addListener = function(source, event, handler) {
901
+ var proxy = handler.bind(this);
902
+ this[event + "Proxy"] = proxy;
903
+ source.addEventListener(event, proxy);
904
+ };
905
+
906
+ DragDropHandler.prototype._removeListener = function(source, event) {
907
+ source.removeEventListener(event, this[event + "Proxy"]);
908
+ delete this[event + "Proxy"];
909
+ };
910
+
911
+ DragDropHandler.prototype._subscribe = function() {
912
+ var ref = this._viewport.getDomRef();
913
+ if (ref) {
914
+ this._dom = ref;
915
+ this._addListener(ref, "keyup", this._onKeyUp);
916
+ this._addListener(ref, "keydown", this._onKeyDown);
917
+ this._addListener(ref, "wheel", this._onPointerMove);
918
+ this._addListener(ref, "pointerup", this._onPointerCancel);
919
+ this._addListener(ref, "pointerdown", this._onPointerDown);
920
+ this._addListener(ref, "pointermove", this._onPointerMove);
921
+ this._addListener(ref, "pointerleave", this._onPointerCancel);
922
+ }
923
+ this._addListener(this._cameraControls, "change", this._onCameraChange);
924
+ };
925
+
926
+ DragDropHandler.prototype._unsubscribe = function() {
927
+ var ref = this._viewport.getDomRef();
928
+ if (ref) {
929
+ this._dom = null;
930
+ this._removeListener(ref, "wheel");
931
+ this._removeListener(ref, "keyup");
932
+ this._removeListener(ref, "keydown");
933
+ this._removeListener(ref, "pointerup");
934
+ this._removeListener(ref, "pointerdown");
935
+ this._removeListener(ref, "pointermove");
936
+ this._removeListener(ref, "pointerleave");
937
+ }
938
+ this._removeListener(this._cameraControls, "change");
939
+ };
940
+
941
+ DragDropHandler.prototype._updatePointer = function(event, out) {
942
+ var rect = this._viewport.getDomRef().getBoundingClientRect();
943
+ out.x = (event.cursor ? event.cursor.x : event.pageX - window.pageXOffset - rect.left) / rect.width * 2 - 1;
944
+ out.y = -(event.cursor ? event.cursor.y : event.pageY - window.pageYOffset - rect.top) / rect.height * 2 + 1;
945
+
946
+ return out;
947
+ };
948
+
949
+ DragDropHandler.prototype._updateSnapPoints = function() {
950
+ // calc bbox center of all snapping points
951
+ _p1.copy(this._snapPointsData[0].pos).add(this._snapPointsData[SNAP_POINTS_COUNT-1].pos).multiplyScalar(0.5);
952
+ var factor = _p1.distanceTo(this._camera.position) * Math.min(1.9 * Math.tan(Math.PI * this._camera.fov / 360) / this._camera.zoom, 10);
953
+
954
+ for (var i = 0; i < SNAP_POINTS_COUNT; ++i) {
955
+ this._snapPointsData[i].scale.set(1,1,1).multiplyScalar(factor / 170);
956
+ _matrix.compose(this._snapPointsData[i].pos, _quaternion, this._snapPointsData[i].scale);
957
+ this._snapPoints.setMatrixAt(i, _matrix);
958
+ }
959
+ this._snapPoints.instanceMatrix.needsUpdate = true;
960
+ // update highlight snapping points as well
961
+ this._snapHighlightPoint.scale.copy(this._snapPointsData[0].scale);
962
+ this._snapHighlightPoint.updateMatrix();
963
+ };
964
+
965
+ DragDropHandler.prototype._onCameraChange = function(event) {
966
+ if (this._snapPoints.visible) {
967
+ this._updateSnapPoints();
968
+ }
969
+ };
970
+
971
+ return DragDropHandler;
972
+ });