@xeokit/xeokit-sdk 2.6.22 → 2.6.25

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.
@@ -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 {activateDraggableDots, Dot3D, 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
 
@@ -1760,26 +1612,19 @@ export class ZonesPolysurfaceTouchControl extends Component {
1760
1612
 
1761
1613
  export class ZoneEditControl extends Component {
1762
1614
  constructor(zone, cfg, handleMouseEvents, handleTouchEvents) {
1763
- super(zone.plugin.viewer.scene);
1764
- const self = this;
1615
+ const viewer = zone.plugin.viewer;
1616
+ const scene = viewer.scene;
1617
+ super(scene);
1765
1618
 
1766
1619
  const altitude = zone._geometry.altitude;
1767
- const pointerLens = cfg && cfg.pointerLens;
1768
- const updatePointerLens = (pointerLens
1769
- ? function(canvasPos) {
1770
- pointerLens.visible = !! canvasPos;
1771
- if (canvasPos)
1772
- {
1773
- pointerLens.canvasPos = canvasPos;
1774
- }
1775
- }
1776
- : () => { });
1777
1620
 
1778
1621
  const dots = zone._geometry.planeCoordinates.map(planeCoord => {
1779
- let initWorldPos, initPlaneCoord;
1780
- const setPlaneCoord = function(coord) {
1781
- planeCoord[0] = coord[0];
1782
- planeCoord[1] = coord[1];
1622
+ const dotParent = scene.canvas.canvas.ownerDocument.body;
1623
+ const dot = new Dot3D(scene, {}, dotParent, { fillColor: zone._color });
1624
+ dot.worldPos = math.vec3([ planeCoord[0], altitude, planeCoord[1] ]);
1625
+ dot.on("worldPos", function() {
1626
+ planeCoord[0] = dot.worldPos[0];
1627
+ planeCoord[1] = dot.worldPos[2];
1783
1628
  try {
1784
1629
  zone._rebuildMesh();
1785
1630
  } catch (e) {
@@ -1788,45 +1633,29 @@ export class ZoneEditControl extends Component {
1788
1633
  zone._zoneMesh = null;
1789
1634
  }
1790
1635
  }
1791
- };
1792
-
1793
- const dot = draggableDot3D(
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
- () => {
1801
- initWorldPos = dot.getWorldPos().slice();
1802
- initPlaneCoord = planeCoord.slice();
1803
- set_other_dots_active(false, dot);
1804
- },
1805
- (canvasPos, worldPos) => {
1806
- updatePointerLens(canvasPos);
1807
- setPlaneCoord([ worldPos[0], worldPos[2] ]);
1808
- },
1809
- () => {
1810
- if (zone._zoneMesh)
1811
- {
1812
- self.fire("edited");
1813
- }
1814
- else
1815
- {
1816
- dot.setWorldPos(initWorldPos);
1817
- setPlaneCoord(initPlaneCoord);
1818
- }
1819
- updatePointerLens(null);
1820
- set_other_dots_active(true, dot);
1821
- });
1636
+ });
1822
1637
  return dot;
1823
1638
  });
1824
- const set_other_dots_active = (active, dot) => dots.forEach(d => (d !== dot) && d.setActive(active));
1825
- set_other_dots_active(true);
1639
+
1640
+ const cleanupDrag = activateDraggableDots({
1641
+ viewer: viewer,
1642
+ handleMouseEvents: handleMouseEvents,
1643
+ handleTouchEvents: handleTouchEvents,
1644
+ pointerLens: cfg && cfg.pointerLens,
1645
+ dots: dots,
1646
+ ray2WorldPos: (orig, dir) => planeIntersect(altitude, math.vec3([ 0, 1, 0 ]), orig, dir),
1647
+ onEnd: (initPos, dot) => {
1648
+ if (zone._zoneMesh)
1649
+ {
1650
+ this.fire("edited");
1651
+ }
1652
+ return !! zone._zoneMesh;
1653
+ }
1654
+ });
1826
1655
 
1827
1656
  const cleanup = function() {
1828
- dots.forEach(m => m.destroy());
1829
- updatePointerLens(null);
1657
+ cleanupDrag();
1658
+ dots.forEach(d => d.destroy());
1830
1659
  };
1831
1660
 
1832
1661
  const destroyCb = zone.on("destroyed", cleanup);
@@ -97,6 +97,24 @@ class Dot {
97
97
  });
98
98
  }
99
99
 
100
+ if (cfg.onTouchstart) {
101
+ dotClickable.addEventListener('touchstart', (event) => {
102
+ cfg.onTouchstart(event, this);
103
+ });
104
+ }
105
+
106
+ if (cfg.onTouchmove) {
107
+ dotClickable.addEventListener('touchmove', (event) => {
108
+ cfg.onTouchmove(event, this);
109
+ });
110
+ }
111
+
112
+ if (cfg.onTouchend) {
113
+ dotClickable.addEventListener('touchend', (event) => {
114
+ cfg.onTouchend(event, this);
115
+ });
116
+ }
117
+
100
118
  if (cfg.onContextMenu) {
101
119
  if(os.isIphoneSafari()){
102
120
  dotClickable.addEventListener('touchstart', (event) => {
@@ -136,24 +154,6 @@ class Dot {
136
154
 
137
155
  }
138
156
 
139
- if (cfg.onTouchstart) {
140
- dotClickable.addEventListener('touchstart', (event) => {
141
- cfg.onTouchstart(event, this);
142
- });
143
- }
144
-
145
- if (cfg.onTouchmove) {
146
- dotClickable.addEventListener('touchmove', (event) => {
147
- cfg.onTouchmove(event, this);
148
- });
149
- }
150
-
151
- if (cfg.onTouchend) {
152
- dotClickable.addEventListener('touchend', (event) => {
153
- cfg.onTouchend(event, this);
154
- });
155
- }
156
-
157
157
  this.setPos(cfg.x || 0, cfg.y || 0);
158
158
  this.setFillColor(cfg.fillColor);
159
159
  this.setBorderColor(cfg.borderColor);
@@ -0,0 +1,286 @@
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 class Dot3D extends Marker {
15
+ constructor(scene, markerCfg, parentElement, cfg = {}) {
16
+ super(scene, markerCfg);
17
+
18
+ const handler = (cfgEvent, componentEvent) => {
19
+ return event => {
20
+ if (cfgEvent) {
21
+ cfgEvent(event);
22
+ }
23
+ this.fire(componentEvent, event, true);
24
+ };
25
+ };
26
+ this._dot = new Dot(parentElement, {
27
+ fillColor: cfg.fillColor,
28
+ zIndex: cfg.zIndex,
29
+ onMouseOver: handler(cfg.onMouseOver, "mouseover"),
30
+ onMouseLeave: handler(cfg.onMouseLeave, "mouseleave"),
31
+ onMouseWheel: handler(cfg.onMouseWheel, "wheel"),
32
+ onMouseDown: handler(cfg.onMouseDown, "mousedown"),
33
+ onMouseUp: handler(cfg.onMouseUp, "mouseup"),
34
+ onMouseMove: handler(cfg.onMouseMove, "mousemove"),
35
+ onTouchstart: handler(cfg.onTouchstart, "touchstart"),
36
+ onTouchmove: handler(cfg.onTouchmove, "touchmove"),
37
+ onTouchend: handler(cfg.onTouchend, "touchend"),
38
+ onContextMenu: handler(cfg.onContextMenu, "contextmenu")
39
+ });
40
+
41
+ const updateDotPos = () => {
42
+ const pos = this.canvasPos.slice();
43
+ transformToNode(scene.canvas.canvas, parentElement, pos);
44
+ this._dot.setPos(pos[0], pos[1]);
45
+ };
46
+
47
+ this.on("worldPos", updateDotPos);
48
+
49
+ const onViewMatrix = scene.camera.on("viewMatrix", updateDotPos);
50
+ const onProjMatrix = scene.camera.on("projMatrix", updateDotPos);
51
+ this._cleanup = () => {
52
+ scene.camera.off(onViewMatrix);
53
+ scene.camera.off(onProjMatrix);
54
+ this._dot.destroy();
55
+ };
56
+ }
57
+
58
+ setClickable(value) {
59
+ this._dot.setClickable(value);
60
+ }
61
+
62
+ setCulled(value) {
63
+ this._dot.setCulled(value);
64
+ }
65
+
66
+ setFillColor(value) {
67
+ this._dot.setFillColor(value);
68
+ }
69
+
70
+ setHighlighted(value) {
71
+ this._dot.setHighlighted(value);
72
+ }
73
+
74
+ setOpacity(value) {
75
+ this._dot.setOpacity(value);
76
+ }
77
+
78
+ setVisible(value) {
79
+ this._dot.setVisible(value);
80
+ }
81
+
82
+ destroy() {
83
+ this._cleanup();
84
+ super.destroy();
85
+ }
86
+
87
+ }
88
+
89
+ export function activateDraggableDot(dot, cfg) {
90
+ const extractCFG = function(propName, defaultValue) {
91
+ if (propName in cfg) {
92
+ return cfg[propName];
93
+ } else if (defaultValue !== undefined) {
94
+ return defaultValue;
95
+ } else {
96
+ throw "config missing: " + propName;
97
+ }
98
+ };
99
+
100
+ const viewer = extractCFG("viewer");
101
+ const ray2WorldPos = extractCFG("ray2WorldPos");
102
+ const handleMouseEvents = extractCFG("handleMouseEvents", false);
103
+ const handleTouchEvents = extractCFG("handleTouchEvents", false);
104
+ const onStart = extractCFG("onStart", nop);
105
+ const onMove = extractCFG("onMove", nop);
106
+ const onEnd = extractCFG("onEnd", nop);
107
+
108
+ const scene = viewer.scene;
109
+ const canvas = scene.canvas.canvas;
110
+
111
+ const pickWorldPos = canvasPos => {
112
+ const origin = math.vec3();
113
+ const direction = math.vec3();
114
+ math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
115
+ return ray2WorldPos(origin, direction, canvasPos);
116
+ };
117
+
118
+ const onChange = event => {
119
+ const canvasPos = math.vec2([ event.clientX, event.clientY ]);
120
+ transformToNode(canvas.ownerDocument.body, canvas, canvasPos);
121
+ onMove(canvasPos, pickWorldPos(canvasPos));
122
+ };
123
+
124
+ let currentDrag = null;
125
+
126
+ const onDragMove = function(event) {
127
+ const e = currentDrag.matchesEvent(event);
128
+ if (e)
129
+ {
130
+ onChange(e);
131
+ }
132
+ };
133
+
134
+ const onDragEnd = function(event) {
135
+ const e = currentDrag.matchesEvent(event);
136
+ if (e)
137
+ {
138
+ dot.setOpacity(idleOpacity);
139
+ currentDrag.cleanup();
140
+ onChange(e);
141
+ onEnd();
142
+ }
143
+ };
144
+
145
+ const startDrag = function(matchesEvent, cleanupHandlers) {
146
+ if (currentDrag) {
147
+ currentDrag.cleanup();
148
+ }
149
+
150
+ dot.setOpacity(1.0);
151
+ dot.setClickable(false);
152
+ viewer.cameraControl.active = false;
153
+
154
+ currentDrag = {
155
+ matchesEvent: matchesEvent,
156
+ cleanup: function() {
157
+ currentDrag = null;
158
+ dot.setClickable(true);
159
+ viewer.cameraControl.active = true;
160
+ cleanupHandlers();
161
+ }
162
+ };
163
+
164
+ onStart();
165
+ };
166
+
167
+ const cleanupDotHandlers = [ ];
168
+ const dot_on = function(event, callback) {
169
+ const id = dot.on(event, callback);
170
+ cleanupDotHandlers.push(() => dot.off(id));
171
+ };
172
+
173
+ if (handleMouseEvents)
174
+ {
175
+ dot_on("mouseover", () => (! currentDrag) && dot.setOpacity(1.0));
176
+ dot_on("mouseleave", () => (! currentDrag) && dot.setOpacity(idleOpacity));
177
+ dot_on("mousedown", event => {
178
+ if (event.which === 1)
179
+ {
180
+ canvas.addEventListener("mousemove", onDragMove);
181
+ canvas.addEventListener("mouseup", onDragEnd);
182
+ startDrag(
183
+ event => (event.which === 1) && event,
184
+ () => {
185
+ canvas.removeEventListener("mousemove", onDragMove);
186
+ canvas.removeEventListener("mouseup", onDragEnd);
187
+ });
188
+ }
189
+ });
190
+ }
191
+
192
+ if (handleTouchEvents)
193
+ {
194
+ let touchStartId;
195
+ dot_on("touchstart", event => {
196
+ event.preventDefault();
197
+ if (event.touches.length === 1)
198
+ {
199
+ touchStartId = event.touches[0].identifier;
200
+ startDrag(
201
+ event => [...event.changedTouches].find(e => e.identifier === touchStartId),
202
+ () => { touchStartId = null; });
203
+ }
204
+ });
205
+ dot_on("touchmove", event => {
206
+ event.preventDefault();
207
+ onDragMove(event);
208
+ });
209
+ dot_on("touchend", event => {
210
+ event.preventDefault();
211
+ onDragEnd(event);
212
+ });
213
+ }
214
+
215
+ const idleOpacity = 0.8;
216
+ dot.setOpacity(idleOpacity);
217
+
218
+ return function() {
219
+ currentDrag && currentDrag.cleanup();
220
+ cleanupDotHandlers.forEach(c => c());
221
+ dot.setOpacity(1.0);
222
+ };
223
+ };
224
+
225
+ export function activateDraggableDots(cfg) {
226
+ const extractCFG = function(propName, defaultValue) {
227
+ if (propName in cfg) {
228
+ return cfg[propName];
229
+ } else if (defaultValue !== undefined) {
230
+ return defaultValue;
231
+ } else {
232
+ throw "config missing: " + propName;
233
+ }
234
+ };
235
+
236
+ const viewer = extractCFG("viewer");
237
+ const handleMouseEvents = extractCFG("handleMouseEvents", false);
238
+ const handleTouchEvents = extractCFG("handleTouchEvents", false);
239
+ const pointerLens = extractCFG("pointerLens", null);
240
+ const dots = extractCFG("dots");
241
+ const ray2WorldPos = extractCFG("ray2WorldPos");
242
+ const onEnd = extractCFG("onEnd", nop);
243
+
244
+ const updatePointerLens = (pointerLens
245
+ ? function(canvasPos) {
246
+ pointerLens.visible = !! canvasPos;
247
+ if (canvasPos)
248
+ {
249
+ pointerLens.canvasPos = canvasPos;
250
+ }
251
+ }
252
+ : () => { });
253
+
254
+ const cleanups = dots.map(dot => {
255
+ let initPos;
256
+ return activateDraggableDot(dot, {
257
+ handleMouseEvents: handleMouseEvents,
258
+ handleTouchEvents: handleTouchEvents,
259
+ viewer: viewer,
260
+ ray2WorldPos: (orig, dir, canvasPos) => (ray2WorldPos(orig, dir, canvasPos) || initPos),
261
+ onStart: () => {
262
+ initPos = dot.worldPos.slice();
263
+ setOtherDotsActive(false, dot);
264
+ },
265
+ onMove: (canvasPos, worldPos) => {
266
+ updatePointerLens(canvasPos);
267
+ dot.worldPos = worldPos;
268
+ },
269
+ onEnd: () => {
270
+ if (! onEnd(initPos, dot)) {
271
+ dot.worldPos = initPos;
272
+ }
273
+ updatePointerLens(null);
274
+ setOtherDotsActive(true, dot);
275
+ }
276
+ });
277
+ });
278
+
279
+ const setOtherDotsActive = (active, dot) => dots.forEach(d => (d !== dot) && d.setClickable(active));
280
+ setOtherDotsActive(true);
281
+
282
+ return function() {
283
+ cleanups.forEach(c => c());
284
+ updatePointerLens(null);
285
+ };
286
+ }
@@ -6,4 +6,5 @@ export * from "./buildPlaneGeometry.js";
6
6
  export * from "./buildSphereGeometry.js";
7
7
  export * from "./buildTorusGeometry.js";
8
8
  export * from "./buildVectorTextGeometry.js";
9
- export * from "./buildPolylineGeometry.js";
9
+ export * from "./buildPolylineGeometry.js";
10
+ export * from "./buildLineGeometry.js";
@@ -196,11 +196,17 @@ class Marker extends Component {
196
196
  return;
197
197
  }
198
198
  if (this._onEntityDestroyed !== null) {
199
- this._entity.model.off(this._onEntityDestroyed);
199
+ if (this._entity.model) {
200
+ this._entity.model.off(this._onEntityDestroyed);
201
+ } else {
202
+ this._entity.off(this._onEntityDestroyed);
203
+ }
200
204
  this._onEntityDestroyed = null;
201
205
  }
202
206
  if (this._onEntityModelDestroyed !== null) {
203
- this._entity.model.off(this._onEntityModelDestroyed);
207
+ if (this._entity.model) {
208
+ this._entity.model.off(this._onEntityModelDestroyed);
209
+ }
204
210
  this._onEntityModelDestroyed = null;
205
211
  }
206
212
  }
@@ -212,7 +218,7 @@ class Marker extends Component {
212
218
  this._onEntityModelDestroyed = null;
213
219
  });
214
220
  } else {
215
- this._onEntityDestroyed = this._entity.model.on("destroyed", () => {
221
+ this._onEntityDestroyed = this._entity.on("destroyed", () => {
216
222
  this._entity = null;
217
223
  this._onEntityDestroyed = null;
218
224
  });