@xeokit/xeokit-sdk 2.6.69 → 2.6.72

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 (46) hide show
  1. package/dist/xeokit-sdk.cjs.js +4332 -207
  2. package/dist/xeokit-sdk.es.js +4332 -208
  3. package/dist/xeokit-sdk.es5.js +221 -204
  4. package/dist/xeokit-sdk.min.cjs.js +10 -4
  5. package/dist/xeokit-sdk.min.es.js +11 -5
  6. package/dist/xeokit-sdk.min.es5.js +10 -4
  7. package/package.json +3 -1
  8. package/src/extras/ContextMenu/ContextMenu.js +11 -9
  9. package/src/plugins/AnnotationsPlugin/Annotation.js +28 -16
  10. package/src/plugins/CxConverterIFCLoaderPlugin/CxConverterIFCLoaderPlugin.js +86 -0
  11. package/src/plugins/CxConverterIFCLoaderPlugin/index.js +1 -0
  12. package/src/plugins/LASLoaderPlugin/LASDefaultDataSource.js +13 -1
  13. package/src/plugins/LASLoaderPlugin/LASLoaderPlugin.js +89 -43
  14. package/src/plugins/NavCubePlugin/CubeTextureCanvas.js +2 -2
  15. package/src/plugins/NavCubePlugin/NavCubePlugin.js +10 -10
  16. package/src/plugins/SectionPlanesPlugin/Control.js +2 -2
  17. package/src/plugins/TreeViewPlugin/TreeViewPlugin.js +4 -3
  18. package/src/plugins/ZonesPlugin/ZonesPlugin.js +6 -151
  19. package/src/plugins/index.js +1 -0
  20. package/src/plugins/lib/ui/index.js +592 -34
  21. package/src/viewer/scene/geometry/ReadableGeometry.js +60 -0
  22. package/src/viewer/scene/geometry/builders/buildPlaneGeometry.js +1 -1
  23. package/src/viewer/scene/math/math.js +14 -2
  24. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLineSnapInitRenderer.js +2 -0
  25. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapInitRenderer.js +2 -0
  26. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapRenderer.js +2 -0
  27. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapInitRenderer.js +2 -0
  28. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapRenderer.js +2 -0
  29. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapInitRenderer.js +2 -0
  30. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapRenderer.js +2 -0
  31. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapInitRenderer.js +1 -24
  32. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapRenderer.js +2 -19
  33. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapInitRenderer.js +1 -24
  34. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapRenderer.js +1 -19
  35. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapInitRenderer.js +1 -24
  36. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapRenderer.js +2 -19
  37. package/src/viewer/scene/scene/Scene.js +2 -3
  38. package/src/viewer/scene/sectionCaps/SectionCaps.js +46 -65
  39. package/types/plugins/CxConverterIFCLoaderPlugin/CxConverterIFCLoaderPlugin.d.ts +36 -0
  40. package/types/plugins/CxConverterIFCLoaderPlugin/index.d.ts +1 -0
  41. package/types/plugins/NavCubePlugin/NavCubePlugin.d.ts +1 -1
  42. package/types/plugins/index.d.ts +1 -0
  43. package/types/viewer/scene/ImagePlane/ImagePlane.d.ts +397 -0
  44. package/types/viewer/scene/ImagePlane/index.d.ts +1 -0
  45. package/types/viewer/scene/geometry/builders/index.d.ts +1 -0
  46. package/types/viewer/scene/index.d.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xeokit/xeokit-sdk",
3
- "version": "2.6.69",
3
+ "version": "2.6.72",
4
4
  "description": "3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision",
5
5
  "module": "./dist/xeokit-sdk.es.js",
6
6
  "main": "./dist/xeokit-sdk.cjs.js",
@@ -53,6 +53,7 @@
53
53
  },
54
54
  "homepage": "https://xeokit.io",
55
55
  "dependencies": {
56
+ "@creooxag/cx-converter": "^0.0.12-alpha",
56
57
  "@loaders.gl/core": "^4.3.3",
57
58
  "@loaders.gl/gltf": "^4.3.3",
58
59
  "@loaders.gl/las": "^4.3.3",
@@ -76,6 +77,7 @@
76
77
  "eslint": "^8.13.0",
77
78
  "parse5": "^7.0.0",
78
79
  "rollup": "^2.70.2",
80
+ "rollup-plugin-banner": "^0.2.1",
79
81
  "rollup-plugin-terser": "^7.0.2",
80
82
  "typedoc": "^0.22.15"
81
83
  },
