@xeokit/xeokit-sdk 2.6.20 → 2.6.23
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/dist/xeokit-sdk.cjs.js +419 -167
- package/dist/xeokit-sdk.es.js +416 -168
- package/dist/xeokit-sdk.es5.js +743 -721
- package/dist/xeokit-sdk.min.cjs.js +5 -5
- package/dist/xeokit-sdk.min.es.js +5 -5
- package/dist/xeokit-sdk.min.es5.js +4 -4
- package/package.json +1 -1
- package/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js +2 -2
- package/src/plugins/AngleMeasurementsPlugin/index.js +59 -1
- package/src/plugins/DistanceMeasurementsPlugin/index.js +59 -1
- package/src/plugins/ZonesPlugin/index.js +13 -160
- package/src/plugins/lib/ui/index.js +264 -0
- package/src/viewer/metadata/MetaModel.js +27 -4
- package/src/viewer/metadata/PropertySet.js +5 -1
package/package.json
CHANGED
|
@@ -103,10 +103,10 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
_destroyMarkerDiv() {
|
|
106
|
-
if (this.
|
|
106
|
+
if (this.markerDiv) {
|
|
107
107
|
const element = document.getElementById('myMarkerDiv')
|
|
108
108
|
element.parentNode.removeChild(element)
|
|
109
|
-
this.
|
|
109
|
+
this.markerDiv = null
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -1,4 +1,62 @@
|
|
|
1
1
|
export * from "./AngleMeasurementsPlugin.js";
|
|
2
2
|
export * from "./AngleMeasurementsControl.js";
|
|
3
3
|
export * from "./AngleMeasurementsMouseControl.js";
|
|
4
|
-
export * from "./AngleMeasurementsTouchControl.js";
|
|
4
|
+
export * from "./AngleMeasurementsTouchControl.js";
|
|
5
|
+
|
|
6
|
+
import {Component} from "../../viewer/scene/Component.js";
|
|
7
|
+
import {activateDraggableDots} from "../../../src/plugins/lib/ui/index.js";
|
|
8
|
+
|
|
9
|
+
class AngleMeasurementEditControl extends Component {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Edits {@link AngleMeasurement} with mouse and/or touch input.
|
|
13
|
+
*
|
|
14
|
+
* @param {AngleMeasurement} [measurement] The AngleMeasurement to edit.
|
|
15
|
+
* @param [cfg] Configuration
|
|
16
|
+
* @param {PointerLens} [cfg.pointerLens] A PointerLens to use to provide a magnified view of the cursor.
|
|
17
|
+
* @param {boolean} [cfg.snapping] Whether to enable snap-to-vertex and snap-to-edge.
|
|
18
|
+
* @param {boolean} [handleMouseEvents] Whether to hangle mouse input.
|
|
19
|
+
* @param {boolean} [handleTouchEvents] Whether to hangle touch input.
|
|
20
|
+
*/
|
|
21
|
+
constructor(measurement, cfg, handleMouseEvents, handleTouchEvents) {
|
|
22
|
+
|
|
23
|
+
const viewer = measurement.plugin.viewer;
|
|
24
|
+
|
|
25
|
+
super(viewer.scene);
|
|
26
|
+
|
|
27
|
+
const cleanup = activateDraggableDots({
|
|
28
|
+
viewer: viewer,
|
|
29
|
+
handleMouseEvents: handleMouseEvents,
|
|
30
|
+
handleTouchEvents: handleTouchEvents,
|
|
31
|
+
snapping: cfg.snapping,
|
|
32
|
+
pointerLens: cfg.pointerLens,
|
|
33
|
+
color: measurement.color,
|
|
34
|
+
markers: [ measurement.origin, measurement.corner, measurement.target ],
|
|
35
|
+
onEdit: () => this.fire("edited")
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const destroyCb = measurement.on("destroyed", cleanup);
|
|
39
|
+
|
|
40
|
+
this._deactivate = function() {
|
|
41
|
+
measurement.off("destroyed", destroyCb);
|
|
42
|
+
cleanup();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
deactivate() {
|
|
47
|
+
this._deactivate();
|
|
48
|
+
super.destroy();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class AngleMeasurementEditMouseControl extends AngleMeasurementEditControl {
|
|
53
|
+
constructor(zone, cfg) {
|
|
54
|
+
super(zone, cfg, true, false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class AngleMeasurementEditTouchControl extends AngleMeasurementEditControl {
|
|
59
|
+
constructor(zone, cfg) {
|
|
60
|
+
super(zone, cfg, false, true);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,4 +1,62 @@
|
|
|
1
1
|
export * from "./DistanceMeasurementsPlugin.js";
|
|
2
2
|
export * from "./DistanceMeasurementsControl.js";
|
|
3
3
|
export * from "./DistanceMeasurementsMouseControl.js";
|
|
4
|
-
export * from "./DistanceMeasurementsTouchControl.js";
|
|
4
|
+
export * from "./DistanceMeasurementsTouchControl.js";
|
|
5
|
+
|
|
6
|
+
import {Component} from "../../viewer/scene/Component.js";
|
|
7
|
+
import {activateDraggableDots} from "../../../src/plugins/lib/ui/index.js";
|
|
8
|
+
|
|
9
|
+
class DistanceMeasurementEditControl extends Component {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Edits {@link DistanceMeasurement} with mouse and/or touch input.
|
|
13
|
+
*
|
|
14
|
+
* @param {DistanceMeasurement} [measurement] The DistanceMeasurement to edit.
|
|
15
|
+
* @param [cfg] Configuration
|
|
16
|
+
* @param {PointerLens} [cfg.pointerLens] A PointerLens to use to provide a magnified view of the cursor.
|
|
17
|
+
* @param {boolean} [cfg.snapping] Whether to enable snap-to-vertex and snap-to-edge.
|
|
18
|
+
* @param {boolean} [handleMouseEvents] Whether to hangle mouse input.
|
|
19
|
+
* @param {boolean} [handleTouchEvents] Whether to hangle touch input.
|
|
20
|
+
*/
|
|
21
|
+
constructor(measurement, cfg, handleMouseEvents, handleTouchEvents) {
|
|
22
|
+
|
|
23
|
+
const viewer = measurement.plugin.viewer;
|
|
24
|
+
|
|
25
|
+
super(viewer.scene);
|
|
26
|
+
|
|
27
|
+
const cleanup = activateDraggableDots({
|
|
28
|
+
viewer: viewer,
|
|
29
|
+
handleMouseEvents: handleMouseEvents,
|
|
30
|
+
handleTouchEvents: handleTouchEvents,
|
|
31
|
+
snapping: cfg.snapping,
|
|
32
|
+
pointerLens: cfg.pointerLens,
|
|
33
|
+
color: measurement.color,
|
|
34
|
+
markers: [ measurement.origin, measurement.target ],
|
|
35
|
+
onEdit: () => this.fire("edited")
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const destroyCb = measurement.on("destroyed", cleanup);
|
|
39
|
+
|
|
40
|
+
this._deactivate = function() {
|
|
41
|
+
measurement.off("destroyed", destroyCb);
|
|
42
|
+
cleanup();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
deactivate() {
|
|
47
|
+
this._deactivate();
|
|
48
|
+
super.destroy();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class DistanceMeasurementEditMouseControl extends DistanceMeasurementEditControl {
|
|
53
|
+
constructor(zone, cfg) {
|
|
54
|
+
super(zone, cfg, true, false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class DistanceMeasurementEditTouchControl extends DistanceMeasurementEditControl {
|
|
59
|
+
constructor(zone, cfg) {
|
|
60
|
+
super(zone, cfg, false, true);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -7,19 +7,13 @@ import {PhongMaterial} from "../../viewer/scene/materials/PhongMaterial.js";
|
|
|
7
7
|
import {math} from "../../viewer/scene/math/math.js";
|
|
8
8
|
import {Mesh} from "../../viewer/scene/mesh/Mesh.js";
|
|
9
9
|
import {Dot} from "../lib/html/Dot.js";
|
|
10
|
+
import {createDraggableDot3D, transformToNode} from "../../../src/plugins/lib/ui/index.js";
|
|
10
11
|
|
|
11
12
|
const hex2rgb = function(color) {
|
|
12
13
|
const rgb = idx => parseInt(color.substr(idx + 1, 2), 16) / 255;
|
|
13
14
|
return [ rgb(0), rgb(2), rgb(4) ];
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
const transformToNode = function(from, to, vec) {
|
|
17
|
-
const fromRec = from.getBoundingClientRect();
|
|
18
|
-
const toRec = to.getBoundingClientRect();
|
|
19
|
-
vec[0] += fromRec.left - toRec.left;
|
|
20
|
-
vec[1] += fromRec.top - toRec.top;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
17
|
const triangulateEarClipping = function(planeCoords) {
|
|
24
18
|
|
|
25
19
|
const polygonVertices = [ ];
|
|
@@ -123,148 +117,6 @@ const triangulateEarClipping = function(planeCoords) {
|
|
|
123
117
|
return [ planeCoords, baseTriangles ];
|
|
124
118
|
};
|
|
125
119
|
|
|
126
|
-
const draggableDot3D = function(handleMouseEvents, handleTouchEvents, viewer, worldPos, color, ray2WorldPos, onStart, onMove, onEnd) {
|
|
127
|
-
const scene = viewer.scene;
|
|
128
|
-
const canvas = scene.canvas.canvas;
|
|
129
|
-
|
|
130
|
-
const marker = new Marker(scene, {});
|
|
131
|
-
|
|
132
|
-
const pickWorldPos = canvasPos => {
|
|
133
|
-
const origin = math.vec3();
|
|
134
|
-
const direction = math.vec3();
|
|
135
|
-
math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
|
|
136
|
-
return ray2WorldPos(origin, direction);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const onChange = event => {
|
|
140
|
-
const canvasPos = math.vec2([ event.clientX, event.clientY ]);
|
|
141
|
-
transformToNode(canvas.ownerDocument.body, canvas, canvasPos);
|
|
142
|
-
|
|
143
|
-
const worldPos = pickWorldPos(canvasPos);
|
|
144
|
-
marker.worldPos = worldPos;
|
|
145
|
-
updateDotPos();
|
|
146
|
-
onMove(canvasPos, worldPos);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
let currentDrag = null;
|
|
150
|
-
|
|
151
|
-
const onDragMove = function(event) {
|
|
152
|
-
const e = currentDrag.matchesEvent(event);
|
|
153
|
-
if (e)
|
|
154
|
-
{
|
|
155
|
-
onChange(e);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const onDragEnd = function(event) {
|
|
160
|
-
const e = currentDrag.matchesEvent(event);
|
|
161
|
-
if (e)
|
|
162
|
-
{
|
|
163
|
-
dot.setOpacity(idleOpacity);
|
|
164
|
-
currentDrag.cleanup();
|
|
165
|
-
onChange(e);
|
|
166
|
-
onEnd();
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const startDrag = function(matchesEvent, cleanupHandlers) {
|
|
171
|
-
if (currentDrag) {
|
|
172
|
-
currentDrag.cleanup();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
dot.setOpacity(1.0);
|
|
176
|
-
dot.setClickable(false);
|
|
177
|
-
viewer.cameraControl.active = false;
|
|
178
|
-
|
|
179
|
-
currentDrag = {
|
|
180
|
-
matchesEvent: matchesEvent,
|
|
181
|
-
cleanup: function() {
|
|
182
|
-
currentDrag = null;
|
|
183
|
-
dot.setClickable(true);
|
|
184
|
-
viewer.cameraControl.active = true;
|
|
185
|
-
cleanupHandlers();
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
onStart();
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const dotCfg = { fillColor: color };
|
|
193
|
-
|
|
194
|
-
if (handleMouseEvents)
|
|
195
|
-
{
|
|
196
|
-
dotCfg.onMouseOver = () => (! currentDrag) && dot.setOpacity(1.0);
|
|
197
|
-
dotCfg.onMouseLeave = () => (! currentDrag) && dot.setOpacity(idleOpacity);
|
|
198
|
-
dotCfg.onMouseDown = event => {
|
|
199
|
-
if (event.which === 1)
|
|
200
|
-
{
|
|
201
|
-
canvas.addEventListener("mousemove", onDragMove);
|
|
202
|
-
canvas.addEventListener("mouseup", onDragEnd);
|
|
203
|
-
startDrag(
|
|
204
|
-
event => (event.which === 1) && event,
|
|
205
|
-
() => {
|
|
206
|
-
canvas.removeEventListener("mousemove", onDragMove);
|
|
207
|
-
canvas.removeEventListener("mouseup", onDragEnd);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (handleTouchEvents)
|
|
214
|
-
{
|
|
215
|
-
let touchStartId;
|
|
216
|
-
dotCfg.onTouchstart = event => {
|
|
217
|
-
event.preventDefault();
|
|
218
|
-
if (event.touches.length === 1)
|
|
219
|
-
{
|
|
220
|
-
touchStartId = event.touches[0].identifier;
|
|
221
|
-
startDrag(
|
|
222
|
-
event => [...event.changedTouches].find(e => e.identifier === touchStartId),
|
|
223
|
-
() => { touchStartId = null; });
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
dotCfg.onTouchmove = event => {
|
|
227
|
-
event.preventDefault();
|
|
228
|
-
onDragMove(event);
|
|
229
|
-
};
|
|
230
|
-
dotCfg.onTouchend = event => {
|
|
231
|
-
event.preventDefault();
|
|
232
|
-
onDragEnd(event);
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const dotParent = canvas.ownerDocument.body;
|
|
237
|
-
const dot = new Dot(dotParent, dotCfg);
|
|
238
|
-
|
|
239
|
-
const idleOpacity = 0.5;
|
|
240
|
-
dot.setOpacity(idleOpacity);
|
|
241
|
-
|
|
242
|
-
const updateDotPos = function() {
|
|
243
|
-
const pos = marker.canvasPos.slice();
|
|
244
|
-
transformToNode(canvas, dotParent, pos);
|
|
245
|
-
dot.setPos(pos[0], pos[1]);
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
marker.worldPos = worldPos;
|
|
249
|
-
updateDotPos();
|
|
250
|
-
|
|
251
|
-
const onViewMatrix = scene.camera.on("viewMatrix", updateDotPos);
|
|
252
|
-
const onProjMatrix = scene.camera.on("projMatrix", updateDotPos);
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
setActive: value => dot.setClickable(value),
|
|
256
|
-
getWorldPos: () => marker.worldPos,
|
|
257
|
-
setWorldPos: pos => { marker.worldPos = pos; updateDotPos(); },
|
|
258
|
-
destroy: function() {
|
|
259
|
-
currentDrag && currentDrag.cleanup();
|
|
260
|
-
scene.camera.off(onViewMatrix);
|
|
261
|
-
scene.camera.off(onProjMatrix);
|
|
262
|
-
marker.destroy();
|
|
263
|
-
dot.destroy();
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
};
|
|
267
|
-
|
|
268
120
|
const marker3D = function(scene, color) {
|
|
269
121
|
const canvas = scene.canvas.canvas;
|
|
270
122
|
|
|
@@ -1790,23 +1642,23 @@ export class ZoneEditControl extends Component {
|
|
|
1790
1642
|
}
|
|
1791
1643
|
};
|
|
1792
1644
|
|
|
1793
|
-
const dot =
|
|
1794
|
-
handleMouseEvents,
|
|
1795
|
-
handleTouchEvents,
|
|
1796
|
-
zone.plugin.viewer,
|
|
1797
|
-
math.vec3([ planeCoord[0], altitude, planeCoord[1] ]),
|
|
1798
|
-
zone._color,
|
|
1799
|
-
(orig, dir) => planeIntersect(altitude, math.vec3([ 0, 1, 0 ]), orig, dir),
|
|
1800
|
-
() => {
|
|
1645
|
+
const dot = createDraggableDot3D({
|
|
1646
|
+
handleMouseEvents: handleMouseEvents,
|
|
1647
|
+
handleTouchEvents: handleTouchEvents,
|
|
1648
|
+
viewer: zone.plugin.viewer,
|
|
1649
|
+
worldPos: math.vec3([ planeCoord[0], altitude, planeCoord[1] ]),
|
|
1650
|
+
color: zone._color,
|
|
1651
|
+
ray2WorldPos: (orig, dir) => planeIntersect(altitude, math.vec3([ 0, 1, 0 ]), orig, dir),
|
|
1652
|
+
onStart: () => {
|
|
1801
1653
|
initWorldPos = dot.getWorldPos().slice();
|
|
1802
1654
|
initPlaneCoord = planeCoord.slice();
|
|
1803
1655
|
set_other_dots_active(false, dot);
|
|
1804
1656
|
},
|
|
1805
|
-
(canvasPos, worldPos) => {
|
|
1657
|
+
onMove: (canvasPos, worldPos) => {
|
|
1806
1658
|
updatePointerLens(canvasPos);
|
|
1807
1659
|
setPlaneCoord([ worldPos[0], worldPos[2] ]);
|
|
1808
1660
|
},
|
|
1809
|
-
() => {
|
|
1661
|
+
onEnd: () => {
|
|
1810
1662
|
if (zone._zoneMesh)
|
|
1811
1663
|
{
|
|
1812
1664
|
self.fire("edited");
|
|
@@ -1818,7 +1670,8 @@ export class ZoneEditControl extends Component {
|
|
|
1818
1670
|
}
|
|
1819
1671
|
updatePointerLens(null);
|
|
1820
1672
|
set_other_dots_active(true, dot);
|
|
1821
|
-
}
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1822
1675
|
return dot;
|
|
1823
1676
|
});
|
|
1824
1677
|
const set_other_dots_active = (active, dot) => dots.forEach(d => (d !== dot) && d.setActive(active));
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import {Dot} from "../html/Dot.js";
|
|
2
|
+
import {math} from "../../../viewer/scene/math/math.js";
|
|
3
|
+
import {Marker} from "../../../viewer/scene/marker/Marker.js";
|
|
4
|
+
|
|
5
|
+
const nop = () => { };
|
|
6
|
+
|
|
7
|
+
export function transformToNode(from, to, vec) {
|
|
8
|
+
const fromRec = from.getBoundingClientRect();
|
|
9
|
+
const toRec = to.getBoundingClientRect();
|
|
10
|
+
vec[0] += fromRec.left - toRec.left;
|
|
11
|
+
vec[1] += fromRec.top - toRec.top;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function createDraggableDot3D(cfg) {
|
|
15
|
+
const extractCFG = function(propName, defaultValue) {
|
|
16
|
+
if (propName in cfg) {
|
|
17
|
+
return cfg[propName];
|
|
18
|
+
} else if (defaultValue !== undefined) {
|
|
19
|
+
return defaultValue;
|
|
20
|
+
} else {
|
|
21
|
+
throw "config missing: " + propName;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const viewer = extractCFG("viewer");
|
|
26
|
+
const worldPos = extractCFG("worldPos");
|
|
27
|
+
const color = extractCFG("color");
|
|
28
|
+
const ray2WorldPos = extractCFG("ray2WorldPos");
|
|
29
|
+
const handleMouseEvents = extractCFG("handleMouseEvents", false);
|
|
30
|
+
const handleTouchEvents = extractCFG("handleTouchEvents", false);
|
|
31
|
+
const onStart = extractCFG("onStart", nop);
|
|
32
|
+
const onMove = extractCFG("onMove", nop);
|
|
33
|
+
const onEnd = extractCFG("onEnd", nop);
|
|
34
|
+
|
|
35
|
+
const scene = viewer.scene;
|
|
36
|
+
const canvas = scene.canvas.canvas;
|
|
37
|
+
|
|
38
|
+
const marker = new Marker(scene, {});
|
|
39
|
+
|
|
40
|
+
const pickWorldPos = canvasPos => {
|
|
41
|
+
const origin = math.vec3();
|
|
42
|
+
const direction = math.vec3();
|
|
43
|
+
math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
|
|
44
|
+
return ray2WorldPos(origin, direction, canvasPos);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const onChange = event => {
|
|
48
|
+
const canvasPos = math.vec2([ event.clientX, event.clientY ]);
|
|
49
|
+
transformToNode(canvas.ownerDocument.body, canvas, canvasPos);
|
|
50
|
+
|
|
51
|
+
const worldPos = pickWorldPos(canvasPos);
|
|
52
|
+
marker.worldPos = worldPos;
|
|
53
|
+
updateDotPos();
|
|
54
|
+
onMove(canvasPos, worldPos);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let currentDrag = null;
|
|
58
|
+
|
|
59
|
+
const onDragMove = function(event) {
|
|
60
|
+
const e = currentDrag.matchesEvent(event);
|
|
61
|
+
if (e)
|
|
62
|
+
{
|
|
63
|
+
onChange(e);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const onDragEnd = function(event) {
|
|
68
|
+
const e = currentDrag.matchesEvent(event);
|
|
69
|
+
if (e)
|
|
70
|
+
{
|
|
71
|
+
dot.setOpacity(idleOpacity);
|
|
72
|
+
currentDrag.cleanup();
|
|
73
|
+
onChange(e);
|
|
74
|
+
onEnd();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const startDrag = function(matchesEvent, cleanupHandlers) {
|
|
79
|
+
if (currentDrag) {
|
|
80
|
+
currentDrag.cleanup();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
dot.setOpacity(1.0);
|
|
84
|
+
dot.setClickable(false);
|
|
85
|
+
viewer.cameraControl.active = false;
|
|
86
|
+
|
|
87
|
+
currentDrag = {
|
|
88
|
+
matchesEvent: matchesEvent,
|
|
89
|
+
cleanup: function() {
|
|
90
|
+
currentDrag = null;
|
|
91
|
+
dot.setClickable(true);
|
|
92
|
+
viewer.cameraControl.active = true;
|
|
93
|
+
cleanupHandlers();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
onStart();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const dotCfg = { fillColor: color };
|
|
101
|
+
|
|
102
|
+
if (handleMouseEvents)
|
|
103
|
+
{
|
|
104
|
+
dotCfg.onMouseOver = () => (! currentDrag) && dot.setOpacity(1.0);
|
|
105
|
+
dotCfg.onMouseLeave = () => (! currentDrag) && dot.setOpacity(idleOpacity);
|
|
106
|
+
dotCfg.onMouseDown = event => {
|
|
107
|
+
if (event.which === 1)
|
|
108
|
+
{
|
|
109
|
+
canvas.addEventListener("mousemove", onDragMove);
|
|
110
|
+
canvas.addEventListener("mouseup", onDragEnd);
|
|
111
|
+
startDrag(
|
|
112
|
+
event => (event.which === 1) && event,
|
|
113
|
+
() => {
|
|
114
|
+
canvas.removeEventListener("mousemove", onDragMove);
|
|
115
|
+
canvas.removeEventListener("mouseup", onDragEnd);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (handleTouchEvents)
|
|
122
|
+
{
|
|
123
|
+
let touchStartId;
|
|
124
|
+
dotCfg.onTouchstart = event => {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
if (event.touches.length === 1)
|
|
127
|
+
{
|
|
128
|
+
touchStartId = event.touches[0].identifier;
|
|
129
|
+
startDrag(
|
|
130
|
+
event => [...event.changedTouches].find(e => e.identifier === touchStartId),
|
|
131
|
+
() => { touchStartId = null; });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
dotCfg.onTouchmove = event => {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
onDragMove(event);
|
|
137
|
+
};
|
|
138
|
+
dotCfg.onTouchend = event => {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
onDragEnd(event);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const dotParent = canvas.ownerDocument.body;
|
|
145
|
+
const dot = new Dot(dotParent, dotCfg);
|
|
146
|
+
|
|
147
|
+
const idleOpacity = 0.5;
|
|
148
|
+
dot.setOpacity(idleOpacity);
|
|
149
|
+
|
|
150
|
+
const updateDotPos = function() {
|
|
151
|
+
const pos = marker.canvasPos.slice();
|
|
152
|
+
transformToNode(canvas, dotParent, pos);
|
|
153
|
+
dot.setPos(pos[0], pos[1]);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
marker.worldPos = worldPos;
|
|
157
|
+
updateDotPos();
|
|
158
|
+
|
|
159
|
+
const onViewMatrix = scene.camera.on("viewMatrix", updateDotPos);
|
|
160
|
+
const onProjMatrix = scene.camera.on("projMatrix", updateDotPos);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
setActive: value => dot.setClickable(value),
|
|
164
|
+
getWorldPos: () => marker.worldPos,
|
|
165
|
+
setWorldPos: pos => { marker.worldPos = pos; updateDotPos(); },
|
|
166
|
+
destroy: function() {
|
|
167
|
+
currentDrag && currentDrag.cleanup();
|
|
168
|
+
scene.camera.off(onViewMatrix);
|
|
169
|
+
scene.camera.off(onProjMatrix);
|
|
170
|
+
marker.destroy();
|
|
171
|
+
dot.destroy();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export function activateDraggableDots(cfg) {
|
|
177
|
+
const extractCFG = function(propName, defaultValue) {
|
|
178
|
+
if (propName in cfg) {
|
|
179
|
+
return cfg[propName];
|
|
180
|
+
} else if (defaultValue !== undefined) {
|
|
181
|
+
return defaultValue;
|
|
182
|
+
} else {
|
|
183
|
+
throw "config missing: " + propName;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const viewer = extractCFG("viewer");
|
|
188
|
+
const handleMouseEvents = extractCFG("handleMouseEvents", false);
|
|
189
|
+
const handleTouchEvents = extractCFG("handleTouchEvents", false);
|
|
190
|
+
const snapping = extractCFG("snapping");
|
|
191
|
+
const pointerLens = extractCFG("pointerLens", null);
|
|
192
|
+
const color = extractCFG("color");
|
|
193
|
+
const markers = extractCFG("markers");
|
|
194
|
+
const onEdit = extractCFG("onEdit", nop);
|
|
195
|
+
|
|
196
|
+
const updatePointerLens = (pointerLens
|
|
197
|
+
? function(canvasPos) {
|
|
198
|
+
pointerLens.visible = !! canvasPos;
|
|
199
|
+
if (canvasPos)
|
|
200
|
+
{
|
|
201
|
+
pointerLens.canvasPos = canvasPos;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
: () => { });
|
|
205
|
+
|
|
206
|
+
const dots = markers.map(marker => {
|
|
207
|
+
let initDotPos, initMarkerPos;
|
|
208
|
+
const setCoord = coord => marker.worldPos = coord;
|
|
209
|
+
|
|
210
|
+
const dot = createDraggableDot3D({
|
|
211
|
+
handleMouseEvents: handleMouseEvents,
|
|
212
|
+
handleTouchEvents: handleTouchEvents,
|
|
213
|
+
viewer: viewer,
|
|
214
|
+
worldPos: marker.worldPos,
|
|
215
|
+
color: color,
|
|
216
|
+
ray2WorldPos: (orig, dir, canvasPos) => {
|
|
217
|
+
const tryPickWorldPos = snap => {
|
|
218
|
+
const pickResult = viewer.scene.pick({
|
|
219
|
+
canvasPos: canvasPos,
|
|
220
|
+
snapToEdge: snap,
|
|
221
|
+
snapToVertex: snap,
|
|
222
|
+
pickSurface: true // <<------ This causes picking to find the intersection point on the entity
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// If - when snapping - no pick found, then try w/o snapping
|
|
226
|
+
return (pickResult && pickResult.worldPos) ? pickResult.worldPos : (snap && tryPickWorldPos(false));
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return tryPickWorldPos(!!snapping) || initDotPos;
|
|
230
|
+
},
|
|
231
|
+
onStart: () => {
|
|
232
|
+
initDotPos = dot.getWorldPos().slice();
|
|
233
|
+
initMarkerPos = marker.worldPos.slice();
|
|
234
|
+
setOtherDotsActive(false, dot);
|
|
235
|
+
},
|
|
236
|
+
onMove: (canvasPos, worldPos) => {
|
|
237
|
+
updatePointerLens(canvasPos);
|
|
238
|
+
setCoord(worldPos);
|
|
239
|
+
},
|
|
240
|
+
onEnd: () => {
|
|
241
|
+
if (! math.compareVec3(initMarkerPos, marker.worldPos))
|
|
242
|
+
{
|
|
243
|
+
onEdit();
|
|
244
|
+
}
|
|
245
|
+
else
|
|
246
|
+
{
|
|
247
|
+
dot.setWorldPos(initDotPos);
|
|
248
|
+
setCoord(initMarkerPos);
|
|
249
|
+
}
|
|
250
|
+
updatePointerLens(null);
|
|
251
|
+
setOtherDotsActive(true, dot);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
return dot;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const setOtherDotsActive = (active, dot) => dots.forEach(d => (d !== dot) && d.setActive(active));
|
|
258
|
+
setOtherDotsActive(true);
|
|
259
|
+
|
|
260
|
+
return function() {
|
|
261
|
+
dots.forEach(m => m.destroy());
|
|
262
|
+
updatePointerLens(null);
|
|
263
|
+
};
|
|
264
|
+
}
|