@xeokit/xeokit-sdk 2.6.55 → 2.6.57

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.
Files changed (32) hide show
  1. package/dist/xeokit-sdk.cjs.js +1369 -1771
  2. package/dist/xeokit-sdk.es.js +1367 -1772
  3. package/dist/xeokit-sdk.es5.js +709 -719
  4. package/dist/xeokit-sdk.min.cjs.js +5 -5
  5. package/dist/xeokit-sdk.min.es.js +5 -5
  6. package/dist/xeokit-sdk.min.es5.js +4 -4
  7. package/package.json +1 -1
  8. package/src/plugins/AngleMeasurementsPlugin/AngleMeasurement.js +155 -415
  9. package/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js +1 -1
  10. package/src/plugins/AnnotationsPlugin/Annotation.js +7 -5
  11. package/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js +236 -750
  12. package/src/plugins/StoreyViewsPlugin/StoreyViewsPlugin.js +51 -0
  13. package/src/plugins/TreeViewPlugin/RenderService.js +2 -1
  14. package/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js +5 -1
  15. package/src/plugins/ZonesPlugin/ZonesPlugin.js +15 -79
  16. package/src/plugins/lib/html/Dot.js +14 -41
  17. package/src/plugins/lib/html/Label.js +10 -37
  18. package/src/plugins/lib/html/MenuEvent.js +74 -0
  19. package/src/plugins/lib/html/Wire.js +20 -47
  20. package/src/plugins/lib/ui/index.js +370 -12
  21. package/src/viewer/Viewer.js +9 -0
  22. package/src/viewer/index.js +1 -0
  23. package/src/viewer/scene/CameraControl/lib/handlers/MousePanRotateDollyHandler.js +1 -1
  24. package/src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js +2 -1
  25. package/src/viewer/scene/marker/Marker.js +20 -23
  26. package/src/viewer/scene/model/SceneModelEntity.js +5 -0
  27. package/src/viewer/utils/index.js +1 -0
  28. package/src/viewer/utils/os.js +8 -1
  29. package/types/viewer/scene/models/PerformanceModel/index.d.ts +0 -1
  30. package/types/viewer/scene/models/SceneModel.d.ts +0 -3
  31. package/types/viewer/scene/models/SceneModelEntity.d.ts +6 -0
  32. package/types/viewer/scene/nodes/Node.d.ts +1 -1
@@ -1,7 +1,20 @@
1
1
  import {Dot} from "../html/Dot.js";
2
+ import {Label} from "../html/Label.js";
3
+ import {Wire} from "../html/Wire.js";
2
4
  import {math} from "../../../viewer/scene/math/math.js";
3
5
  import {Marker} from "../../../viewer/scene/marker/Marker.js";
4
6
 
7
+ const tmpVec2a = math.vec2();
8
+ const tmpVec2b = math.vec2();
9
+ const tmpVec2c = math.vec2();
10
+ const tmpVec2d = math.vec2();
11
+ const tmpVec3a = math.vec3();
12
+ const tmpVec3b = math.vec3();
13
+ const tmpVec4a = math.vec4();
14
+ const tmpVec4b = math.vec4();
15
+ const tmpVec4c = math.vec4();
16
+ const tmpVec4d = math.vec4();
17
+
5
18
  const nop = () => { };
6
19
 