@@ -282,6 +282,7 @@ class ContextMenu {
282
282
  * @param {Boolean} [cfg.enabled=true] Whether this ````ContextMenu```` is initially enabled. {@link ContextMenu#show} does nothing while this is ````false````.
283
283
  * @param {Boolean} [cfg.hideOnMouseDown=true] Whether this ````ContextMenu```` automatically hides whenever we mouse-down or tap anywhere in the page.
284
284
  * @param {Boolean} [cfg.hideOnAction=true] Whether this ````ContextMenu```` automatically hides after we select a menu item. Se false if we want the menu to remain shown and show any updates to its item titles, after we've selected an item.
285
+ * @param {Node | undefined} [cfg.parentNode] Optional reference to an existing DOM Node (e.g. ShadowRoot), to which the menu's HTML element will be appended, defaults to ````document.body````.
285
286
  */
286
287
  constructor(cfg = {}) {
287
288
 
@@ -296,6 +297,7 @@ class ContextMenu {
296
297
  this._itemMap = {}; // Items mapped to their IDs
297
298
  this._shown = false; // True when the ContextMenu is visible
298
299
  this._nextId = 0;
300
+ this._parentNode = cfg.parentNode || document.body;
299
301
 
300
302
  /**
301
303
  * Subscriptions to events fired at this ContextMenu.
@@ -304,12 +306,12 @@ class ContextMenu {
304
306
  this._eventSubs = {};
305
307
 
306
308
  if (cfg.hideOnMouseDown !== false) {
307
- document.addEventListener("mousedown", (event) => {
309
+ this._parentNode.addEventListener("mousedown", (event) => {
308
310
  if (!event.target.classList.contains("xeokit-context-menu-item")) {
309
311
  this.hide();
310
312
  }
311
313
  });
312
- document.addEventListener("touchstart", this._canvasTouchStartHandler = (event) => {
314
+ this._parentNode.addEventListener("touchstart", this._canvasTouchStartHandler = (event) => {
313
315
  if (!event.target.classList.contains("xeokit-context-menu-item")) {
314
316
  this.hide();
315
317
  }
@@ -613,7 +615,10 @@ class ContextMenu {
613
615
  const groups = menu.groups;
614
616
  const html = [];
615
617
 
616
- html.push('<div class="xeokit-context-menu ' + menu.id + '" style="z-index:300000; position: absolute;">');
618
+ const menuElement= document.createElement("div");
619
+ menuElement.classList.add("xeokit-context-menu", menu.id);
620
+ menuElement.style.zIndex = 300000;
621
+ menuElement.style.position = "absolute";
617
622
 
618
623
  html.push('<ul>');
619
624
 
@@ -664,13 +669,10 @@ class ContextMenu {
664
669
  }
665
670
 
666
671
  html.push('</ul>');
667
- html.push('</div>');
668
672
 
669
673
  const htmlString = html.join("");
670
-
671
- document.body.insertAdjacentHTML('beforeend', htmlString);
672
-
673
- const menuElement = document.querySelector("." + menu.id);
674
+ menuElement.innerHTML = htmlString;
675
+ this._parentNode.appendChild(menuElement);
674
676
 
675
677
  menu.menuElement = menuElement;
676
678
 
@@ -704,7 +706,7 @@ class ContextMenu {
704
706
  const item = groupItems[j];
705
707
  const itemSubMenu = item.subMenu;
706
708
 
707
- item.itemElement = document.getElementById(item.id);
709
+ item.itemElement = this._parentNode.querySelector(`#${item.id}`);
708
710
 
709
711
  if (!item.itemElement) {
710
712
  console.error("ContextMenu item element not found: " + item.id);
@@ -7,6 +7,8 @@ const tempVec3a = math.vec3();
7
7
  const tempVec3b = math.vec3();
8
8
  const tempVec3c = math.vec3();
9
9
 
10
+ const px = x => x + "px";
11
+
10
12
  /**
11
13
  * A {@link Marker} with an HTML label attached to it, managed by an {@link AnnotationsPlugin}.
12
14
  *
@@ -213,22 +215,29 @@ class Annotation extends Marker {
213
215
  * @private
214
216
  */
215
217
  _updateWithCurWidths() {
216
- const px = x => x + "px";
217
218
  const boundary = this.scene.canvas.boundary;
218
219
  const left = boundary[0] + this.canvasPos[0];
219
220
  const top = boundary[1] + this.canvasPos[1];
220
- const markerWidth = this._curMarkerWidth;
221
- const markerDir = (this._markerAlign === "right") ? -1 : ((this._markerAlign === "center") ? 0 : 1);
222
- const markerCenter = left + markerDir * (markerWidth / 2 - 12);
223
- this._marker.style.left = px(markerCenter - markerWidth / 2);
224
- this._marker.style.top = px(top - 12);
225
- this._marker.style["z-index"] = 90005 + Math.floor(this._viewPos[2]) + 1;
226
-
227
- const labelWidth = this._curLabelWidth;
228
- const labelDir = Math.sign(this._labelPosition);
229
- this._label.style.left = px(markerCenter + labelDir * (markerWidth / 2 + Math.abs(this._labelPosition) + labelWidth / 2) - labelWidth / 2);
221
+ this._marker.style.top = px(top - 12);
230
222
  this._label.style.top = px(top - 17);
231
- this._label.style["z-index"] = 90005 + Math.floor(this._viewPos[2]) + 1;
223
+
224
+ if (this._markerAlign === "legacy") {
225
+ this._marker.style.left = px(left - 12);
226
+ this._label.style.left = px(left + 40);
227
+ } else {
228
+ const markerWidth = this._curMarkerWidth;
229
+ const markerDir = (this._markerAlign === "right") ? -1 : ((this._markerAlign === "center") ? 0 : 1);
230
+ const markerCenter = left + markerDir * (markerWidth / 2 - 12);
231
+ this._marker.style.left = px(markerCenter - markerWidth / 2);
232
+
233
+ const labelWidth = this._curLabelWidth;
234
+ const labelDir = Math.sign(this._labelPosition);
235
+ this._label.style.left = px(markerCenter + labelDir * (markerWidth / 2 + Math.abs(this._labelPosition) + labelWidth / 2) - labelWidth / 2);
236
+ }
237
+
238
+ const zIndex = 90005 + Math.floor(this._viewPos[2]) + 1;
239
+ this._marker.style["z-index"] = zIndex;
240
+ this._label.style["z-index"] = zIndex;
232
241
  }
233
242
 
234
243
  /**
@@ -259,7 +268,8 @@ class Annotation extends Marker {
259
268
  * @private
260
269
  */
261
270
  _updatePosition() {
262
- if (this._curMarkerWidth === undefined) {
271
+ const isLegacy = this._markerAlign === "legacy";
272
+ if ((! isLegacy) && (this._curMarkerWidth === undefined)) {
263
273
  this._updateIfWidthsChanged();
264
274
  } else {
265
275
  // Update position with cached width values
@@ -267,7 +277,9 @@ class Annotation extends Marker {
267
277
  // so they don't interfere with e.g. interactive scene manipulation
268
278
  this._updateWithCurWidths();
269
279
  window.clearTimeout(this._widthTimeout);
270
- this._widthTimeout = window.setTimeout(() => this._updateIfWidthsChanged(), 500);
280
+ if (! isLegacy) {
281
+ this._widthTimeout = window.setTimeout(() => this._updateIfWidthsChanged(), 500);
282
+ }
271
283
  }
272
284
  }
273
285
 
@@ -305,10 +317,10 @@ class Annotation extends Marker {
305
317
  /**
306
318
  * Sets the horizontal alignment of the Annotation's marker HTML.
307
319
  *
308
- * @param {String} align Either "left", "center", "right" (default "left")
320
+ * @param {String} align Either "left", "center", "right", "legacy" (default "left")
309
321
  */
310
322
  setMarkerAlign(align) {
311
- const valid = [ "left", "center", "right" ];
323
+ const valid = [ "left", "center", "right", "legacy" ];
312
324
  if (! valid.includes(align)) {
313
325
  this.error("Param 'align' should be one of: " + JSON.stringify(valid));
314
326
  } else {
@@ -0,0 +1,86 @@
1
+ import { Plugin } from "../../viewer/Plugin.js";
2
+ import { GLTFLoaderPlugin } from "../GLTFLoaderPlugin/GLTFLoaderPlugin.js";
3
+ import { ifc2gltf } from "@creooxag/cx-converter";
4
+
5
+ /**
6
+ * Fetches a file from the given URL and returns its contents as text.
7
+ *
8
+ * @param {string} url - The URL to fetch the file from.
9
+ * @returns {Promise<string>} A promise that resolves to the file contents.
10
+ * @private
11
+ */
12
+ async function fetchFile(url) {
13
+ try {
14
+ const response = await fetch(url);
15
+ if (!response.ok) {
16
+ throw new Error(`HTTP error! Status: ${response.status}`);
17
+ }
18
+ return response.text();
19
+ } catch (error) {
20
+ console.error('Error fetching file:', error);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * {@link Viewer} plugin that uses [CxConverter](https://github.com/Creoox/cxconverter) to load BIM models directly from IFC files, using its WebAssembly buid from [npm package](https://www.npmjs.com/package/@creooxag/cx-converter).
26
+ *
27
+ * ## Overview
28
+ * The WebAssembly build of CxConverter is still in alfa stage, so it may not work as expected. This documentation will be updated as the library and the plugin evolve. The example below shows how to use the plugin:
29
+ * ````javascript
30
+ * import { CxConverterIFCLoaderPlugin, Viewer } from "../../dist/xeokit-sdk.es.js";
31
+ * const cxConverterIFCLoaderPlugin = new CxConverterIFCLoaderPlugin(viewer);
32
+ *
33
+ * const sceneModel = await cxConverterIFCLoaderPlugin.load({
34
+ * src: "../../assets/models/ifc/Duplex.ifc"
35
+ * });
36
+ * ````
37
+ See the code in action [here](https://xeokit.github.io/xeokit-sdk/examples/buildings/#cxConverterIFC_vbo_Duplex).
38
+ */
39
+
40
+ class CxConverterIFCLoaderPlugin extends Plugin {
41
+ /**
42
+ * @constructor
43
+ * @param {Viewer} viewer The Viewer.
44
+ * @param {Object} [cfg={}] Plugin configuration.
45
+ */
46
+ constructor(viewer, cfg = {}) {
47
+ super("ifcLoader", viewer, cfg);
48
+
49
+ /**
50
+ * The GLTFLoaderPlugin used internally to load the converted GLTF.
51
+ * @type {GLTFLoaderPlugin}
52
+ */
53
+ this.gltfLoader = new GLTFLoaderPlugin(this.viewer);
54
+ }
55
+
56
+ /**
57
+ * Loads an IFC model from the given source.
58
+ *
59
+ * @param {Object} [params={}] Loading parameters.
60
+ * @param {string} params.src Path to an IFC file.
61
+ * @param {Function} [params.progressCallback] Callback to track loading progress.
62
+ * @param {Function} [params.progressTextCallback] Callback to track loading progress with text updates.
63
+ * @returns {Promise<SceneModel>} A promise that resolves to the loaded SceneModel.
64
+ */
65
+ async load(params = {}) {
66
+ if (!params.src) {
67
+ this.error("load() param expected: src");
68
+ }
69
+ const data = await fetchFile(params.src);
70
+ const { gltf, metaData } = await ifc2gltf(
71
+ data,
72
+ {
73
+ remote: true,
74
+ progressCallback: params.progressCallback,
75
+ progressTextCallback: params.progressTextCallback
76
+ }
77
+ );
78
+ const sceneModel = this.gltfLoader.load({
79
+ id: "myModel",
80
+ gltf: gltf,
81
+ metaModelJSON: metaData
82
+ });
83
+ return sceneModel;
84
+ }
85
+ }
86
+ export { CxConverterIFCLoaderPlugin };
@@ -0,0 +1 @@
1
+ export * from "./CxConverterIFCLoaderPlugin.js";
@@ -1,3 +1,5 @@
1
+ import {utils} from "../../viewer/scene/utils.js";
2
+
1
3
  /**
2
4
  * Default data access strategy for {@link LASLoaderPlugin}.
3
5
  */
@@ -60,13 +62,23 @@ class LASDefaultDataSource {
60
62
  if (request.status === 200) {
61
63
  ok(request.response);
62
64
  } else {
63
- error('getXKT error : ' + request.response);
65
+ error('getLAS error : ' + request.response);
64
66
  }
65
67
  }
66
68
  };
67
69
  request.send(null);
68
70
  }
69
71
  }
72
+
73
+ getManifest(manifestSrc, ok, error) {
74
+ utils.loadJSON(this._cacheBusterURL(manifestSrc),
75
+ (json) => {
76
+ ok(json);
77
+ },
78
+ function (errMsg) {
79
+ error(errMsg);
80
+ });
81
+ }
70
82
  }
71
83
 
72
84
  export {LASDefaultDataSource};
@@ -430,6 +430,8 @@ class LASLoaderPlugin extends Plugin {
430
430
  * @param {String} [params.id] ID to assign to the root {@link Entity#id}, unique among all components in the Viewer's {@link Scene}, generated automatically by default.
431
431
  * @param {String} [params.src] Path to a LAS file, as an alternative to the ````las```` parameter.
432
432
  * @param {ArrayBuffer} [params.las] The LAS file data, as an alternative to the ````src```` parameter.
433
+ * @param {String} [params.manifestSrc] Path or URL to a JSON manifest file that provides paths to ````.laz```` || ````.las```` files to load as parts of the model. Use this option to load models that have been split into
434
+ * @param {Object} [params.manifest] A JSON manifest object (as an alternative to a path or URL) that provides paths to ````.laz```` || ````.las```` files to load as parts of the model. Use this option to load models that have been split into
433
435
  * @param {Boolean} [params.loadMetadata=true] Whether to load metadata for the LAS model.
434
436
  * @param {Number[]} [params.origin=[0,0,0]] The model's World-space double-precision 3D origin. Use this to position the model within xeokit's World coordinate system, using double-precision coordinates.
435
437
  * @param {Number[]} [params.position=[0,0,0]] The model single-precision 3D position, relative to the ````origin```` parameter.
@@ -451,7 +453,7 @@ class LASLoaderPlugin extends Plugin {
451
453
  isModel: true
452
454
  }));
453
455
 
454
- if (!params.src && !params.las) {
456
+ if (!params.src && !params.las && !params.manifest && !params.manifestSrc) {
455
457
  this.error("load() param expected: src or las");
456
458
  return sceneModel; // Return new empty model
457
459
  }
@@ -464,18 +466,77 @@ class LASLoaderPlugin extends Plugin {
464
466
  }
465
467
  };
466
468
 
469
+ const spinner = this.viewer.scene.canvas.spinner;
470
+ const done = () => {
471
+ sceneModel.scene.once("tick", () => {
472
+ if (sceneModel.destroyed) {
473
+ return;
474
+ }
475
+ sceneModel.scene.fire("modelLoaded", sceneModel.id); // FIXME: Assumes listeners know order of these two events
476
+ sceneModel.fire("loaded", true, false); // Don't forget the event, for late subscribers
477
+ });
478
+ spinner.processes--;
479
+ }
480
+ const error = (errMsg) => {
481
+ spinner.processes--;
482
+ this.error(errMsg);
483
+ sceneModel.fire("error", errMsg);
484
+ }
485
+
467
486
  if (params.src) {
468
487
  this._loadModel(params.src, params, options, sceneModel);
469
- } else {
470
- const spinner = this.viewer.scene.canvas.spinner;
488
+ } else if(params.las) {
471
489
  spinner.processes++;
472
- this._parseModel(params.las, params, options, sceneModel).then(() => {
473
- spinner.processes--;
474
- }, (errMsg) => {
475
- spinner.processes--;
476
- this.error(errMsg);
477
- sceneModel.fire("error", errMsg);
478
- });
490
+ this._parseModel(params.las, params, options, sceneModel).then(done, error);
491
+ } else if (params.manifest || params.manifestSrc) {
492
+ const baseDir = params.manifestSrc ? getBaseDirectory(params.manifestSrc) : "";
493
+ const loadAllFiles = (lasFiles) => {
494
+ let i = 0;
495
+ const modelsLoaded = new Array(lasFiles.length);
496
+ const loadNext = () => {
497
+ if (sceneModel.destroyed) {
498
+ done();
499
+ } else if (i >= lasFiles.length) {
500
+ return
501
+ } else {
502
+ modelsLoaded[i] = false;
503
+ this._dataSource.getLAS(`${baseDir}${lasFiles[i]}`, (arrayBuffer) => {
504
+ const modelParams = utils.apply(params, {isManifest: true, index: i});
505
+ this._parseModel(arrayBuffer, modelParams, options, sceneModel).then(() => {
506
+ modelsLoaded[modelParams.index] = true;
507
+ let allLoaded = true;
508
+ modelsLoaded.forEach((loaded) => {
509
+ if(!loaded) allLoaded = false;
510
+ })
511
+ if(allLoaded) done();
512
+ });
513
+ i++;
514
+ this.scheduleTask(loadNext, 200);
515
+ }, error);
516
+ }
517
+ }
518
+ loadNext();
519
+ };
520
+ const loadManifestData = (manifestData) => {
521
+ if(sceneModel.destroyed) return;
522
+ const files = manifestData.lasFiles || manifestData.lazFiles;
523
+ if(!files || files.length <= 0) {
524
+ this.error(`load(): Failed to load model manifest - manifest not valid`);
525
+ return;
526
+ }
527
+ loadAllFiles(files);
528
+ }
529
+ if(params.manifestSrc) {
530
+ this._dataSource.getManifest(params.manifestSrc, (manifestData) => {
531
+ loadManifestData(manifestData);
532
+ }, (errMsg) => {
533
+ this.error(errMsg);
534
+ sceneModel.fire("error", errMsg);
535
+ })
536
+ } else {
537
+ const manifestData = params.manifest;
538
+ loadManifestData(manifestData);
539
+ }
479
540
  }
480
541
 
481
542
  return sceneModel;
@@ -486,6 +547,13 @@ class LASLoaderPlugin extends Plugin {
486
547
  spinner.processes++;
487
548
  this._dataSource.getLAS(params.src, (arrayBuffer) => {
488
549
  this._parseModel(arrayBuffer, params, options, sceneModel).then(() => {
550
+ sceneModel.scene.once("tick", () => {
551
+ if (sceneModel.destroyed) {
552
+ return;
553
+ }
554
+ sceneModel.scene.fire("modelLoaded", sceneModel.id); // FIXME: Assumes listeners know order of these two events
555
+ sceneModel.fire("loaded", true, false); // Don't forget the event, for late subscribers
556
+ });
489
557
  spinner.processes--;
490
558
  }, (errMsg) => {
491
559
  spinner.processes--;
@@ -668,7 +736,7 @@ class LASLoaderPlugin extends Plugin {
668
736
  const meshIds = [];
669
737
 
670
738
  for (let i = 0, len = pointsChunks.length; i < len; i++) {
671
- const meshId = `pointsMesh${i}`;
739
+ const meshId = `${params.isManifest ? 'model'+params.index : ''}pointsMesh${i}`;
672
740
  meshIds.push(meshId);
673
741
  sceneModel.createMesh({
674
742
  id: meshId,
@@ -677,30 +745,6 @@ class LASLoaderPlugin extends Plugin {
677
745
  colorsCompressed: (i < colorsChunks.length) ? colorsChunks[i] : null
678
746
  });
679
747
  }
680
- /*
681
- const pointsChunks = chunkArray(positionsValue, MAX_VERTICES * 3);
682
- const colorsChunks = chunkArray(colorsCompressed, MAX_VERTICES * 4);
683
- const meshIds = [];
684
-
685
- for (let i = 0, len = pointsChunks.length; i < len; i++) {
686
-
687
- const geometryId = `geometryMesh${i}`;
688
- const meshId = `pointsMesh${i}`;
689
- meshIds.push(meshId);
690
-
691
- sceneModel.createGeometry({
692
- id: geometryId,
693
- primitive: "points",
694
- positions: pointsChunks[i],
695
- colorsCompressed: (i < colorsChunks.length) ? colorsChunks[i] : null
696
- });
697
-
698
- sceneModel.createMesh({
699
- id: meshId,
700
- geometryId
701
- });
702
- }
703
- */
704
748
 
705
749
  const pointsObjectId = params.entityId || math.createUUID();
706
750
 
@@ -743,14 +787,6 @@ class LASLoaderPlugin extends Plugin {
743
787
  this.viewer.metaScene.createMetaModel(metaModelId, metadata, options);
744
788
  }
745
789
 
746
- sceneModel.scene.once("tick", () => {
747
- if (sceneModel.destroyed) {
748
- return;
749
- }
750
- sceneModel.scene.fire("modelLoaded", sceneModel.id); // FIXME: Assumes listeners know order of these two events
751
- sceneModel.fire("loaded", true, false); // Don't forget the event, for late subscribers
752
- });
753
-
754
790
  resolve();
755
791
  });
756
792
  } catch (e) {
@@ -772,4 +808,14 @@ function chunkArray(array, chunkSize) {
772
808
  return result;
773
809
  }
774
810
 
811
+ function getBaseDirectory(filePath) {
812
+ if (filePath.indexOf('?') > -1) {
813
+ filePath = filePath.split('?')[0];
814
+ }
815
+
816
+ const pathArray = filePath.split('/');
817
+ pathArray.pop(); // Remove the file name or the last segment of the path
818
+ return pathArray.join('/') + '/';
819
+ }
820
+
775
821
  export {LASLoaderPlugin};
@@ -8,6 +8,7 @@ function CubeTextureCanvas(viewer, navCubeScene, cfg = {}) {
8
8
  const cubeColor = "lightgrey";
9
9
  const cubeHighlightColor = cfg.hoverColor || "rgba(0,0,0,0.4)";
10
10
  const textColor = cfg.textColor || "black";
11
+ const parentNode = cfg.canvasElement || document.body;
11
12
 
12
13
  const height = 500;
13
14
  const width = height + (height / 3);
@@ -119,8 +120,7 @@ function CubeTextureCanvas(viewer, navCubeScene, cfg = {}) {
119
120
  this._textureCanvas.style.visibility = "hidden";
120
121
  this._textureCanvas.style["z-index"] = 2000000;
121
122
 
122
- const body = document.getElementsByTagName("body")[0];
123
- body.appendChild(this._textureCanvas);
123
+ parentNode.appendChild(this._textureCanvas);
124
124
 
125
125
  const context = this._textureCanvas.getContext("2d");
126
126
 
@@ -115,7 +115,7 @@ class NavCubePlugin extends Plugin {
115
115
  this._navCubeScene = new Scene(viewer, {
116
116
  canvasId: cfg.canvasId,
117
117
  canvasElement: cfg.canvasElement,
118
- transparent: true
118
+ transparent: true,
119
119
  });
120
120
 
121
121
  this._navCubeCanvas = this._navCubeScene.canvas.canvas;
@@ -361,7 +361,7 @@ class NavCubePlugin extends Plugin {
361
361
  }
362
362
  });
363
363
 
364
- document.addEventListener("mouseup", self._onMouseUp = function (e) {
364
+ self._navCubeCanvas.addEventListener("mouseup", self._onMouseUp = function (e) {
365
365
  if (e.which !== 1) {// Left button
366
366
  return;
367
367
  }
@@ -378,7 +378,7 @@ class NavCubePlugin extends Plugin {
378
378
  if (hit.uv) {
379
379
  var areaId = self._cubeTextureCanvas.getArea(hit.uv);
380
380
  if (areaId >= 0) {
381
- document.body.style.cursor = "pointer";
381
+ self._navCubeCanvas.style.cursor = "pointer";
382
382
  if (lastAreaId >= 0) {
383
383
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
384
384
  self._repaint();
@@ -404,7 +404,7 @@ class NavCubePlugin extends Plugin {
404
404
  self._repaint();
405
405
  lastAreaId = -1;
406
406
  }
407
- document.body.style.cursor = "pointer";
407
+ self._navCubeCanvas.style.cursor = "pointer";
408
408
  if (lastAreaId >= 0) {
409
409
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
410
410
  self._repaint();
@@ -423,7 +423,7 @@ class NavCubePlugin extends Plugin {
423
423
  }
424
424
  });
425
425
 
426
- document.addEventListener("mousemove", self._onMouseMove = function (e) {
426
+ self._navCubeCanvas.addEventListener("mousemove", self._onMouseMove = function (e) {
427
427
  if (lastAreaId >= 0) {
428
428
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
429
429
  self._repaint();
@@ -435,7 +435,7 @@ class NavCubePlugin extends Plugin {
435
435
  if (down) {
436
436
  var posX = e.clientX;
437
437
  var posY = e.clientY;
438
- document.body.style.cursor = "move";
438
+ self._navCubeCanvas.style.cursor = "move";
439
439
  actionMove(posX, posY);
440
440
  return;
441
441
  }
@@ -449,7 +449,7 @@ class NavCubePlugin extends Plugin {
449
449
  });
450
450
  if (hit) {
451
451
  if (hit.uv) {
452
- document.body.style.cursor = "pointer";
452
+ self._navCubeCanvas.style.cursor = "pointer";
453
453
  var areaId = self._cubeTextureCanvas.getArea(hit.uv);
454
454
  if (areaId === lastAreaId) {
455
455
  return;
@@ -464,7 +464,7 @@ class NavCubePlugin extends Plugin {
464
464
  }
465
465
  }
466
466
  } else {
467
- document.body.style.cursor = "default";
467
+ self._navCubeCanvas.style.cursor = "default";
468
468
  if (lastAreaId >= 0) {
469
469
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
470
470
  self._repaint();
@@ -727,8 +727,8 @@ class NavCubePlugin extends Plugin {
727
727
  this._navCubeCanvas.removeEventListener("mouseleave", this._onMouseLeave);
728
728
  this._navCubeCanvas.removeEventListener("mousedown", this._onMouseDown);
729
729
 
730
- document.removeEventListener("mousemove", this._onMouseMove);
731
- document.removeEventListener("mouseup", this._onMouseUp);
730
+ this._navCubeCanvas.removeEventListener("mousemove", this._onMouseMove);
731
+ this._navCubeCanvas.removeEventListener("mouseup", this._onMouseUp);
732
732
 
733
733
  this._navCubeCanvas = null;
734
734
  this._cubeTextureCanvas.destroy();
@@ -482,8 +482,8 @@ class Control {
482
482
  const canvasPos = math.vec2();
483
483
 
484
484
  const copyCanvasPos = (event, vec2) => {
485
- vec2[0] = event.clientX;
486
- vec2[1] = event.clientY;
485
+ vec2[0] = event.pageX;
486
+ vec2[1] = event.pageY;
487
487
  transformToNode(canvas.ownerDocument.documentElement, canvas, vec2);
488
488
  };
489
489
 
@@ -464,7 +464,8 @@ export class TreeViewPlugin extends Plugin {
464
464
  const indeterminate = this._showIndeterminate
465
465
  && parent.numVisibleEntities > 0
466
466
  && parent.numVisibleEntities < parent.numEntities;
467
- this._renderService.setCheckbox(parent.nodeId, (parent.numVisibleEntities > 0), indeterminate);
467
+ parent.checked = parent.numVisibleEntities > 0;
468
+ this._renderService.setCheckbox(parent.nodeId, parent.checked, indeterminate);
468
469
 
469
470
  parent = parent.parent;
470
471
  }
@@ -539,7 +540,6 @@ export class TreeViewPlugin extends Plugin {
539
540
 
540
541
  let parent = checkedNode.parent;
541
542
  while (parent) {
542
- parent.checked = visible;
543
543
 
544
544
  if (visible) {
545
545
  parent.numVisibleEntities += numUpdated;
@@ -549,7 +549,8 @@ export class TreeViewPlugin extends Plugin {
549
549
  const indeterminate = this._showIndeterminate
550
550
  && parent.numVisibleEntities > 0
551
551
  && parent.numVisibleEntities < parent.numEntities;
552
- this._renderService.setCheckbox(parent.nodeId, (parent.numVisibleEntities > 0), indeterminate);
552
+ parent.checked = parent.numVisibleEntities > 0;
553
+ this._renderService.setCheckbox(parent.nodeId, parent.checked, indeterminate);
553
554
 
554
555
  parent = parent.parent;
555
556
  }