7
20
  export function transformToNode(from, to, vec) {
@@ -11,10 +24,124 @@ export function transformToNode(from, to, vec) {
11
24
  vec[1] += fromRec.top - toRec.top;
12
25
  };
13
26
 
27
+ const toClipSpace = (camera, worldPos, p) => {
28
+ // to homogeneous coords
29
+ p.set(worldPos);
30
+ p[3] = 1;
31
+
32
+ // to clip space
33
+ math.mulMat4v4(camera.viewMatrix, p, p);
34
+ math.mulMat4v4(camera.projMatrix, p, p);
35
+ };
36
+
37
+ const toCanvasSpace = (canvas, parentElement, ndc, p) => {
38
+ p[0] = (1 + ndc[0]) * 0.5 * canvas.offsetWidth;
39
+ p[1] = (1 - ndc[1]) * 0.5 * canvas.offsetHeight;
40
+ transformToNode(canvas, parentElement, p);
41
+ };
42
+
43
+ const clipSegment = (scene, parentElement, start, end, canvasStart, canvasEnd) => {
44
+ const camera = scene.camera;
45
+ const canvas = scene.canvas.canvas;
46
+
47
+ if (math.distVec3(start, end) < 0.001) {
48
+ return false;
49
+ }
50
+
51
+ const delta = math.subVec3(end, start, tmpVec3a);
52
+ let s_min = 0.0;
53
+ let s_max = 1.0;
54
+
55
+ for (let plane of scene._sectionPlanesState.sectionPlanes) {
56
+ const endDot = math.dotVec3(plane.dir, math.subVec3(plane.pos, end, tmpVec3b));
57
+ const startDot = math.dotVec3(plane.dir, math.subVec3(plane.pos, start, tmpVec3b));
58
+
59
+ if ((startDot > 0) && (endDot > 0)) {
60
+ return false;
61
+ } else if ((startDot > 0) || (endDot > 0)) {
62
+ const denom = math.dotVec3(plane.dir, delta);
63
+ if (Math.abs(denom) >= 1e-6) {
64
+ const ratio = math.dotVec3(plane.dir, tmpVec3b) / denom;
65
+ if (startDot > 0) {
66
+ s_min = Math.max(s_min, ratio);
67
+ } else {
68
+ s_max = Math.min(s_max, ratio);
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ const p0 = tmpVec4a;
75
+ const p1 = tmpVec4b;
76
+ toClipSpace(camera, (s_min > 0) ? math.addVec3(start, math.mulVec3Scalar(delta, s_min, tmpVec3b), tmpVec3b) : start, p0);
77
+ toClipSpace(camera, (s_max < 1) ? math.addVec3(start, math.mulVec3Scalar(delta, s_max, tmpVec3b), tmpVec3b) : end, p1);
78
+
79
+ const p0Behind = ((p0[2] / p0[3]) < -1) || (p0[3] < 0);
80
+ const p1Behind = ((p1[2] / p1[3]) < -1) || (p1[3] < 0);
81
+ if (p0Behind && p1Behind) {
82
+ return false;
83
+ }
84
+
85
+ const t = (p0[3] + p0[2]) / ((p0[3] + p0[2]) - (p1[3] + p1[2]));
86
+
87
+ if ((t > 0) && (t < 1)) { //p0Behind || p1Behind) {
88
+ // Find the intersection of a segment with the near plane in clip space, if it exists."""
89
+ // Calculate the interpolation factor t where the line segment crosses the near plane
90
+ const delta = math.subVec4(p1, p0, tmpVec4c);
91
+ math.mulVec4Scalar(delta, t, delta);
92
+ math.addVec4(p0, delta, p0Behind ? p0 : p1);
93
+ }
94
+
95
+ // normalize clip space coords
96
+ math.mulVec4Scalar(p0, 1.0 / p0[3]);
97
+ math.mulVec4Scalar(p1, 1.0 / p1[3]);
98
+
99
+ let t_min = 0.0;
100
+ let t_max = 1.0;
101
+
102
+ // If either point is outside the view frustum, clip the line segment
103
+
104
+ for (let i = 0; i < 2; ++i) {
105
+ const denom = p1[i] - p0[i];
106
+
107
+ const l = (-p0[3] - p0[i]) / denom;
108
+ const r = ( p0[3] - p0[i]) / denom;
109
+
110
+ if (denom > 0) {
111
+ t_min = Math.max(t_min, l);
112
+ t_max = Math.min(t_max, r);
113
+ } else {
114
+ t_min = Math.max(t_min, r);
115
+ t_max = Math.min(t_max, l);
116
+ }
117
+ }
118
+
119
+ if (t_min >= t_max) {
120
+ return false;
121
+ }
122
+
123
+ // Calculate the clipped start and end points
124
+ const ndcDelta = math.subVec4(p1, p0, tmpVec4c);
125
+ math.addVec4(p0, math.mulVec4Scalar(ndcDelta, t_max, tmpVec4d), p1);
126
+ math.addVec4(p0, math.mulVec4Scalar(ndcDelta, t_min, tmpVec4d), p0);
127
+
128
+ math.mulVec4Scalar(p0, 1 / p0[3]);
129
+ math.mulVec4Scalar(p1, 1 / p1[3]);
130
+
131
+ toCanvasSpace(canvas, parentElement, p0, canvasStart);
132
+ toCanvasSpace(canvas, parentElement, p1, canvasEnd);
133
+
134
+ return true;
135
+ };
136
+
14
137
  export class Dot3D extends Marker {
15
138
  constructor(scene, markerCfg, parentElement, cfg = {}) {
139
+ const camera = scene.camera;
140
+
16
141
  super(scene, markerCfg);
17
142
 
143
+ this.__visible = true; // "__" to not interfere with Marker::_visible
144
+
18
145
  const handler = (cfgEvent, componentEvent) => {
19
146
  return event => {
20
147
  if (cfgEvent) {
@@ -24,6 +151,7 @@ export class Dot3D extends Marker {
24
151
  };
25
152
  };
26
153
  this._dot = new Dot(parentElement, {
154
+ borderColor: cfg.borderColor,
27
155
  fillColor: cfg.fillColor,
28
156
  zIndex: cfg.zIndex,
29
157
  onMouseOver: handler(cfg.onMouseOver, "mouseover"),
@@ -38,19 +166,62 @@ export class Dot3D extends Marker {
38
166
  onContextMenu: handler(cfg.onContextMenu, "contextmenu")
39
167
  });
40
168
 
169
+ const toClipSpace = (worldPos, p) => {
170
+ // to homogeneous coords
171
+ p.set(worldPos);
172
+ p[3] = 1;
173
+
174
+ // to clip space
175
+ math.mulMat4v4(camera.viewMatrix, p, p);
176
+ math.mulMat4v4(camera.projMatrix, p, p);
177
+ };
178
+
179
+ const toCanvasSpace = ndc => {
180
+ const canvas = scene.canvas.canvas;
181
+ ndc[0] = (1 + ndc[0]) * 0.5 * canvas.offsetWidth;
182
+ ndc[1] = (1 - ndc[1]) * 0.5 * canvas.offsetHeight;
183
+ transformToNode(canvas, parentElement, ndc);
184
+ };
185
+
41
186
  const updateDotPos = () => {
42
- const pos = this.canvasPos.slice();
43
- transformToNode(scene.canvas.canvas, parentElement, pos);
44
- this._dot.setPos(pos[0], pos[1]);
187
+ if (! this.__visible) {
188
+ return;
189
+ }
190
+ const p0 = tmpVec4c;
191
+ toClipSpace(this.worldPos, p0);
192
+ math.mulVec3Scalar(p0, 1.0 / p0[3]);
193
+
194
+ const outsideFrustum = ((p0[3] < 0)
195
+ ||
196
+ (p0[0] < -1) || (p0[0] > 1)
197
+ ||
198
+ (p0[1] < -1) || (p0[1] > 1)
199
+ ||
200
+ (p0[2] < -1) || (p0[2] > 1));
201
+ const culled = outsideFrustum || scene._sectionPlanesState.sectionPlanes.some(
202
+ plane => (math.dotVec3(plane.dir, math.subVec3(plane.pos, this.worldPos, tmpVec3a)) > 0));
203
+
204
+ this._dot.setCulled(culled);
205
+ if (!culled) {
206
+ toCanvasSpace(p0);
207
+ this._dot.setPos(p0[0], p0[1]);
208
+ }
45
209
  };
46
210
 
47
211
  this.on("worldPos", updateDotPos);
48
212
 
49
- const onViewMatrix = scene.camera.on("viewMatrix", updateDotPos);
50
- const onProjMatrix = scene.camera.on("projMatrix", updateDotPos);
213
+ const onViewMatrix = camera.on("viewMatrix", updateDotPos);
214
+ const onProjMatrix = camera.on("projMatrix", updateDotPos);
215
+ const onCanvasBnd = scene.canvas.on("boundary", updateDotPos);
216
+ const planesUpdate = scene.on("sectionPlaneUpdated", updateDotPos);
217
+
218
+ this._updatePosition = updateDotPos;
219
+
51
220
  this._cleanup = () => {
52
- scene.camera.off(onViewMatrix);
53
- scene.camera.off(onProjMatrix);
221
+ camera.off(onViewMatrix);
222
+ camera.off(onProjMatrix);
223
+ scene.canvas.off(onCanvasBnd);
224
+ scene.off(planesUpdate);
54
225
  this._dot.destroy();
55
226
  };
56
227
  }
@@ -59,14 +230,14 @@ export class Dot3D extends Marker {
59
230
  this._dot.setClickable(value);
60
231
  }
61
232
 
62
- setCulled(value) {
63
- this._dot.setCulled(value);
64
- }
65
-
66
233
  setFillColor(value) {
67
234
  this._dot.setFillColor(value);
68
235
  }
69
236
 
237
+ setBorderColor(value) {
238
+ this._dot.setBorderColor(value);
239
+ }
240
+
70
241
  setHighlighted(value) {
71
242
  this._dot.setHighlighted(value);
72
243
  }
@@ -76,7 +247,11 @@ export class Dot3D extends Marker {
76
247
  }
77
248
 
78
249
  setVisible(value) {
79
- this._dot.setVisible(value);
250
+ if (this.__visible != value) {
251
+ this.__visible = value;
252
+ this._updatePosition();
253
+ this._dot.setVisible(value);
254
+ }
80
255
  }
81
256
 
82
257
  destroy() {
@@ -86,6 +261,189 @@ export class Dot3D extends Marker {
86
261
 
87
262
  }
88
263
 
264
+ export class Label3D {
265
+ constructor(scene, parentElement, cfg) {
266
+ const camera = scene.camera;
267
+
268
+ this._label = new Label(parentElement, cfg);
269
+ this._start = math.vec3();
270
+ this._mid = math.vec3();
271
+ this._end = math.vec3();
272
+ this._yOff = 0;
273
+ this._betweenWires = false;
274
+ this.__visible = true;
275
+
276
+ const setPosOnWire = (p0, p1, yOff) => {
277
+ p0[0] += p1[0];
278
+ p0[1] += p1[1];
279
+ math.mulVec2Scalar(p0, .5);
280
+ this._label.setPos(p0[0], p0[1] + yOff);
281
+ };
282
+
283
+ this._updatePositions = () => {
284
+ if (! this.__visible) {
285
+ return;
286
+ }
287
+ if (this._betweenWires) {
288
+ const visibleA = clipSegment(scene, parentElement, this._start, this._mid, tmpVec2a, tmpVec2b);
289
+ const visibleB = clipSegment(scene, parentElement, this._end, this._mid, tmpVec2c, tmpVec2d);
290
+ this._label.setCulled(! (visibleA || visibleB));
291
+ if (visibleA && visibleB) {
292
+ tmpVec2b[0] += tmpVec2d[0];
293
+ tmpVec2b[1] += tmpVec2d[1];
294
+ math.mulVec2Scalar(tmpVec2b, .5);
295
+
296
+ tmpVec2b[0] += tmpVec2a[0] + tmpVec2c[0];
297
+ tmpVec2b[1] += tmpVec2a[1] + tmpVec2c[1];
298
+ math.mulVec2Scalar(tmpVec2b, 1/3);
299
+ this._label.setPos(tmpVec2b[0], tmpVec2b[1]);
300
+ } else if (visibleA) {
301
+ setPosOnWire(tmpVec2a, tmpVec2b, 0);
302
+ } else if (visibleB) {
303
+ setPosOnWire(tmpVec2c, tmpVec2d, 0);
304
+ }
305
+ } else {
306
+ const visible = (clipSegment(scene, parentElement, this._start, this._end, tmpVec2a, tmpVec2b)
307
+ &&
308
+ (math.distVec2(tmpVec2a, tmpVec2b) >= this._labelMinAxisLength));
309
+ this._label.setCulled(!visible);
310
+ if (visible) {
311
+ setPosOnWire(tmpVec2a, tmpVec2b, this._yOff);
312
+ }
313
+ }
314
+ };
315
+
316
+ const onViewMatrix = camera.on("viewMatrix", this._updatePositions);
317
+ const onProjMatrix = camera.on("projMatrix", this._updatePositions);
318
+ const onCanvasBnd = scene.canvas.on("boundary", this._updatePositions);
319
+ const planesUpdate = scene.on("sectionPlaneUpdated", this._updatePositions);
320
+
321
+ this._cleanup = () => {
322
+ camera.off(onViewMatrix);
323
+ camera.off(onProjMatrix);
324
+ scene.canvas.off(onCanvasBnd);
325
+ scene.off(planesUpdate);
326
+ this._label.destroy();
327
+ };
328
+ }
329
+
330
+ setPosOnWire(p0, p1, yOff, labelMinAxisLength) {
331
+ this._start.set(p0);
332
+ this._end.set(p1);
333
+ this._yOff = yOff;
334
+ this._labelMinAxisLength = labelMinAxisLength;
335
+ this._betweenWires = false;
336
+ this._updatePositions();
337
+ }
338
+
339
+ setPosBetween(p0, p1, p2) {
340
+ this._start.set(p0);
341
+ this._mid.set(p1);
342
+ this._end.set(p2);
343
+ this._betweenWires = true;
344
+ this._updatePositions();
345
+ }
346
+
347
+ setFillColor(value) {
348
+ this._label.setFillColor(value);
349
+ }
350
+
351
+ setHighlighted(value) {
352
+ this._label.setHighlighted(value);
353
+ }
354
+
355
+ setText(value) {
356
+ this._label.setText(value);
357
+ }
358
+
359
+ setClickable(value) {
360
+ this._label.setClickable(value);
361
+ }
362
+
363
+ setVisible(value) {
364
+ if (this.__visible != value) {
365
+ this.__visible = value;
366
+ this._updatePositions();
367
+ this._label.setVisible(value);
368
+ }
369
+ }
370
+
371
+ destroy() {
372
+ this._cleanup();
373
+ }
374
+
375
+ }
376
+
377
+ export class Wire3D {
378
+ constructor(scene, parentElement, cfg) {
379
+ const camera = scene.camera;
380
+
381
+ this._wire = new Wire(parentElement, cfg);
382
+ this._start = math.vec3();
383
+ this._end = math.vec3();
384
+ this.__visible = true;
385
+
386
+ this._updatePositions = () => {
387
+ if (! this.__visible) {
388
+ return;
389
+ }
390
+ const visible = clipSegment(scene, parentElement, this._start, this._end, tmpVec2a, tmpVec2b);
391
+ this._wire.setCulled(! visible);
392
+ if (visible) {
393
+ this._wire.setStartAndEnd(tmpVec2a[0], tmpVec2a[1], tmpVec2b[0], tmpVec2b[1]);
394
+ }
395
+ };
396
+
397
+ const onViewMatrix = camera.on("viewMatrix", this._updatePositions);
398
+ const onProjMatrix = camera.on("projMatrix", this._updatePositions);
399
+ const onCanvasBnd = scene.canvas.on("boundary", this._updatePositions);
400
+ const planesUpdate = scene.on("sectionPlaneUpdated", this._updatePositions);
401
+
402
+ this._cleanup = () => {
403
+ camera.off(onViewMatrix);
404
+ camera.off(onProjMatrix);
405
+ scene.canvas.off(onCanvasBnd);
406
+ scene.off(planesUpdate);
407
+ this._wire.destroy();
408
+ };
409
+ }
410
+
411
+ setEnds(start, end) {
412
+ this._start.set(start);
413
+ this._end.set(end);
414
+ this._updatePositions();
415
+ }
416
+
417
+ setClickable(value) {
418
+ this._wire.setClickable(value);
419
+ }
420
+
421
+ setColor(value) {
422
+ this._wire.setColor(value);
423
+ }
424
+
425
+ setHighlighted(value) {
426
+ this._wire.setHighlighted(value);
427
+ }
428
+
429
+ setOpacity(value) {
430
+ this._wire.setOpacity(value);
431
+ }
432
+
433
+ setVisible(value) {
434
+ if (this.__visible != value) {
435
+ this.__visible = value;
436
+ this._updatePositions();
437
+ this._wire.setVisible(value);
438
+ }
439
+ }
440
+
441
+ destroy() {
442
+ this._cleanup();
443
+ }
444
+
445
+ }
446
+
89
447
  export function activateDraggableDot(dot, cfg) {
90
448
  const extractCFG = function(propName, defaultValue) {
91
449
  if (propName in cfg) {
@@ -491,6 +491,12 @@ class Viewer {
491
491
  }
492
492
  }
493
493
 
494
+ // Added to fix label's text offset in an html2canvas capture (See XEOK-151)
495
+ // based on https://github.com/niklasvh/html2canvas/issues/2775#issuecomment-1316356991
496
+ const style = document.createElement('style');
497
+ document.head.appendChild(style);
498
+ style.sheet?.insertRule('body > div:last-child img { display: inline-block; }');
499
+
494
500
  for (let i = 0, len = pluginContainerElements.length; i < len; i++) {
495
501
  const containerElement = pluginContainerElements[i];
496
502
  await html2canvas(containerElement, {
@@ -503,6 +509,9 @@ class Viewer {
503
509
  // (implemented to compensate XCD-153 issue)
504
510
  snapshotCanvas.getContext("2d").resetTransform();
505
511
  }
512
+
513
+ style.remove();
514
+
506
515
  if (!params.includeGizmos) {
507
516
  this.sendToPlugins("snapshotFinished");
508
517
  }
@@ -1,5 +1,6 @@
1
1
  export * from "./localization/LocaleService.js";
2
2
  export * from "./scene/index.js";
3
+ export * from './utils/index.js';
3
4
  export * from "./Plugin.js";
4
5
  export * from "./Viewer.js";
5
6
  export * from "./Configs.js";
@@ -108,7 +108,7 @@ class MousePanRotateDollyHandler {
108
108
  }
109
109
 
110
110
  function isPanning() {
111
- return cameraControl._isKeyDownForAction(cameraControl.MOUSE_PAN, keyDown);
111
+ return configs.planView || cameraControl._isKeyDownForAction(cameraControl.MOUSE_PAN, keyDown);
112
112
  }
113
113
 
114
114
  function isRotating() {
@@ -119,7 +119,8 @@ class TouchPanRotateAndDollyHandler {
119
119
 
120
120
  canvas.addEventListener("touchend", this._canvasTouchEndHandler = () => {
121
121
  if (pivotController.getPivoting()) {
122
- pivotController.endPivot()
122
+ pivotController.endPivot();
123
+ pivotController.hidePivot();
123
124
  }
124
125
  firstDragDeltaX = 0;
125
126
  firstDragDeltaY = 0;
@@ -191,28 +191,13 @@ class Marker extends Component {
191
191
  * @type {Entity}
192
192
  */
193
193
  set entity(entity) {
194
- if (this._entity) {
195
- if (this._entity === entity) {
196
- return;
197
- }
198
- if (this._onEntityDestroyed !== null) {
199
- if (this._entity.model) {
200
- this._entity.model.off(this._onEntityDestroyed);
201
- } else {
202
- this._entity.off(this._onEntityDestroyed);
203
- }
204
- this._onEntityDestroyed = null;
205
- }
206
- if (this._onEntityModelDestroyed !== null) {
207
- if (this._entity.model) {
208
- this._entity.model.off(this._onEntityModelDestroyed);
209
- }
210
- this._onEntityModelDestroyed = null;
211
- }
194
+ if (this._entity === entity) {
195
+ return;
212
196
  }
197
+ this._cleanupDestroyedHandlers();
213
198
  this._entity = entity;
214
199
  if (this._entity) {
215
- if (this._entity instanceof SceneModelEntity) {
200
+ if (this._entity.isSceneModelEntity) {
216
201
  this._onEntityModelDestroyed = this._entity.model.on("destroyed", () => { // SceneModelEntity does not fire events, and cannot exist beyond its VBOSceneModel
217
202
  this._entity = null; // Marker now may become visible, if it was synched to invisible Entity
218
203
  this._onEntityModelDestroyed = null;
@@ -374,16 +359,28 @@ class Marker extends Component {
374
359
  this.fire("destroyed", true);
375
360
  this.scene.camera.off(this._onCameraViewMatrix);
376
361
  this.scene.camera.off(this._onCameraProjMatrix);
362
+ this._cleanupDestroyedHandlers();
363
+ this._renderer.removeMarker(this);
364
+ super.destroy();
365
+ }
366
+
367
+ _cleanupDestroyedHandlers() {
377
368
  if (this._entity) {
378
369
  if (this._onEntityDestroyed !== null) {
379
- this._entity.model.off(this._onEntityDestroyed);
370
+ if (this._entity.model) {
371
+ this._entity.model.off(this._onEntityDestroyed);
372
+ } else {
373
+ this._entity.off(this._onEntityDestroyed);
374
+ }
375
+ this._onEntityDestroyed = null;
380
376
  }
381
377
  if (this._onEntityModelDestroyed !== null) {
382
- this._entity.model.off(this._onEntityModelDestroyed);
378
+ if (this._entity.model) {
379
+ this._entity.model.off(this._onEntityModelDestroyed);
380
+ }
381
+ this._onEntityModelDestroyed = null;
383
382
  }
384
383
  }
385
- this._renderer.removeMarker(this);
386
- super.destroy();
387
384
  }
388
385
  }
389
386
 
@@ -34,6 +34,11 @@ export class SceneModelEntity {
34
34
  */
35
35
  this.model = model;
36
36
 
37
+ /**
38
+ * Identifies if it's a SceneModelEntity
39
+ */
40
+ this.isSceneModelEntity = true;
41
+
37
42
  /**
38
43
  * The {@link SceneModelMesh}es belonging to this SceneModelEntity.
39
44
  *
@@ -0,0 +1 @@
1
+ export * from './os.js';
@@ -5,7 +5,14 @@ const os = {
5
5
  const isSafari = /Safari/i.test(userAgent) && !/Chrome/i.test(userAgent);
6
6
 
7
7
  return isIphone && isSafari;
8
+ },
9
+ isTouchDevice() {
10
+ return (
11
+ 'ontouchstart' in window || //works for most devices
12
+ navigator.maxTouchPoints > 0 || //works for modern touch devices
13
+ navigator.mxMaxTouchPoints > 0 //works for older microsoft touch devices
14
+ )
8
15
  }
9
16
  };
10
17
 
11
- export {os};
18
+ export { os };
@@ -1,2 +1 @@
1
1
  export * from "./PerformanceModel";
2
- export * from "./PerformanceNode";
@@ -168,9 +168,6 @@ export declare class SceneModel extends Component {
168
168
  */
169
169
  get aabb(): number[];
170
170
 
171
-
172
- get numEntities(): number;
173
-
174
171
  /**
175
172
  * The number of entities in this SceneModel.
176
173
  *
@@ -411,6 +411,12 @@ export declare abstract class SceneModelEntity implements Entity {
411
411
  */
412
412
  meshes : SceneModelMesh[];
413
413
 
414
+
415
+ /**
416
+ * Identifies if it's a SceneModelEntity
417
+ */
418
+ isSceneModelEntity: boolean;
419
+
414
420
  /**
415
421
  * Destroys this SceneModelEntity.
416
422
  */
@@ -58,7 +58,7 @@ export declare type NodeConfiguration = {
58
58
  /**
59
59
  * An {@link Entity} that is a scene graph node that can have child Nodes and {@link Mesh}es.
60
60
  */
61
- export declare class Node extends Component implements Omit<Entity, "parent"> {
61
+ export declare class Node extends Component {
62
62
  /**
63
63
  * @constructor
64
64
  * @param {Component} owner Owner component. When destroyed, the owner will destroy this component as well.