@xeokit/xeokit-sdk 2.3.7 → 2.4.0-alpha

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.
@@ -42,7 +42,7 @@ class Map$1 {
42
42
  }
43
43
  }
44
44
 
45
- const idMap$1 = new Map$1();
45
+ const idMap = new Map$1();
46
46
 
47
47
  /**
48
48
  * Internal data class that represents the state of a menu or a submenu.
@@ -326,7 +326,7 @@ class ContextMenu {
326
326
  */
327
327
  constructor(cfg = {}) {
328
328
 
329
- this._id = idMap$1.addItem();
329
+ this._id = idMap.addItem();
330
330
  this._context = null;
331
331
  this._enabled = false; // True when the ContextMenu is enabled
332
332
  this._itemsCfg = []; // Items as given as configs
@@ -537,7 +537,7 @@ class ContextMenu {
537
537
  this._context = null;
538
538
  this._clear();
539
539
  if (this._id !== null) {
540
- idMap$1.removeItem(this._id);
540
+ idMap.removeItem(this._id);
541
541
  this._id = null;
542
542
  }
543
543
  }
@@ -30788,7 +30788,7 @@ class Bitmap extends Component {
30788
30788
  * [[Run this example](http://xeokit.github.io/xeokit-sdk/examples/#LineSet_grid)]
30789
30789
  *
30790
30790
  * ````javascript
30791
- * import {Viewer, XKTLoaderPlugin, LineSet, buildGridGeometry} from "../dist/xeokit-sdk.es.js";
30791
+ * import {Viewer, XKTLoaderPlugin, LineSet, buildGridGeometry} from "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/xeokit-sdk.es.min.js";
30792
30792
  *
30793
30793
  * const viewer = new Viewer({
30794
30794
  * canvasId: "myCanvas",
@@ -33757,7 +33757,7 @@ class Marker extends Component {
33757
33757
  * [[Run this example](http://xeokit.github.io/xeokit-sdk/examples/#markers_SpriteMarker)]
33758
33758
  *
33759
33759
  * ```` javascript
33760
- * import {Viewer, SpriteMarker } from "./../dist/xeokit-sdk.es.js";
33760
+ * import {Viewer, SpriteMarker } from "./https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/xeokit-sdk.es.min.js";
33761
33761
  *
33762
33762
  * const viewer = new Viewer({
33763
33763
  * canvasId: "myCanvas",
@@ -61010,7 +61010,7 @@ let activeTranscoders = 0;
61010
61010
  *
61011
61011
  * const textureTranscoder = new KTX2TextureTranscoder({
61012
61012
  * viewer,
61013
- * transcoderPath: "./../dist/basis/" // <------ Path to Basis Universal transcoder
61013
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to Basis Universal transcoder
61014
61014
  * });
61015
61015
  *
61016
61016
  * const xktLoader = new XKTLoaderPlugin(viewer, {
@@ -61051,7 +61051,7 @@ let activeTranscoders = 0;
61051
61051
  *
61052
61052
  * const textureTranscoder = new KTX2TextureTranscoder({
61053
61053
  * viewer,
61054
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
61054
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to BasisU transcoder module
61055
61055
  * });
61056
61056
  *
61057
61057
  * const vboSceneModel = new VBOSceneModel(viewer.scene, {
@@ -61117,7 +61117,7 @@ let activeTranscoders = 0;
61117
61117
  *
61118
61118
  * const textureTranscoder = new KTX2TextureTranscoder({
61119
61119
  * viewer,
61120
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
61120
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to BasisU transcoder module
61121
61121
  * });
61122
61122
  *
61123
61123
  * const vboSceneModel = new VBOSceneModel(viewer.scene, {
@@ -62499,7 +62499,7 @@ const defaultTextureSetId = "defaultTextureSet";
62499
62499
  *
62500
62500
  * const textureTranscoder = new KTX2TextureTranscoder({
62501
62501
  * viewer,
62502
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
62502
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to BasisU transcoder module
62503
62503
  * });
62504
62504
  *
62505
62505
  * const vboSceneModel = new VBOSceneModel(viewer.scene, {
@@ -62563,7 +62563,7 @@ const defaultTextureSetId = "defaultTextureSet";
62563
62563
  *
62564
62564
  * const textureTranscoder = new KTX2TextureTranscoder({
62565
62565
  * viewer,
62566
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
62566
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to BasisU transcoder module
62567
62567
  * });
62568
62568
  *
62569
62569
  * const vboSceneModel = new VBOSceneModel(viewer.scene, {
@@ -65652,7 +65652,7 @@ class Dot {
65652
65652
  dotStyle.left = 0 + "px";
65653
65653
  dotStyle["box-shadow"] = "0 2px 5px 0 #182A3D;";
65654
65654
  dotStyle["opacity"] = 1.0;
65655
- dotStyle["pointer-events"] = "none";
65655
+ dotStyle["pointer-events"] = "all";
65656
65656
  if (cfg.onContextMenu) ;
65657
65657
  parentElement.appendChild(dot);
65658
65658
 
@@ -65668,7 +65668,7 @@ class Dot {
65668
65668
  dotClickableStyle.top = 0 + "px";
65669
65669
  dotClickableStyle.left = 0 + "px";
65670
65670
  dotClickableStyle["opacity"] = 0.0;
65671
- dotClickableStyle["pointer-events"] = "all";
65671
+ dotClickableStyle["pointer-events"] = "none";
65672
65672
  if (cfg.onContextMenu) ;
65673
65673
  parentElement.appendChild(dotClickable);
65674
65674
 
@@ -66016,7 +66016,7 @@ class AngleMeasurement extends Component {
66016
66016
 
66017
66017
  this._angleVisible = false;
66018
66018
  this._labelsVisible = false;
66019
- this._clickable = true;
66019
+ this._clickable = false;
66020
66020
 
66021
66021
  this._originMarker.on("worldPos", (value) => {
66022
66022
  this._originWorld.set(value || [0, 0, 0]);
@@ -67183,6 +67183,7 @@ class AngleMeasurementsPlugin extends Plugin {
67183
67183
  this._onMouseOver = (event, measurement) => {
67184
67184
  this.fire("mouseOver", {
67185
67185
  plugin: this,
67186
+ angleMeasurement: measurement,
67186
67187
  measurement,
67187
67188
  event
67188
67189
  });
@@ -67191,6 +67192,7 @@ class AngleMeasurementsPlugin extends Plugin {
67191
67192
  this._onMouseLeave = (event, measurement) => {
67192
67193
  this.fire("mouseLeave", {
67193
67194
  plugin: this,
67195
+ angleMeasurement: measurement,
67194
67196
  measurement,
67195
67197
  event
67196
67198
  });
@@ -67199,6 +67201,7 @@ class AngleMeasurementsPlugin extends Plugin {
67199
67201
  this._onContextMenu = (event, measurement) => {
67200
67202
  this.fire("contextMenu", {
67201
67203
  plugin: this,
67204
+ angleMeasurement: measurement,
67202
67205
  measurement,
67203
67206
  event
67204
67207
  });
@@ -80416,7 +80419,9 @@ class BCFViewpointsPlugin extends Plugin {
80416
80419
  for (let id in sectionPlanes) {
80417
80420
  if (sectionPlanes.hasOwnProperty(id)) {
80418
80421
  let sectionPlane = sectionPlanes[id];
80419
-
80422
+ if (!sectionPlane.active) {
80423
+ continue;
80424
+ }
80420
80425
  let location = sectionPlane.pos;
80421
80426
 
80422
80427
  let direction;
@@ -81144,7 +81149,7 @@ class DistanceMeasurement extends Component {
81144
81149
  this._zAxisVisible = false;
81145
81150
  this._axisEnabled = true;
81146
81151
  this._labelsVisible = false;
81147
- this._clickable = true;
81152
+ this._clickable = false;
81148
81153
 
81149
81154
  this._originMarker.on("worldPos", (value) => {
81150
81155
  this._originWorld.set(value || [0, 0, 0]);
@@ -81253,10 +81258,10 @@ class DistanceMeasurement extends Component {
81253
81258
 
81254
81259
  if (vpz1 > near || vpz2 > near) {
81255
81260
 
81256
- this._xAxisLabel.setVisible(false);
81257
- this._yAxisLabel.setVisible(false);
81258
- this._zAxisLabel.setVisible(false);
81259
- this._lengthLabel.setVisible(false);
81261
+ this._xAxisLabel.setCulled(true);
81262
+ this._yAxisLabel.setCulled(true);
81263
+ this._zAxisLabel.setCulled(true);
81264
+ this._lengthLabel.setCulled(true);
81260
81265
 
81261
81266
  this._xAxisWire.setVisible(false);
81262
81267
  this._yAxisWire.setVisible(false);
@@ -81360,6 +81365,11 @@ class DistanceMeasurement extends Component {
81360
81365
  }
81361
81366
  }
81362
81367
 
81368
+ // this._xAxisLabel.setVisible(this.axisVisible && this.xAxisVisible);
81369
+ // this._yAxisLabel.setVisible(this.axisVisible && this.yAxisVisible);
81370
+ // this._zAxisLabel.setVisible(this.axisVisible && this.zAxisVisible);
81371
+ // this._lengthLabel.setVisible(false);
81372
+
81363
81373
  this._originDot.setVisible(this._visible && this._originVisible);
81364
81374
  this._targetDot.setVisible(this._visible && this._targetVisible);
81365
81375
 
@@ -86368,7 +86378,7 @@ class MetaModel {
86368
86378
  /**
86369
86379
  * @private
86370
86380
  */
86371
- constructor(metaScene, id, projectId, revisionId, author, createdAt, creatingApplication, schema, propertySets, rootMetaObject) {
86381
+ constructor(metaScene, id, projectId, revisionId, author, createdAt, creatingApplication, schema, propertySets) {
86372
86382
 
86373
86383
  /**
86374
86384
  * Globally-unique ID.
@@ -86460,8 +86470,17 @@ class MetaModel {
86460
86470
  *
86461
86471
  * @property rootMetaObject
86462
86472
  * @type {MetaObject}
86473
+ * @deprecated
86474
+ */
86475
+ this.rootMetaObject = null;
86476
+
86477
+ /**
86478
+ * The root {@link MetaObject}s in this MetaModel's composition structure hierarchy.
86479
+ *
86480
+ * @property rootMetaObject
86481
+ * @type {MetaObject[]}
86463
86482
  */
86464
- this.rootMetaObject = rootMetaObject;
86483
+ this.rootMetaObjects = [];
86465
86484
  }
86466
86485
 
86467
86486
  getJSON() {
@@ -86527,9 +86546,16 @@ class MetaObject {
86527
86546
  *
86528
86547
  * @property metaModel
86529
86548
  * @type {MetaModel}
86549
+ * @deprecated
86530
86550
  */
86531
86551
  this.metaModel = metaModel;
86532
86552
 
86553
+ /**
86554
+ * The MetaModels that share this MetaObject.
86555
+ * @type {MetaModel[]}
86556
+ */
86557
+ this.metaModels = [metaModel];
86558
+
86533
86559
  /**
86534
86560
  * Globally-unique ID.
86535
86561
  *
@@ -86889,466 +86915,17 @@ class MetaScene {
86889
86915
  this.metaObjectsByType = {};
86890
86916
 
86891
86917
  /**
86892
- * Tracks number of MetaObjects of each type.
86893
- * @private
86894
- */
86895
- this._typeCounts = {};
86896
-
86897
- /**
86898
- * Subscriptions to events sent with {@link fire}.
86899
- * @private
86900
- */
86901
- this._eventSubs = {};
86902
- }
86903
-
86904
- /**
86905
- * Subscribes to an event fired at this Viewer.
86906
- *
86907
- * @param {String} event The event
86908
- * @param {Function} callback Callback fired on the event
86909
- */
86910
- on(event, callback) {
86911
- let subs = this._eventSubs[event];
86912
- if (!subs) {
86913
- subs = [];
86914
- this._eventSubs[event] = subs;
86915
- }
86916
- subs.push(callback);
86917
- }
86918
-
86919
- /**
86920
- * Fires an event at this Viewer.
86921
- *
86922
- * @param {String} event Event name
86923
- * @param {Object} value Event parameters
86924
- */
86925
- fire(event, value) {
86926
- const subs = this._eventSubs[event];
86927
- if (subs) {
86928
- for (let i = 0, len = subs.length; i < len; i++) {
86929
- subs[i](value);
86930
- }
86931
- }
86932
- }
86933
-
86934
- /**
86935
- * Unsubscribes from an event fired at this Viewer.
86936
- * @param event
86937
- */
86938
- off(event) { // TODO
86939
-
86940
- }
86941
-
86942
- /**
86943
- * Creates a {@link MetaModel} in this MetaScene.
86944
- *
86945
- * The MetaModel will contain a hierarchy of {@link MetaObject}s, created from the
86946
- * meta objects in ````metaModelData````.
86947
- *
86948
- * The meta object hierarchy in ````metaModelData```` is expected to be non-cyclic, with a single root. If the meta
86949
- * objects are cyclic, then this method will log an error and attempt to recover by creating a dummy root MetaObject
86950
- * of type "Model" and connecting all other MetaObjects as its direct children. If the meta objects contain multiple
86951
- * roots, then this method similarly attempts to recover by creating a dummy root MetaObject of type "Model" and
86952
- * connecting all the root MetaObjects as its children.
86953
- *
86954
- * @param {String} modelId ID for the new {@link MetaModel}, which will have {@link MetaModel#id} set to this value.
86955
- * @param {Object} metaModelData Data for the {@link MetaModel}.
86956
- * @param {Object} [options] Options for creating the {@link MetaModel}.
86957
- * @param {Object} [options.includeTypes] When provided, only create {@link MetaObject}s with types in this list.
86958
- * @param {Object} [options.excludeTypes] When provided, never create {@link MetaObject}s with types in this list.
86959
- * @param {Boolean} [options.globalizeObjectIds=false] Whether to globalize each {@link MetaObject#id}. Set this ````true```` when you need to load multiple instances of the same meta model, to avoid ID clashes between the meta objects in the different instances.
86960
- * @returns {MetaModel} The new MetaModel.
86961
- */
86962
- createMetaModel(modelId, metaModelData, options = {}) {
86963
-
86964
- const projectId = metaModelData.projectId || "none";
86965
- const revisionId = metaModelData.revisionId || "none";
86966
- const newPropertySets = metaModelData.propertySets || [];
86967
- const newObjects = metaModelData.metaObjects || [];
86968
- const author = metaModelData.author;
86969
- const createdAt = metaModelData.createdAt;
86970
- const creatingApplication = metaModelData.creatingApplication;
86971
- const schema = metaModelData.schema;
86972
- // if (options.excludeTypes) {
86973
- // excludeTypes = {};
86974
- // for (let i = 0, len = options.excludeTypes.length; i < len; i++) {
86975
- // includeTypes[options.excludeTypes[i]] = true;
86976
- // }
86977
- // }
86978
-
86979
- const metaModel = new MetaModel(this, modelId, projectId, revisionId, author, createdAt, creatingApplication, schema, [], null);
86980
-
86981
- this.metaModels[modelId] = metaModel;
86982
-
86983
- for (let i = 0, len = newPropertySets.length; i < len; i++) {
86984
- const propertySetCfg = newPropertySets[i];
86985
- const propertySetId = propertySetCfg.id;
86986
- const propertySet = new PropertySet(propertySetId, propertySetCfg.originalSystemId, propertySetCfg.name, propertySetCfg.type, propertySetCfg.properties);
86987
- metaModel.propertySets[propertySetId] = propertySet;
86988
- this.propertySets[propertySetId] = propertySet;
86989
- }
86990
-
86991
- const rootMetaObjects = [];
86992
-
86993
- for (let i = 0, len = newObjects.length; i < len; i++) {
86994
- const newObject = newObjects[i];
86995
- if (newObject.parent === undefined || newObject.parent === null) {
86996
- rootMetaObjects.push(newObject);
86997
- }
86998
- }
86999
-
87000
- if (rootMetaObjects.length === 0) {
87001
- this.scene.error("Cyclic containment hierarchy found in metamodel - will flatten the hierarchy and insert fake 'Model' root");
87002
- const fakeRoot = {
87003
- "id": modelId + ".fakeRoot",
87004
- "name": modelId,
87005
- "type": "Model",
87006
- "parent": null
87007
- };
87008
- for (let i = 0, len = newObjects.length; i < len; i++) {
87009
- newObjects[i].parent = fakeRoot.id;
87010
- }
87011
- newObjects.push(fakeRoot);
87012
- }
87013
-
87014
- if (rootMetaObjects.length > 1) {
87015
- this.scene.error("Multiple containment hierarchy root found in metamodel - will insert fake 'Model' root");
87016
- const fakeRoot = {
87017
- "id": modelId + ".fakeRoot",
87018
- "name": modelId,
87019
- "type": "Model",
87020
- "parent": null
87021
- };
87022
- newObjects.push(fakeRoot);
87023
- for (let i = 0, len = rootMetaObjects.length; i < len; i++) {
87024
- rootMetaObjects[i].parent = fakeRoot.id;
87025
- }
87026
- }
87027
-
87028
- for (let i = 0, len = newObjects.length; i < len; i++) {
87029
- const newObject = newObjects[i];
87030
- const type = newObject.type;
87031
- const objectId = options.globalizeObjectIds ? math.globalizeObjectId(modelId, newObject.id) : newObject.id;
87032
- const originalSystemId = newObject.id;
87033
- const name = newObject.name;
87034
- const propertySets = [];
87035
- if (newObject.propertySetIds && newObject.propertySetIds.length > 0) {
87036
- for (let j = 0, lenj = newObject.propertySetIds.length; j < lenj; j++) {
87037
- const propertySetId = newObject.propertySetIds[j];
87038
- const propertySet = metaModel.propertySets[propertySetId];
87039
- if (propertySet) {
87040
- propertySets.push(propertySet);
87041
- }
87042
- }
87043
- }
87044
- const parent = null;
87045
- const children = null;
87046
- const external = newObject.external;
87047
- const metaObject = new MetaObject(metaModel, objectId, originalSystemId, name, type, propertySets, parent, children, external);
87048
- this.metaObjects[objectId] = metaObject;
87049
- (this.metaObjectsByType[type] || (this.metaObjectsByType[type] = {}))[objectId] = metaObject;
87050
- if (this._typeCounts[type] === undefined) {
87051
- this._typeCounts[type] = 1;
87052
- } else {
87053
- this._typeCounts[type]++;
87054
- }
87055
- }
87056
-
87057
- for (let i = 0, len = newObjects.length; i < len; i++) {
87058
- const newObject = newObjects[i];
87059
- const objectId = options.globalizeObjectIds ? math.globalizeObjectId(modelId, newObject.id) : newObject.id;
87060
- const metaObject = this.metaObjects[objectId];
87061
- if (!metaObject) {
87062
- continue;
87063
- }
87064
- if (newObject.parent === undefined || newObject.parent === null) {
87065
- metaModel.rootMetaObject = metaObject;
87066
- } else if (newObject.parent) {
87067
- const parentId = options.globalizeObjectIds ? math.globalizeObjectId(modelId, newObject.parent) : newObject.parent;
87068
- let parentMetaObject = this.metaObjects[parentId];
87069
- if (parentMetaObject) {
87070
- metaObject.parent = parentMetaObject;
87071
- parentMetaObject.children = parentMetaObject.children || [];
87072
- parentMetaObject.children.push(metaObject);
87073
- }
87074
- }
87075
- }
87076
-
87077
- this.fire("metaModelCreated", modelId);
87078
- return metaModel;
87079
- }
87080
-
87081
- /**
87082
- * Removes a {@link MetaModel} from this MetaScene.
87083
- *
87084
- * Fires a "metaModelDestroyed" event with the value of the {@link MetaModel#id}.
87085
- *
87086
- * @param {String} id ID of the target {@link MetaModel}.
87087
- */
87088
- destroyMetaModel(id) {
87089
- const metaModel = this.metaModels[id];
87090
- if (!metaModel) {
87091
- return;
87092
- }
87093
- this._removeMetaModel(metaModel);
87094
- this.fire("metaModelDestroyed", id);
87095
- }
87096
-
87097
- _removeMetaModel(metaModel) {
87098
- const metaObjects = this.metaObjects;
87099
- const metaObjectsByType = this.metaObjectsByType;
87100
- let visit = (metaObject) => {
87101
- delete metaObjects[metaObject.id];
87102
- const types = metaObjectsByType[metaObject.type];
87103
- if (types && types[metaObject.id]) {
87104
- delete types[metaObject.id];
87105
- if (--this._typeCounts[metaObject.type] === 0) {
87106
- delete this._typeCounts[metaObject.type];
87107
- delete metaObjectsByType[metaObject.type];
87108
- }
87109
- }
87110
- const children = metaObject.children;
87111
- if (children) {
87112
- for (let i = 0, len = children.length; i < len; i++) {
87113
- const childMetaObject = children[i];
87114
- visit(childMetaObject);
87115
- }
87116
- }
87117
- };
87118
- visit(metaModel.rootMetaObject);
87119
- for (let propertySetId in metaModel.propertySets) {
87120
- if (metaModel.propertySets.hasOwnProperty(propertySetId)) {
87121
- delete this.propertySets[propertySetId];
87122
- }
87123
- }
87124
- delete this.metaModels[metaModel.id];
87125
- }
87126
-
87127
- /**
87128
- * Gets the {@link MetaObject#id}s of the {@link MetaObject}s that have the given {@link MetaObject#type}.
87129
- *
87130
- * @param {String} type The type.
87131
- * @returns {String[]} Array of {@link MetaObject#id}s.
87132
- */
87133
- getObjectIDsByType(type) {
87134
- const metaObjects = this.metaObjectsByType[type];
87135
- return metaObjects ? Object.keys(metaObjects) : [];
87136
- }
87137
-
87138
- /**
87139
- * Gets the {@link MetaObject#id}s of the {@link MetaObject}s within the given subtree.
87140
- *
87141
- * @param {String} id ID of the root {@link MetaObject} of the given subtree.
87142
- * @param {String[]} [includeTypes] Optional list of types to include.
87143
- * @param {String[]} [excludeTypes] Optional list of types to exclude.
87144
- * @returns {String[]} Array of {@link MetaObject#id}s.
87145
- */
87146
- getObjectIDsInSubtree(id, includeTypes, excludeTypes) {
87147
- const list = [];
87148
- const metaObject = this.metaObjects[id];
87149
- const includeMask = (includeTypes && includeTypes.length > 0) ? arrayToMap(includeTypes) : null;
87150
- const excludeMask = (excludeTypes && excludeTypes.length > 0) ? arrayToMap(excludeTypes) : null;
87151
-
87152
- function visit(metaObject) {
87153
- if (!metaObject) {
87154
- return;
87155
- }
87156
- var include = true;
87157
- if (excludeMask && excludeMask[metaObject.type]) {
87158
- include = false;
87159
- } else if (includeMask && (!includeMask[metaObject.type])) {
87160
- include = false;
87161
- }
87162
- if (include) {
87163
- list.push(metaObject.id);
87164
- }
87165
- const children = metaObject.children;
87166
- if (children) {
87167
- for (var i = 0, len = children.length; i < len; i++) {
87168
- visit(children[i]);
87169
- }
87170
- }
87171
- }
87172
-
87173
- visit(metaObject);
87174
- return list;
87175
- }
87176
-
87177
- /**
87178
- * Iterates over the {@link MetaObject}s within the subtree.
87179
- *
87180
- * @param {String} id ID of root {@link MetaObject}.
87181
- * @param {Function} callback Callback fired at each {@link MetaObject}.
87182
- */
87183
- withMetaObjectsInSubtree(id, callback) {
87184
- const metaObject = this.metaObjects[id];
87185
- if (!metaObject) {
87186
- return;
87187
- }
87188
- metaObject.withMetaObjectsInSubtree(callback);
87189
- }
87190
- }
87191
-
87192
- function arrayToMap(array) {
87193
- const map = {};
87194
- for (var i = 0, len = array.length; i < len; i++) {
87195
- map[array[i]] = true;
87196
- }
87197
- return map;
87198
- }
87199
-
87200
- /**
87201
- * The 3D Viewer at the heart of the xeokit SDK.
87202
- *
87203
- * * A Viewer wraps a single {@link Scene}
87204
- * * Add {@link Plugin}s to a Viewer to extend its functionality.
87205
- * * {@link Viewer#metaScene} holds metadata about models in the
87206
- * Viewer's {@link MetaScene}.
87207
- * * Use {@link Viewer#cameraFlight} to fly or jump the {@link Scene}'s
87208
- * {@link Camera} to target positions, boundaries or {@link Entity}s.
87209
- *
87210
- * @public
87211
- */
87212
- class Viewer {
87213
-
87214
- /**
87215
- * @constructor
87216
- * @param {Object} cfg Viewer configuration.
87217
- * @param {String} [cfg.id] Optional ID for this Viewer, defaults to the ID of {@link Viewer#scene}, which xeokit automatically generates.
87218
- * @param {String} [cfg.canvasId] ID of an existing HTML canvas for the {@link Viewer#scene} - either this or canvasElement is mandatory. When both values are given, the element reference is always preferred to the ID.
87219
- * @param {HTMLCanvasElement} [cfg.canvasElement] Reference of an existing HTML canvas for the {@link Viewer#scene} - either this or canvasId is mandatory. When both values are given, the element reference is always preferred to the ID.
87220
- * @param {HTMLElement} [cfg.keyboardEventsElement] Optional reference to HTML element on which key events should be handled. Defaults to the HTML Document.
87221
- * @param {String} [cfg.spinnerElementId] ID of existing HTML element to show the {@link Spinner} - internally creates a default element automatically if this is omitted.
87222
- * @param {Number} [cfg.passes=1] The number of times the {@link Viewer#scene} renders per frame.
87223
- * @param {Boolean} [cfg.clearEachPass=false] When doing multiple passes per frame, specifies if to clear the canvas before each pass (true) or just before the first pass (false).
87224
- * @param {Boolean} [cfg.preserveDrawingBuffer=true] Whether or not to preserve the WebGL drawing buffer. This needs to be ````true```` for {@link Viewer#getSnapshot} to work.
87225
- * @param {Boolean} [cfg.transparent=true] Whether or not the canvas is transparent.
87226
- * @param {Boolean} [cfg.premultipliedAlpha=false] Whether or not you want alpha composition with premultiplied alpha. Highlighting and selection works best when this is ````false````.
87227
- * @param {Boolean} [cfg.gammaInput=true] When true, expects that all textures and colors are premultiplied gamma.
87228
- * @param {Boolean} [cfg.gammaOutput=false] Whether or not to render with pre-multiplied gama.
87229
- * @param {Number} [cfg.gammaFactor=2.2] The gamma factor to use when rendering with pre-multiplied gamma.
87230
- * @param {Number[]} [cfg.backgroundColor=[1,1,1]] Sets the canvas background color to use when ````transparent```` is false.
87231
- * @param {Boolean} [cfg.backgroundColorFromAmbientLight=true] When ````transparent```` is false, set this ````true````
87232
- * to derive the canvas background color from {@link AmbientLight#color}, or ````false```` to set the canvas background to ````backgroundColor````.
87233
- * @param {String} [cfg.units="meters"] The measurement unit type. Accepted values are ````"meters"````, ````"metres"````, , ````"centimeters"````, ````"centimetres"````, ````"millimeters"````, ````"millimetres"````, ````"yards"````, ````"feet"```` and ````"inches"````.
87234
- * @param {Number} [cfg.scale=1] The number of Real-space units in each World-space coordinate system unit.
87235
- * @param {Number[]} [cfg.origin=[0,0,0]] The Real-space 3D origin, in current measurement units, at which the World-space coordinate origin ````[0,0,0]```` sits.
87236
- * @param {Boolean} [cfg.saoEnabled=false] Whether to enable Scalable Ambient Obscurance (SAO) effect. See {@link SAO} for more info.
87237
- * @param {Boolean} [cfg.antialias=true] Whether to enable anti-aliasing.
87238
- * @throws {String} Throws an exception when both canvasId or canvasElement are missing or they aren't pointing to a valid HTMLCanvasElement.
87239
- * @param {Boolean} [cfg.alphaDepthMask=true] Whether writing into the depth buffer is enabled or disabled when rendering transparent objects.
87240
- * @param {Boolean} [cfg.entityOffsetsEnabled=false] Whether to enable {@link Entity#offset}. For best performance, only set this ````true```` when you need to use {@link Entity#offset}.
87241
- * @param {Boolean} [cfg.pickSurfacePrecisionEnabled=false] Whether to enable full-precision accuracy when surface picking with {@link Scene#pick}. Note that when ````true````, this configuration will increase the amount of browser memory used by the Viewer. The ````pickSurfacePrecision```` option for ````Scene#pick```` only works if this is set ````true````.
87242
- * @param {Boolean} [cfg.logarithmicDepthBufferEnabled=false] Whether to enable logarithmic depth buffer. When this is true,
87243
- * you can set huge values for {@link Perspective#far} and {@link Ortho#far}, to push the far clipping plane back so
87244
- * that it does not clip huge models.
87245
- * @param {Boolean} [cfg.colorTextureEnabled=true] Whether to enable base color texture rendering.
87246
- * @param {Boolean} [cfg.pbrEnabled=false] Whether to enable physically-based rendering.
87247
- * @param {LocaleService} [cfg.localeService=null] Optional locale-based translation service.
87248
- */
87249
- constructor(cfg) {
87250
-
87251
- /**
87252
- * The Viewer's current language setting.
87253
- * @property language
87254
- * @deprecated
87255
- * @type {String}
87256
- */
87257
- this.language = "en";
87258
-
87259
- /**
87260
- * The viewer's locale service.
87261
- *
87262
- * This is configured via the Viewer's constructor.
86918
+ * The root {@link MetaObject}s belonging to this MetaScene, each mapped to its {@link MetaObject#id}.
87263
86919
  *
87264
- * By default, this service will be an instance of {@link LocaleService}, which will just return
87265
- * null translations for all given strings and phrases.
87266
- *
87267
- * @property localeService
87268
- * @type {LocaleService}
87269
- * @since 2.0
87270
- */
87271
- this.localeService = cfg.localeService || new LocaleService();
87272
-
87273
- /**
87274
- * The Viewer's {@link Scene}.
87275
- * @property scene
87276
- * @type {Scene}
87277
- */
87278
- this.scene = new Scene(this, {
87279
- canvasId: cfg.canvasId,
87280
- canvasElement: cfg.canvasElement,
87281
- keyboardEventsElement: cfg.keyboardEventsElement,
87282
- contextAttr: {
87283
- preserveDrawingBuffer: cfg.preserveDrawingBuffer !== false,
87284
- premultipliedAlpha: (!!cfg.premultipliedAlpha),
87285
- antialias: (cfg.antialias !== false)
87286
- },
87287
- spinnerElementId: cfg.spinnerElementId,
87288
- transparent: (cfg.transparent !== false),
87289
- gammaInput: true,
87290
- gammaOutput: false,
87291
- backgroundColor: cfg.backgroundColor,
87292
- backgroundColorFromAmbientLight: cfg.backgroundColorFromAmbientLight,
87293
- ticksPerRender: 1,
87294
- ticksPerOcclusionTest: 20,
87295
- units: cfg.units,
87296
- scale: cfg.scale,
87297
- origin: cfg.origin,
87298
- saoEnabled: cfg.saoEnabled,
87299
- alphaDepthMask: (cfg.alphaDepthMask !== false),
87300
- entityOffsetsEnabled: (!!cfg.entityOffsetsEnabled),
87301
- pickSurfacePrecisionEnabled: (!!cfg.pickSurfacePrecisionEnabled),
87302
- logarithmicDepthBufferEnabled: (!!cfg.logarithmicDepthBufferEnabled),
87303
- pbrEnabled: (!!cfg.pbrEnabled),
87304
- colorTextureEnabled: (cfg.colorTextureEnabled !== false)
87305
- });
87306
-
87307
- /**
87308
- * Metadata about the {@link Scene} and the models and objects within it.
87309
- * @property metaScene
87310
- * @type {MetaScene}
87311
- * @readonly
87312
- */
87313
- this.metaScene = new MetaScene(this, this.scene);
87314
-
87315
- /**
87316
- * The Viewer's ID.
87317
- * @property id
87318
- *
87319
- * @type {String|Number}
87320
- */
87321
- this.id = cfg.id || this.scene.id;
87322
-
87323
- /**
87324
- * The Viewer's {@link Camera}. This is also found on {@link Scene#camera}.
87325
- * @property camera
87326
- * @type {Camera}
87327
- */
87328
- this.camera = this.scene.camera;
87329
-
87330
- /**
87331
- * The Viewer's {@link CameraFlightAnimation}, which
87332
- * is used to fly the {@link Scene}'s {@link Camera} to given targets.
87333
- * @property cameraFlight
87334
- * @type {CameraFlightAnimation}
86920
+ * @type {{String:MetaObject}}
87335
86921
  */
87336
- this.cameraFlight = new CameraFlightAnimation(this.scene, {
87337
- duration: 0.5
87338
- });
86922
+ this.rootMetaObjects = {};
87339
86923
 
87340
86924
  /**
87341
- * The Viewer's {@link CameraControl}, which
87342
- * controls the {@link Scene}'s {@link Camera} with mouse, touch and keyboard input.
87343
- * @property cameraControl
87344
- * @type {CameraControl}
86925
+ * Tracks number of MetaObjects of each type.
86926
+ * @private
87345
86927
  */
87346
- this.cameraControl = new CameraControl(this.scene, {
87347
- // panToPointer: true,
87348
- doublePickFlyTo: true
87349
- });
87350
-
87351
- this._plugins = [];
86928
+ this._typeCounts = {};
87352
86929
 
87353
86930
  /**
87354
86931
  * Subscriptions to events sent with {@link fire}.
@@ -87357,15 +86934,467 @@ class Viewer {
87357
86934
  this._eventSubs = {};
87358
86935
  }
87359
86936
 
87360
- /**
87361
- * Returns the capabilities of this Viewer.
87362
- *
87363
- * @returns {{astcSupported: boolean, etc1Supported: boolean, pvrtcSupported: boolean, etc2Supported: boolean, dxtSupported: boolean, bptcSupported: boolean}}
87364
- */
87365
- get capabilities() {
87366
- return this.scene.capabilities;
87367
- }
87368
-
86937
+ /**
86938
+ * Subscribes to an event fired at this Viewer.
86939
+ *
86940
+ * @param {String} event The event
86941
+ * @param {Function} callback Callback fired on the event
86942
+ */
86943
+ on(event, callback) {
86944
+ let subs = this._eventSubs[event];
86945
+ if (!subs) {
86946
+ subs = [];
86947
+ this._eventSubs[event] = subs;
86948
+ }
86949
+ subs.push(callback);
86950
+ }
86951
+
86952
+ /**
86953
+ * Fires an event at this Viewer.
86954
+ *
86955
+ * @param {String} event Event name
86956
+ * @param {Object} value Event parameters
86957
+ */
86958
+ fire(event, value) {
86959
+ const subs = this._eventSubs[event];
86960
+ if (subs) {
86961
+ for (let i = 0, len = subs.length; i < len; i++) {
86962
+ subs[i](value);
86963
+ }
86964
+ }
86965
+ }
86966
+
86967
+ /**
86968
+ * Unsubscribes from an event fired at this Viewer.
86969
+ * @param event
86970
+ */
86971
+ off(event) { // TODO
86972
+
86973
+ }
86974
+
86975
+ /**
86976
+ * Creates a {@link MetaModel} in this MetaScene.
86977
+ *
86978
+ * The MetaModel will contain a hierarchy of {@link MetaObject}s, created from the
86979
+ * meta objects in ````metaModelData````.
86980
+ *
86981
+ * The meta object hierarchy in ````metaModelData```` is expected to be non-cyclic, with a single root. If the meta
86982
+ * objects are cyclic, then this method will log an error and attempt to recover by creating a dummy root MetaObject
86983
+ * of type "Model" and connecting all other MetaObjects as its direct children. If the meta objects contain multiple
86984
+ * roots, then this method similarly attempts to recover by creating a dummy root MetaObject of type "Model" and
86985
+ * connecting all the root MetaObjects as its children.
86986
+ *
86987
+ * @param {String} modelId ID for the new {@link MetaModel}, which will have {@link MetaModel#id} set to this value.
86988
+ * @param {Object} metaModelData Data for the {@link MetaModel}.
86989
+ * @param {Object} [options] Options for creating the {@link MetaModel}.
86990
+ * @param {Object} [options.includeTypes] When provided, only create {@link MetaObject}s with types in this list.
86991
+ * @param {Object} [options.excludeTypes] When provided, never create {@link MetaObject}s with types in this list.
86992
+ * @param {Boolean} [options.globalizeObjectIds=false] Whether to globalize each {@link MetaObject#id}. Set this ````true```` when you need to load multiple instances of the same meta model, to avoid ID clashes between the meta objects in the different instances.
86993
+ * @returns {MetaModel} The new MetaModel.
86994
+ */
86995
+ createMetaModel(modelId, metaModelData, options = {}) {
86996
+
86997
+ const projectId = metaModelData.projectId || "none";
86998
+ const revisionId = metaModelData.revisionId || "none";
86999
+ const newPropertySets = metaModelData.propertySets || [];
87000
+ const newObjects = metaModelData.metaObjects || [];
87001
+ const author = metaModelData.author;
87002
+ const createdAt = metaModelData.createdAt;
87003
+ const creatingApplication = metaModelData.creatingApplication;
87004
+ const schema = metaModelData.schema;
87005
+ // if (options.excludeTypes) {
87006
+ // excludeTypes = {};
87007
+ // for (let i = 0, len = options.excludeTypes.length; i < len; i++) {
87008
+ // includeTypes[options.excludeTypes[i]] = true;
87009
+ // }
87010
+ // }
87011
+
87012
+ const metaModel = new MetaModel(this, modelId, projectId, revisionId, author, createdAt, creatingApplication, schema, [], null);
87013
+
87014
+ this.metaModels[modelId] = metaModel;
87015
+
87016
+ for (let i = 0, len = newPropertySets.length; i < len; i++) {
87017
+ const propertySetCfg = newPropertySets[i];
87018
+ const propertySetId = propertySetCfg.id;
87019
+ const propertySet = new PropertySet(propertySetId, propertySetCfg.originalSystemId, propertySetCfg.name, propertySetCfg.type, propertySetCfg.properties);
87020
+ metaModel.propertySets[propertySetId] = propertySet;
87021
+ this.propertySets[propertySetId] = propertySet;
87022
+ }
87023
+
87024
+ const filteredObjectsParams = []; // Params for each new metaobject that passes our filters
87025
+ const existingObjects = []; // List of our metaobjects that are already existing, to which we'll also append new metaobjects we'll create
87026
+ const createObjectsParams = []; // List of params for metaobjects we'll create
87027
+
87028
+
87029
+ // Filter out the parameters we'll create
87030
+
87031
+ for (let i = 0, len = newObjects.length; i < len; i++) {
87032
+ const newObject = newObjects[i];
87033
+ newObject.type;
87034
+ filteredObjectsParams.push(newObject);
87035
+ }
87036
+
87037
+ // Build list of params for metaobjects we'll create
87038
+
87039
+ for (let i = 0, len = filteredObjectsParams.length; i < len; i++) {
87040
+ let filteredObject = filteredObjectsParams[i];
87041
+ const existingObject = this.metaObjects[filteredObject.id];
87042
+ if (existingObject) {
87043
+ existingObject.metaModels.push(metaModel);
87044
+ existingObjects.push(existingObject);
87045
+ continue;
87046
+ }
87047
+ createObjectsParams.push(filteredObject);
87048
+ }
87049
+
87050
+ // Create metaobjects in the creation list, add them to list of existing metaobjects
87051
+
87052
+ for (let i = 0, len = createObjectsParams.length; i < len; i++) {
87053
+ const createObject = createObjectsParams[i];
87054
+ const type = createObject.type;
87055
+ const objectId = createObject.id;
87056
+ const originalSystemId = createObject.id;
87057
+ const name = createObject.name;
87058
+ const propertySets = [];
87059
+ if (createObject.propertySetIds && createObject.propertySetIds.length > 0) {
87060
+ for (let j = 0, lenj = createObject.propertySetIds.length; j < lenj; j++) {
87061
+ const propertySetId = createObject.propertySetIds[j];
87062
+ const propertySet = metaModel.propertySets[propertySetId];
87063
+ if (propertySet) {
87064
+ propertySets.push(propertySet);
87065
+ }
87066
+ }
87067
+ }
87068
+ const parent = null;
87069
+ const children = null;
87070
+ const external = createObject.external;
87071
+ const metaObject = new MetaObject(metaModel, objectId, originalSystemId, name, type, propertySets, parent, children, external);
87072
+ metaObject._parentId = createObject.parent;
87073
+ this.metaObjects[objectId] = metaObject;
87074
+ (this.metaObjectsByType[type] || (this.metaObjectsByType[type] = {}))[objectId] = metaObject;
87075
+ if (this._typeCounts[type] === undefined) {
87076
+ this._typeCounts[type] = 1;
87077
+ } else {
87078
+ this._typeCounts[type]++;
87079
+ }
87080
+ existingObjects.push(metaObject);
87081
+ if (createObject.parent === undefined || createObject.parent === null) ;
87082
+ }
87083
+
87084
+ // Ensure that metaobjects in the existing metaobject list are all attached to their parents,
87085
+ // or registered as root metaobjects
87086
+
87087
+ for (let i = 0, len = existingObjects.length; i < len; i++) {
87088
+ const existingObject = existingObjects[i];
87089
+ const objectId = existingObject.id;
87090
+ if ((existingObject._parentId === undefined || existingObject._parentId === null) && !existingObject.parent) {
87091
+ metaModel.rootMetaObject = existingObject; // Deprecated, and won't work for federated models
87092
+ metaModel.rootMetaObjects[objectId] = existingObject;
87093
+ this.rootMetaObjects[objectId] = existingObject;
87094
+ } else if (existingObject._parentId) {
87095
+ const parentId = existingObject._parentId;
87096
+ existingObject._parentId = null;
87097
+ let parentMetaObject = this.metaObjects[parentId];
87098
+ if (parentMetaObject) {
87099
+ existingObject.parent = parentMetaObject;
87100
+ parentMetaObject.children = parentMetaObject.children || [];
87101
+ parentMetaObject.children.push(existingObject);
87102
+ }
87103
+ }
87104
+ }
87105
+
87106
+ this.fire("metaModelCreated", modelId);
87107
+ return metaModel;
87108
+ }
87109
+
87110
+ /**
87111
+ * Removes a {@link MetaModel} from this MetaScene.
87112
+ *
87113
+ * Fires a "metaModelDestroyed" event with the value of the {@link MetaModel#id}.
87114
+ *
87115
+ * @param {String} id ID of the target {@link MetaModel}.
87116
+ */
87117
+ destroyMetaModel(id) {
87118
+ const metaModel = this.metaModels[id];
87119
+ if (!metaModel) {
87120
+ return;
87121
+ }
87122
+ this._removeMetaModel(metaModel);
87123
+ this.fire("metaModelDestroyed", id);
87124
+ }
87125
+
87126
+ _removeMetaModel(metaModel) {
87127
+ const metaObjects = this.metaObjects;
87128
+ const metaObjectsByType = this.metaObjectsByType;
87129
+ let visit = (metaObject) => {
87130
+ delete metaObjects[metaObject.id];
87131
+ const types = metaObjectsByType[metaObject.type];
87132
+ if (types && types[metaObject.id]) {
87133
+ delete types[metaObject.id];
87134
+ if (--this._typeCounts[metaObject.type] === 0) {
87135
+ delete this._typeCounts[metaObject.type];
87136
+ delete metaObjectsByType[metaObject.type];
87137
+ }
87138
+ }
87139
+ const children = metaObject.children;
87140
+ if (children) {
87141
+ for (let i = 0, len = children.length; i < len; i++) {
87142
+ const childMetaObject = children[i];
87143
+ visit(childMetaObject);
87144
+ }
87145
+ }
87146
+ };
87147
+ visit(metaModel.rootMetaObject);
87148
+ for (let propertySetId in metaModel.propertySets) {
87149
+ if (metaModel.propertySets.hasOwnProperty(propertySetId)) {
87150
+ delete this.propertySets[propertySetId];
87151
+ }
87152
+ }
87153
+ delete this.metaModels[metaModel.id];
87154
+ }
87155
+
87156
+ /**
87157
+ * Gets the {@link MetaObject#id}s of the {@link MetaObject}s that have the given {@link MetaObject#type}.
87158
+ *
87159
+ * @param {String} type The type.
87160
+ * @returns {String[]} Array of {@link MetaObject#id}s.
87161
+ */
87162
+ getObjectIDsByType(type) {
87163
+ const metaObjects = this.metaObjectsByType[type];
87164
+ return metaObjects ? Object.keys(metaObjects) : [];
87165
+ }
87166
+
87167
+ /**
87168
+ * Gets the {@link MetaObject#id}s of the {@link MetaObject}s within the given subtree.
87169
+ *
87170
+ * @param {String} id ID of the root {@link MetaObject} of the given subtree.
87171
+ * @param {String[]} [includeTypes] Optional list of types to include.
87172
+ * @param {String[]} [excludeTypes] Optional list of types to exclude.
87173
+ * @returns {String[]} Array of {@link MetaObject#id}s.
87174
+ */
87175
+ getObjectIDsInSubtree(id, includeTypes, excludeTypes) {
87176
+ const list = [];
87177
+ const metaObject = this.metaObjects[id];
87178
+ const includeMask = (includeTypes && includeTypes.length > 0) ? arrayToMap(includeTypes) : null;
87179
+ const excludeMask = (excludeTypes && excludeTypes.length > 0) ? arrayToMap(excludeTypes) : null;
87180
+
87181
+ function visit(metaObject) {
87182
+ if (!metaObject) {
87183
+ return;
87184
+ }
87185
+ var include = true;
87186
+ if (excludeMask && excludeMask[metaObject.type]) {
87187
+ include = false;
87188
+ } else if (includeMask && (!includeMask[metaObject.type])) {
87189
+ include = false;
87190
+ }
87191
+ if (include) {
87192
+ list.push(metaObject.id);
87193
+ }
87194
+ const children = metaObject.children;
87195
+ if (children) {
87196
+ for (var i = 0, len = children.length; i < len; i++) {
87197
+ visit(children[i]);
87198
+ }
87199
+ }
87200
+ }
87201
+
87202
+ visit(metaObject);
87203
+ return list;
87204
+ }
87205
+
87206
+ /**
87207
+ * Iterates over the {@link MetaObject}s within the subtree.
87208
+ *
87209
+ * @param {String} id ID of root {@link MetaObject}.
87210
+ * @param {Function} callback Callback fired at each {@link MetaObject}.
87211
+ */
87212
+ withMetaObjectsInSubtree(id, callback) {
87213
+ const metaObject = this.metaObjects[id];
87214
+ if (!metaObject) {
87215
+ return;
87216
+ }
87217
+ metaObject.withMetaObjectsInSubtree(callback);
87218
+ }
87219
+ }
87220
+
87221
+ function arrayToMap(array) {
87222
+ const map = {};
87223
+ for (var i = 0, len = array.length; i < len; i++) {
87224
+ map[array[i]] = true;
87225
+ }
87226
+ return map;
87227
+ }
87228
+
87229
+ /**
87230
+ * The 3D Viewer at the heart of the xeokit SDK.
87231
+ *
87232
+ * * A Viewer wraps a single {@link Scene}
87233
+ * * Add {@link Plugin}s to a Viewer to extend its functionality.
87234
+ * * {@link Viewer#metaScene} holds metadata about models in the
87235
+ * Viewer's {@link MetaScene}.
87236
+ * * Use {@link Viewer#cameraFlight} to fly or jump the {@link Scene}'s
87237
+ * {@link Camera} to target positions, boundaries or {@link Entity}s.
87238
+ *
87239
+ * @public
87240
+ */
87241
+ class Viewer {
87242
+
87243
+ /**
87244
+ * @constructor
87245
+ * @param {Object} cfg Viewer configuration.
87246
+ * @param {String} [cfg.id] Optional ID for this Viewer, defaults to the ID of {@link Viewer#scene}, which xeokit automatically generates.
87247
+ * @param {String} [cfg.canvasId] ID of an existing HTML canvas for the {@link Viewer#scene} - either this or canvasElement is mandatory. When both values are given, the element reference is always preferred to the ID.
87248
+ * @param {HTMLCanvasElement} [cfg.canvasElement] Reference of an existing HTML canvas for the {@link Viewer#scene} - either this or canvasId is mandatory. When both values are given, the element reference is always preferred to the ID.
87249
+ * @param {HTMLElement} [cfg.keyboardEventsElement] Optional reference to HTML element on which key events should be handled. Defaults to the HTML Document.
87250
+ * @param {String} [cfg.spinnerElementId] ID of existing HTML element to show the {@link Spinner} - internally creates a default element automatically if this is omitted.
87251
+ * @param {Number} [cfg.passes=1] The number of times the {@link Viewer#scene} renders per frame.
87252
+ * @param {Boolean} [cfg.clearEachPass=false] When doing multiple passes per frame, specifies if to clear the canvas before each pass (true) or just before the first pass (false).
87253
+ * @param {Boolean} [cfg.preserveDrawingBuffer=true] Whether or not to preserve the WebGL drawing buffer. This needs to be ````true```` for {@link Viewer#getSnapshot} to work.
87254
+ * @param {Boolean} [cfg.transparent=true] Whether or not the canvas is transparent.
87255
+ * @param {Boolean} [cfg.premultipliedAlpha=false] Whether or not you want alpha composition with premultiplied alpha. Highlighting and selection works best when this is ````false````.
87256
+ * @param {Boolean} [cfg.gammaInput=true] When true, expects that all textures and colors are premultiplied gamma.
87257
+ * @param {Boolean} [cfg.gammaOutput=false] Whether or not to render with pre-multiplied gama.
87258
+ * @param {Number} [cfg.gammaFactor=2.2] The gamma factor to use when rendering with pre-multiplied gamma.
87259
+ * @param {Number[]} [cfg.backgroundColor=[1,1,1]] Sets the canvas background color to use when ````transparent```` is false.
87260
+ * @param {Boolean} [cfg.backgroundColorFromAmbientLight=true] When ````transparent```` is false, set this ````true````
87261
+ * to derive the canvas background color from {@link AmbientLight#color}, or ````false```` to set the canvas background to ````backgroundColor````.
87262
+ * @param {String} [cfg.units="meters"] The measurement unit type. Accepted values are ````"meters"````, ````"metres"````, , ````"centimeters"````, ````"centimetres"````, ````"millimeters"````, ````"millimetres"````, ````"yards"````, ````"feet"```` and ````"inches"````.
87263
+ * @param {Number} [cfg.scale=1] The number of Real-space units in each World-space coordinate system unit.
87264
+ * @param {Number[]} [cfg.origin=[0,0,0]] The Real-space 3D origin, in current measurement units, at which the World-space coordinate origin ````[0,0,0]```` sits.
87265
+ * @param {Boolean} [cfg.saoEnabled=false] Whether to enable Scalable Ambient Obscurance (SAO) effect. See {@link SAO} for more info.
87266
+ * @param {Boolean} [cfg.antialias=true] Whether to enable anti-aliasing.
87267
+ * @throws {String} Throws an exception when both canvasId or canvasElement are missing or they aren't pointing to a valid HTMLCanvasElement.
87268
+ * @param {Boolean} [cfg.alphaDepthMask=true] Whether writing into the depth buffer is enabled or disabled when rendering transparent objects.
87269
+ * @param {Boolean} [cfg.entityOffsetsEnabled=false] Whether to enable {@link Entity#offset}. For best performance, only set this ````true```` when you need to use {@link Entity#offset}.
87270
+ * @param {Boolean} [cfg.pickSurfacePrecisionEnabled=false] Whether to enable full-precision accuracy when surface picking with {@link Scene#pick}. Note that when ````true````, this configuration will increase the amount of browser memory used by the Viewer. The ````pickSurfacePrecision```` option for ````Scene#pick```` only works if this is set ````true````.
87271
+ * @param {Boolean} [cfg.logarithmicDepthBufferEnabled=false] Whether to enable logarithmic depth buffer. When this is true,
87272
+ * you can set huge values for {@link Perspective#far} and {@link Ortho#far}, to push the far clipping plane back so
87273
+ * that it does not clip huge models.
87274
+ * @param {Boolean} [cfg.colorTextureEnabled=true] Whether to enable base color texture rendering.
87275
+ * @param {Boolean} [cfg.pbrEnabled=false] Whether to enable physically-based rendering.
87276
+ * @param {LocaleService} [cfg.localeService=null] Optional locale-based translation service.
87277
+ */
87278
+ constructor(cfg) {
87279
+
87280
+ /**
87281
+ * The Viewer's current language setting.
87282
+ * @property language
87283
+ * @deprecated
87284
+ * @type {String}
87285
+ */
87286
+ this.language = "en";
87287
+
87288
+ /**
87289
+ * The viewer's locale service.
87290
+ *
87291
+ * This is configured via the Viewer's constructor.
87292
+ *
87293
+ * By default, this service will be an instance of {@link LocaleService}, which will just return
87294
+ * null translations for all given strings and phrases.
87295
+ *
87296
+ * @property localeService
87297
+ * @type {LocaleService}
87298
+ * @since 2.0
87299
+ */
87300
+ this.localeService = cfg.localeService || new LocaleService();
87301
+
87302
+ /**
87303
+ * The Viewer's {@link Scene}.
87304
+ * @property scene
87305
+ * @type {Scene}
87306
+ */
87307
+ this.scene = new Scene(this, {
87308
+ canvasId: cfg.canvasId,
87309
+ canvasElement: cfg.canvasElement,
87310
+ keyboardEventsElement: cfg.keyboardEventsElement,
87311
+ contextAttr: {
87312
+ preserveDrawingBuffer: cfg.preserveDrawingBuffer !== false,
87313
+ premultipliedAlpha: (!!cfg.premultipliedAlpha),
87314
+ antialias: (cfg.antialias !== false)
87315
+ },
87316
+ spinnerElementId: cfg.spinnerElementId,
87317
+ transparent: (cfg.transparent !== false),
87318
+ gammaInput: true,
87319
+ gammaOutput: false,
87320
+ backgroundColor: cfg.backgroundColor,
87321
+ backgroundColorFromAmbientLight: cfg.backgroundColorFromAmbientLight,
87322
+ ticksPerRender: 1,
87323
+ ticksPerOcclusionTest: 20,
87324
+ units: cfg.units,
87325
+ scale: cfg.scale,
87326
+ origin: cfg.origin,
87327
+ saoEnabled: cfg.saoEnabled,
87328
+ alphaDepthMask: (cfg.alphaDepthMask !== false),
87329
+ entityOffsetsEnabled: (!!cfg.entityOffsetsEnabled),
87330
+ pickSurfacePrecisionEnabled: (!!cfg.pickSurfacePrecisionEnabled),
87331
+ logarithmicDepthBufferEnabled: (!!cfg.logarithmicDepthBufferEnabled),
87332
+ pbrEnabled: (!!cfg.pbrEnabled),
87333
+ colorTextureEnabled: (cfg.colorTextureEnabled !== false)
87334
+ });
87335
+
87336
+ /**
87337
+ * Metadata about the {@link Scene} and the models and objects within it.
87338
+ * @property metaScene
87339
+ * @type {MetaScene}
87340
+ * @readonly
87341
+ */
87342
+ this.metaScene = new MetaScene(this, this.scene);
87343
+
87344
+ /**
87345
+ * The Viewer's ID.
87346
+ * @property id
87347
+ *
87348
+ * @type {String|Number}
87349
+ */
87350
+ this.id = cfg.id || this.scene.id;
87351
+
87352
+ /**
87353
+ * The Viewer's {@link Camera}. This is also found on {@link Scene#camera}.
87354
+ * @property camera
87355
+ * @type {Camera}
87356
+ */
87357
+ this.camera = this.scene.camera;
87358
+
87359
+ /**
87360
+ * The Viewer's {@link CameraFlightAnimation}, which
87361
+ * is used to fly the {@link Scene}'s {@link Camera} to given targets.
87362
+ * @property cameraFlight
87363
+ * @type {CameraFlightAnimation}
87364
+ */
87365
+ this.cameraFlight = new CameraFlightAnimation(this.scene, {
87366
+ duration: 0.5
87367
+ });
87368
+
87369
+ /**
87370
+ * The Viewer's {@link CameraControl}, which
87371
+ * controls the {@link Scene}'s {@link Camera} with mouse, touch and keyboard input.
87372
+ * @property cameraControl
87373
+ * @type {CameraControl}
87374
+ */
87375
+ this.cameraControl = new CameraControl(this.scene, {
87376
+ // panToPointer: true,
87377
+ doublePickFlyTo: true
87378
+ });
87379
+
87380
+ this._plugins = [];
87381
+
87382
+ /**
87383
+ * Subscriptions to events sent with {@link fire}.
87384
+ * @private
87385
+ */
87386
+ this._eventSubs = {};
87387
+ }
87388
+
87389
+ /**
87390
+ * Returns the capabilities of this Viewer.
87391
+ *
87392
+ * @returns {{astcSupported: boolean, etc1Supported: boolean, pvrtcSupported: boolean, etc2Supported: boolean, dxtSupported: boolean, bptcSupported: boolean}}
87393
+ */
87394
+ get capabilities() {
87395
+ return this.scene.capabilities;
87396
+ }
87397
+
87369
87398
  /**
87370
87399
  * Subscribes to an event fired at this Viewer.
87371
87400
  *
@@ -87605,7 +87634,7 @@ class Viewer {
87605
87634
  * [[Run this example](http://xeokit.github.io/xeokit-sdk/examples/#Configs_disableDoublePrecisionAndRAF)]
87606
87635
  *
87607
87636
  * ````javascript
87608
- * import {Configs, Viewer, XKTLoaderPlugin} from "../dist/xeokit-sdk.es.js";
87637
+ * import {Configs, Viewer, XKTLoaderPlugin} from "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/xeokit-sdk.es.min.js";
87609
87638
  *
87610
87639
  * // Access xeoit-sdk global configs.
87611
87640
  * // We typically set configs only before we create any Viewers.
@@ -88284,6 +88313,7 @@ class DistanceMeasurementsPlugin extends Plugin {
88284
88313
  this._onMouseOver = (event, measurement) => {
88285
88314
  this.fire("mouseOver", {
88286
88315
  plugin: this,
88316
+ distanceMeasurement: measurement,
88287
88317
  measurement,
88288
88318
  event
88289
88319
  });
@@ -88292,6 +88322,7 @@ class DistanceMeasurementsPlugin extends Plugin {
88292
88322
  this._onMouseLeave = (event, measurement) => {
88293
88323
  this.fire("mouseLeave", {
88294
88324
  plugin: this,
88325
+ distanceMeasurement: measurement,
88295
88326
  measurement,
88296
88327
  event
88297
88328
  });
@@ -88300,6 +88331,7 @@ class DistanceMeasurementsPlugin extends Plugin {
88300
88331
  this._onContextMenu = (event, measurement) => {
88301
88332
  this.fire("contextMenu", {
88302
88333
  plugin: this,
88334
+ distanceMeasurement: measurement,
88303
88335
  measurement,
88304
88336
  event
88305
88337
  });
@@ -103159,142 +103191,398 @@ class StoreyViewsPlugin extends Plugin {
103159
103191
  }
103160
103192
 
103161
103193
  /**
103162
- * Tests if {@link TreeViewPlugin} would be able to create a "types" hierarchy for the given {@link MetaModel}.
103194
+ * @desc A {@link Viewer} plugin that provides an HTML tree view to navigate the IFC elements in models.
103195
+ * <br>
103163
103196
  *
103164
- * @param {MetaModel} metaModel The MetaModel.
103165
- * @param {String[]} errors Accumulates messages for validation errors.
103166
- * @return {boolean} Returns ````true```` if no errors found, else ````false````.
103167
- */
103168
- function validateMetaModelForTreeViewTypesHierarchy(metaModel, errors) {
103169
- const rootMetaObject = metaModel.rootMetaObject;
103170
- if (!rootMetaObject) {
103171
- errors.push("Can't build types hierarchy: model is empty");
103172
- return false;
103173
- }
103174
- return true;
103175
- }
103176
-
103177
- /**
103178
- * Tests if {@link TreeViewPlugin} would be able to create a "storeys" hierarchy for the given {@link MetaModel}.
103197
+ * <a href="https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_WestRiverSideHospital" style="border: 1px solid black;"><img src="http://xeokit.io/img/docs/TreeViewPlugin/TreeViewPlugin.png"></a>
103179
103198
  *
103180
- * @param {MetaModel} metaModel The MetaModel.
103181
- * @param {String[]} errors Accumulates messages for validation errors.
103182
- * @return {boolean} Returns ````true```` if no errors found, else ````false````.
103183
- */
103184
- function validateMetaModelForTreeViewStoreysHierarchy(metaModel, errors) {
103185
- const rootMetaObject = metaModel.rootMetaObject;
103186
- if (!rootMetaObject) {
103187
- errors.push("Can't build storeys hierarchy: model is empty");
103188
- return false;
103189
- }
103190
- return _validateMetaModelForStoreysHierarchy(rootMetaObject, errors);
103191
- }
103192
-
103193
- /**
103194
- * Tests if {@link TreeViewPlugin} would be able to create a "containment" hierarchy for the given {@link MetaModel}.
103199
+ * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_WestRiverSideHospital)]
103195
103200
  *
103196
- * @param {MetaModel} metaModel The MetaModel.
103197
- * @param {String[]} errors Accumulates messages for validation errors.
103198
- * @return {boolean} Returns ````true```` if no errors found, else ````false````.
103199
- */
103200
- function validateMetaModelForTreeViewContainmentHierarchy(metaModel, errors) {
103201
- const rootMetaObject = metaModel.rootMetaObject;
103202
- if (!rootMetaObject) {
103203
- errors.push("Can't build containment hierarchy: model is empty");
103204
- return false;
103205
- }
103206
- return true;
103207
- }
103208
-
103209
- /**
103210
- * @private
103211
- */
103212
- function _validateMetaModelForStoreysHierarchy(metaObject, errors, level = 0, ctx, buildingNode) {
103213
- ctx = ctx || {
103214
- foundIFCBuildingStoreys: false
103215
- };
103216
- const metaObjectType = metaObject.type;
103217
- const children = metaObject.children;
103218
- if (metaObjectType === "IfcBuilding") {
103219
- buildingNode = true;
103220
- } else if (metaObjectType === "IfcBuildingStorey") {
103221
- if (!buildingNode) {
103222
- errors.push("Can't build storeys hierarchy: IfcBuildingStorey found without parent IfcBuilding");
103223
- return false;
103224
- }
103225
- ctx.foundIFCBuildingStoreys = true;
103226
- }
103227
- if (children) {
103228
- for (let i = 0, len = children.length; i < len; i++) {
103229
- const childMetaObject = children[i];
103230
- if (!_validateMetaModelForStoreysHierarchy(childMetaObject, errors, level + 1, ctx, buildingNode)) {
103231
- return false;
103232
- }
103233
- }
103234
- }
103235
- if (level === 0) {
103236
- if (!ctx.foundIFCBuildingStoreys) ;
103237
- }
103238
- return true;
103239
- }
103240
-
103241
- const idMap = new Map$1();
103242
-
103243
- /**
103244
- * @desc Represents a model tree view within a {@link TreeViewPlugin}.
103201
+ * ## Overview
103202
+ *
103203
+ * * A fast HTML tree view, with zero external dependencies, that works with huge numbers of objects.
103204
+ * * Each tree node has a checkbox to control the visibility of its object.
103205
+ * * Has three hierarchy modes: "containment", "types" and "storeys".
103206
+ * * Automatically contains all models (that have metadata) that are currently in the {@link Scene}.
103207
+ * * Sorts tree nodes by default - spatially, from top-to-bottom for ````IfcBuildingStorey```` nodes, and alphanumerically for other nodes.
103208
+ * * Allows custom CSS styling.
103209
+ * * Use {@link ContextMenu} to create a context menu for the tree nodes.
103210
+ *
103211
+ * ## Credits
103212
+ *
103213
+ * TreeViewPlugin is based on techniques described in [*Super Fast Tree View in JavaScript*](https://chrissmith.xyz/super-fast-tree-view-in-javascript/) by [Chris Smith](https://twitter.com/chris22smith).
103214
+ *
103215
+ * ## Usage
103216
+ *
103217
+ * In the example below, we'll add a TreeViewPlugin which, by default, will automatically show the structural
103218
+ * hierarchy of the IFC elements in each model we load.
103219
+ *
103220
+ * Then we'll use an {@link XKTLoaderPlugin} to load the Schependomlaan model from an
103221
+ * [.xkt file](https://github.com/xeokit/xeokit-sdk/tree/master/examples/models/xkt/schependomlaan).
103222
+ *
103223
+ * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_Schependomlaan)]
103224
+ *
103225
+ * ````javascript
103226
+ * import {Viewer, XKTLoaderPlugin, TreeViewPlugin} from "xeokit-sdk.es.js";
103227
+ *
103228
+ * const viewer = new Viewer({
103229
+ * canvasId: "myCanvas",
103230
+ * transparent: true
103231
+ * });
103232
+ *
103233
+ * viewer.camera.eye = [-2.56, 8.38, 8.27];
103234
+ * viewer.camera.look = [13.44, 3.31, -14.83];
103235
+ * viewer.camera.up = [0.10, 0.98, -0.14];
103236
+ *
103237
+ * const treeView = new TreeViewPlugin(viewer, {
103238
+ * containerElement: document.getElementById("myTreeViewContainer")
103239
+ * });
103240
+ *
103241
+ * const xktLoader = new XKTLoaderPlugin(viewer);
103242
+ *
103243
+ * const model = xktLoader.load({
103244
+ * id: "myModel",
103245
+ * src: "./models/xkt/Schependomlaan.xkt",
103246
+ * edges: true
103247
+ * });
103248
+ * ````
103249
+ *
103250
+ * ## Manually Adding Models
103251
+ *
103252
+ * Instead of adding models automatically, we can control which models appear in our TreeViewPlugin by adding them manually.
103253
+ *
103254
+ * In the next example, we'll configure the TreeViewPlugin to not add models automatically. Then, once the model
103255
+ * has loaded, we'll add it manually using {@link TreeViewPlugin#addModel}.
103256
+ *
103257
+ * ````javascript
103258
+ * const treeView = new TreeViewPlugin(viewer, {
103259
+ * containerElement: document.getElementById("myTreeViewContainer"),
103260
+ * autoAddModels: false // <<---------------- Don't auto-add models
103261
+ * });
103262
+ *
103263
+ * const xktLoader = new XKTLoaderPlugin(viewer);
103264
+ *
103265
+ * const model = xktLoader.load({
103266
+ * id: "myModel",
103267
+ * src: "./models/xkt/Schependomlaan.xkt",
103268
+ * edges: true
103269
+ * });
103270
+ *
103271
+ * model.on("loaded", () => {
103272
+ * treeView.addModel(model.id);
103273
+ * });
103274
+ * ````
103275
+ *
103276
+ * Adding models manually also allows us to set some options for the model. For example, the ````rootName```` option allows us to provide a custom name for
103277
+ * the root node, which is sometimes desirable when the model's "IfcProject" element's name is not suitable:
103278
+ *
103279
+ * ````javascript
103280
+ * model.on("loaded", () => {
103281
+ * treeView.addModel(model.id, {
103282
+ * rootName: "Schependomlaan Model"
103283
+ * });
103284
+ * });
103285
+ * ````
103286
+ *
103287
+ * ## Initially Expanding the Hierarchy
103288
+ *
103289
+ * We can also configure TreeViewPlugin to initially expand each model's nodes to a given depth.
103290
+ *
103291
+ * Let's automatically expand the first three nodes from the root, for every model added:
103292
+ *
103293
+ * ````javascript
103294
+ * const treeView = new TreeViewPlugin(viewer, {
103295
+ * containerElement: document.getElementById("myTreeViewContainer"),
103296
+ * autoExpandDepth: 3
103297
+ * });
103298
+ * ````
103299
+ *
103300
+ * ## Showing a Node by ID
103301
+ *
103302
+ * We can show a given node using its ID. This causes the TreeViewPlugin to collapse, then expand and scroll the node into view, then highlight the node.
103303
+ *
103304
+ * See the documentation for the {@link TreeViewPlugin#showNode} method for more information, including how to define a custom highlighted appearance for the node using CSS.
103305
+ *
103306
+ * Let's make the TreeViewPlugin show the node corresponding to whatever object {@link Entity} that we pick:
103307
+ *
103308
+ * ````javascript
103309
+ * viewer.cameraControl.on("picked", function (e) {
103310
+ * var objectId = e.entity.id;
103311
+ * treeView.showNode(objectId);
103312
+ * });
103313
+ * ````
103314
+ *
103315
+ * This will de-highlight any node that was previously shown by this method.
103316
+ *
103317
+ * Note that this method only works if the picked {@link Entity} is an object that belongs to a model that's represented in the TreeViewPlugin.
103318
+ *
103319
+ * ## Customizing Appearance
103320
+ *
103321
+ * We can customize the appearance of our TreeViewPlugin by defining custom CSS for its HTML
103322
+ * elements. See our example's [source code](https://github.com/xeokit/xeokit-sdk/blob/master/examples/BIMOffline_XKT_Schependomlaan.html)
103323
+ * for an example of custom CSS rules.
103324
+ *
103325
+ * ## Model Hierarchies
103326
+ *
103327
+ * TreeViewPlugin has three hierarchies for organizing its nodes:
103328
+ *
103329
+ * * "containment" - organizes the tree nodes to indicate the containment hierarchy of the {@link MetaObject}s.
103330
+ * * "types" - groups nodes by their IFC types.
103331
+ * * "storeys" - groups nodes within their ````IfcBuildingStoreys````, and sub-groups them by their IFC types.
103332
+ *
103333
+ * <br>
103334
+ * The table below shows what the hierarchies look like:
103335
+ * <br>
103336
+ *
103337
+ * | 1. Containment Hierarchy | 2. Types Hierarchy | 3. Storeys Hierarchy |
103338
+ * |---|---|---|
103339
+ * | <img src="http://xeokit.io/img/docs/TreeViewPlugin/structureMode.png"> | <img src="http://xeokit.io/img/docs/TreeViewPlugin/typesMode.png"> | <img src="http://xeokit.io/img/docs/TreeViewPlugin/storeysMode.png"> |
103340
+ * <br>
103341
+ *
103342
+ * Let's create a TreeViewPlugin that groups nodes by their building stories and IFC types:
103343
+ *
103344
+ * ````javascript
103345
+ * const treeView = new TreeViewPlugin(viewer, {
103346
+ * containerElement: document.getElementById("myTreeViewContainer"),
103347
+ * hierarchy: "stories"
103348
+ * });
103349
+ * ````
103350
+ *
103351
+ * ## Sorting Nodes
103352
+ *
103353
+ * TreeViewPlugin sorts its tree nodes by default. For a "storeys" hierarchy, it orders ````IfcBuildingStorey```` nodes
103354
+ * spatially, with the node for the highest story at the top, down to the lowest at the bottom.
103355
+ *
103356
+ * For all the hierarchy types ("containment", "classes" and "storeys"), TreeViewPlugin sorts the other node types
103357
+ * alphanumerically on their titles.
103358
+ *
103359
+ * If for some reason you need to prevent sorting, create your TreeViewPlugin with the option disabled, like so:
103360
+ *
103361
+ * ````javascript
103362
+ * const treeView = new TreeViewPlugin(viewer, {
103363
+ * containerElement: document.getElementById("myTreeViewContainer"),
103364
+ * hierarchy: "stories",
103365
+ * sortNodes: false // <<------ Disable node sorting
103366
+ * });
103367
+ * ````
103368
+ *
103369
+ * Note that, for all hierarchy modes, node sorting is only done for each model at the time that it is added to the TreeViewPlugin, and will not
103370
+ * update dynamically if we later transform the {@link Entity}s corresponding to the nodes.
103371
+ *
103372
+ * ## Pruning empty nodes
103373
+ *
103374
+ * Sometimes a model contains subtrees of objects that don't have any geometry. These are models whose
103375
+ * {@link MetaModel} contains trees of {@link MetaObject}s that don't have any {@link Entity}s in the {@link Scene}.
103376
+ *
103377
+ * For these models, the tree view would contain nodes that don't do anything in the Scene when we interact with them,
103378
+ * which is undesirable.
103379
+ *
103380
+ * By default, TreeViewPlugin will not create nodes for those objects. However, we can override that behaviour if we want
103381
+ * to have nodes for those objects (perhaps for debugging the model):
103382
+ *
103383
+ * ````javascript
103384
+ * const treeView = new TreeViewPlugin(viewer, {
103385
+ * containerElement: document.getElementById("myTreeViewContainer"),
103386
+ * hierarchy: "stories",
103387
+ * pruneEmptyNodes: false // <<------ Create nodes for object subtrees without geometry
103388
+ * });
103389
+ * ````
103390
+ *
103391
+ * ## Context Menu
103392
+ *
103393
+ * TreeViewPlugin fires a "contextmenu" event whenever we right-click on a tree node.
103394
+ *
103395
+ * The event contains:
103396
+ *
103397
+ * * ````event```` - the original [contextmenu](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event) [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
103398
+ * * ````viewer```` - the {@link Viewer}
103399
+ * * ````treeViewPlugin```` - the TreeViewPlugin
103400
+ * * ````treeViewNode```` - the {@link TreeViewNode} representing the tree node
103401
+ *<br><br>
103402
+ *
103403
+ * Let's use {@link ContextMenu} to show a simple context menu for the node we clicked.
103404
+ *
103405
+ * [[Run an example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)]
103406
+ *
103407
+ * ````javascript
103408
+ * import {ContextMenu} from "../src/extras/ContextMenu/ContextMenu.js";
103409
+ *
103410
+ * const treeViewContextMenu = new ContextMenu({
103411
+ * items: [
103412
+ * [
103413
+ * [
103414
+ * {
103415
+ * title: "Hide",
103416
+ * doAction: function (context) {
103417
+ * context.treeViewPlugin.withNodeTree(context.treeViewNode, (treeViewNode) => {
103418
+ * if (treeViewNode.objectId) {
103419
+ * const entity = context.viewer.scene.objects[treeViewNode.objectId];
103420
+ * if (entity) {
103421
+ * entity.visible = false;
103422
+ * }
103423
+ * }
103424
+ * });
103425
+ * }
103426
+ * },
103427
+ * {
103428
+ * title: "Hide all",
103429
+ * doAction: function (context) {
103430
+ * context.viewer.scene.setObjectsVisible(context.viewer.scene.visibleObjectIds, false);
103431
+ * }
103432
+ * }
103433
+ * ],
103434
+ * [
103435
+ * {
103436
+ * title: "Show",
103437
+ * doAction: function (context) {
103438
+ * context.treeViewPlugin.withNodeTree(context.treeViewNode, (treeViewNode) => {
103439
+ * if (treeViewNode.objectId) {
103440
+ * const entity = context.viewer.scene.objects[treeViewNode.objectId];
103441
+ * if (entity) {
103442
+ * entity.visible = true;
103443
+ * entity.xrayed = false;
103444
+ * entity.selected = false;
103445
+ * }
103446
+ * }
103447
+ * });
103448
+ * }
103449
+ * },
103450
+ * {
103451
+ * title: "Show all",
103452
+ * doAction: function (context) {
103453
+ * const scene = context.viewer.scene;
103454
+ * scene.setObjectsVisible(scene.objectIds, true);
103455
+ * scene.setObjectsXRayed(scene.xrayedObjectIds, false);
103456
+ * scene.setObjectsSelected(scene.selectedObjectIds, false);
103457
+ * }
103458
+ * }
103459
+ * ]
103460
+ * ]
103461
+ * ]
103462
+ * });
103463
+ *
103464
+ * treeView.on("contextmenu", (e) => {
103465
+ *
103466
+ * const event = e.event; // MouseEvent
103467
+ * const viewer = e.viewer; // Viewer
103468
+ * const treeViewPlugin = e.treeViewPlugin; // TreeViewPlugin
103469
+ * const treeViewNode = e.treeViewNode; // TreeViewNode
103470
+ *
103471
+ * treeViewContextMenu.show(e.event.pageX, e.event.pageY);
103472
+ *
103473
+ * treeViewContextMenu.context = {
103474
+ * viewer: e.viewer,
103475
+ * treeViewPlugin: e.treeViewPlugin,
103476
+ * treeViewNode: e.treeViewNode
103477
+ * };
103478
+ * });
103479
+ * ````
103480
+ *
103481
+ * ## Clicking Node Titles
103482
+ *
103483
+ * TreeViewPlugin fires a "nodeTitleClicked" event whenever we left-click on a tree node.
103484
+ *
103485
+ * Like the "contextmenu" event, this event contains:
103486
+ *
103487
+ * * ````event```` - the original [click](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event) [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
103488
+ * * ````viewer```` - the {@link Viewer}
103489
+ * * ````treeViewPlugin```` - the TreeViewPlugin
103490
+ * * ````treeViewNode```` - the {@link TreeViewNode} representing the tree node
103491
+ *<br><br>
103492
+ *
103493
+ * Let's register a callback to isolate and fit-to-view the {@link Entity}(s) represented by the node. This callback is
103494
+ * going to X-ray all the other Entitys, fly the camera to fit the Entity(s) for the clicked node, then hide the other Entitys.
103495
+ *
103496
+ * [[Run an example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)]
103497
+ *
103498
+ * ````javascript
103499
+ * treeView.on("nodeTitleClicked", (e) => {
103500
+ * const scene = viewer.scene;
103501
+ * const objectIds = [];
103502
+ * e.treeViewPlugin.withNodeTree(e.treeViewNode, (treeViewNode) => {
103503
+ * if (treeViewNode.objectId) {
103504
+ * objectIds.push(treeViewNode.objectId);
103505
+ * }
103506
+ * });
103507
+ * scene.setObjectsXRayed(scene.objectIds, true);
103508
+ * scene.setObjectsVisible(scene.objectIds, true);
103509
+ * scene.setObjectsXRayed(objectIds, false);
103510
+ * viewer.cameraFlight.flyTo({
103511
+ * aabb: scene.getAABB(objectIds),
103512
+ * duration: 0.5
103513
+ * }, () => {
103514
+ * setTimeout(function () {
103515
+ * scene.setObjectsVisible(scene.xrayedObjectIds, false);
103516
+ * scene.setObjectsXRayed(scene.xrayedObjectIds, false);
103517
+ * }, 500);
103518
+ * });
103519
+ * });
103520
+ * ````
103521
+ *
103522
+ * To make the cursor change to a pointer when we hover over the node titles, and also to make the titles change to blue, we'll also define this CSS for the ````<span>```` elements
103523
+ * that represent the titles of our TreeViewPlugin nodes:
103524
+ *
103525
+ * ````css
103526
+ * #treeViewContainer ul li span:hover {
103527
+ * color: blue;
103528
+ * cursor: pointer;
103529
+ * }
103530
+ * ````
103245
103531
  *
103246
- * * Stored in {@link treeViewPlugin#modelTreeViews}, mapped to the model ID.
103247
- * * Created by each call to {@link TreeViewPlugin#addModel}.
103532
+ * @class TreeViewPlugin
103248
103533
  */
103249
- class ModelTreeView {
103534
+ class TreeViewPlugin extends Plugin {
103250
103535
 
103251
103536
  /**
103252
- * @private
103537
+ * @constructor
103538
+ *
103539
+ * @param {Viewer} viewer The Viewer.
103540
+ * @param {*} cfg Plugin configuration.
103541
+ * @param {HTMLElement} cfg.containerElement DOM element to contain the TreeViewPlugin.
103542
+ * @param {Boolean} [cfg.autoAddModels=true] When ````true```` (default), will automatically add each model as it's created. Set this ````false```` if you want to manually add models using {@link TreeViewPlugin#addModel} instead.
103543
+ * @param {Number} [cfg.autoExpandDepth] Optional depth to which to initially expand the tree.
103544
+ * @param {String} [cfg.hierarchy="containment"] How to organize the tree nodes: "containment", "storeys" or "types". See the class documentation for details.
103545
+ * @param {Boolean} [cfg.sortNodes=true] When true, will sort the children of each node. For a "storeys" hierarchy, the
103546
+ * ````IfcBuildingStorey```` nodes will be ordered spatially, from the highest storey down to the lowest, on the
103547
+ * vertical World axis. For all hierarchy types, other node types will be ordered in the ascending alphanumeric order of their titles.
103548
+ * @param {Boolean} [cfg.pruneEmptyNodes=true] When true, will not contain nodes that don't have content in the {@link Scene}. These are nodes whose {@link MetaObject}s don't have {@link Entity}s.
103253
103549
  */
103254
- constructor(viewer, treeViewPlugin, model, metaModel, cfg) {
103255
-
103256
- if (!cfg.containerElement) {
103257
- throw "Config expected: containerElement";
103258
- }
103550
+ constructor(viewer, cfg = {}) {
103259
103551
 
103260
- const rootMetaObject = metaModel.rootMetaObject;
103261
- if (!rootMetaObject) {
103262
- return;
103263
- }
103552
+ super("TreeViewPlugin", viewer);
103264
103553
 
103265
103554
  /**
103266
- * Contains messages for any errors found in the MetaModel for this ModelTreeView.
103555
+ * Contains messages for any errors found while last rebuilding this TreeView.
103267
103556
  * @type {String[]}
103268
103557
  */
103269
103558
  this.errors = [];
103270
103559
 
103271
103560
  /**
103272
- * True if errors were found in the MetaModel for this ModelTreeView.
103561
+ * True if errors were found generating this TreeView.
103273
103562
  * @type {boolean}
103274
103563
  */
103275
103564
  this.valid = true;
103276
103565
 
103277
- /**
103278
- * The MetaModel corresponding to this ModelTreeView.
103279
- * @type {MetaModel}
103280
- */
103281
- this.metaModel = metaModel;
103566
+ if (!cfg.containerElement) {
103567
+ this.error("Config expected: containerElement");
103568
+ return;
103569
+ }
103282
103570
 
103283
- this._id = idMap.addItem();
103284
- this._baseId = "" + this._id;
103285
- this._viewer = viewer;
103286
- this._treeViewPlugin = treeViewPlugin;
103287
- this._rootMetaObject = rootMetaObject;
103288
103571
  this._containerElement = cfg.containerElement;
103572
+ this._metaModels = {};
103573
+ this._autoAddModels = (cfg.autoAddModels !== false);
103574
+ this._autoExpandDepth = (cfg.autoExpandDepth || 0);
103575
+ this._sortNodes = (cfg.sortNodes !== false);
103576
+ this._pruneEmptyNodes = (cfg.pruneEmptyNodes !== false);
103577
+ this._viewer = viewer;
103289
103578
  this._rootElement = null;
103290
103579
  this._muteSceneEvents = false;
103291
103580
  this._muteTreeEvents = false;
103292
103581
  this._rootNodes = [];
103293
- this._objectNodes = {};
103582
+ this._objectNodes = {}; // Object ID -> Node
103294
103583
  this._rootName = cfg.rootName;
103295
103584
  this._sortNodes = cfg.sortNodes;
103296
103585
  this._pruneEmptyNodes = cfg.pruneEmptyNodes;
103297
-
103298
103586
  this._showListItemElementId = null;
103299
103587
 
103300
103588
  this._containerElement.oncontextmenu = (e) => {
@@ -103347,43 +103635,41 @@ class ModelTreeView {
103347
103635
  });
103348
103636
 
103349
103637
  this._onObjectXrayed = this._viewer.scene.on('objectXRayed', (entity) => {
103350
- if (this._muteSceneEvents) {
103351
- return;
103352
- }
103353
- const objectId = entity.id;
103354
- const node = this._objectNodes[objectId];
103355
- if (!node) {
103356
- return; // Not in this tree
103357
- }
103358
-
103359
- this._muteTreeEvents = true;
103360
- const xrayed = entity.xrayed;
103361
- const updated = (xrayed !== node.xrayed);
103362
- if (!updated) {
103363
- return;
103364
- }
103365
- node.xrayed = xrayed;
103366
- const listItemElementId = 'node-' + node.nodeId;
103367
- const listItemElement = document.getElementById(listItemElementId);
103368
- if (listItemElement !== null) {
103369
- if (xrayed) {
103370
- listItemElement.classList.add('xrayed-node');
103371
- } else {
103372
- listItemElement.classList.remove('xrayed-node');
103638
+ if (this._muteSceneEvents) {
103639
+ return;
103373
103640
  }
103374
- }
103375
-
103376
- this._muteTreeEvents = false;
103641
+ const objectId = entity.id;
103642
+ const node = this._objectNodes[objectId];
103643
+ if (!node) {
103644
+ return; // Not in this tree
103645
+ }
103646
+ this._muteTreeEvents = true;
103647
+ const xrayed = entity.xrayed;
103648
+ const updated = (xrayed !== node.xrayed);
103649
+ if (!updated) {
103650
+ return;
103651
+ }
103652
+ node.xrayed = xrayed;
103653
+ const listItemElementId = 'node-' + node.nodeId;
103654
+ const listItemElement = document.getElementById(listItemElementId);
103655
+ if (listItemElement !== null) {
103656
+ if (xrayed) {
103657
+ listItemElement.classList.add('xrayed-node');
103658
+ } else {
103659
+ listItemElement.classList.remove('xrayed-node');
103660
+ }
103661
+ }
103662
+ this._muteTreeEvents = false;
103377
103663
  });
103378
103664
 
103379
- this.switchExpandHandler = (event) => {
103665
+ this._switchExpandHandler = (event) => {
103380
103666
  event.preventDefault();
103381
103667
  event.stopPropagation();
103382
103668
  const switchElement = event.target;
103383
103669
  this._expandSwitchElement(switchElement);
103384
103670
  };
103385
103671
 
103386
- this.switchCollapseHandler = (event) => {
103672
+ this._switchCollapseHandler = (event) => {
103387
103673
  event.preventDefault();
103388
103674
  event.stopPropagation();
103389
103675
  const switchElement = event.target;
@@ -103398,7 +103684,7 @@ class ModelTreeView {
103398
103684
  const checkbox = event.target;
103399
103685
  const visible = checkbox.checked;
103400
103686
  const nodeId = checkbox.id;
103401
- const checkedObjectId = this._nodeToObjectID(nodeId);
103687
+ const checkedObjectId = nodeId;
103402
103688
  const checkedNode = this._objectNodes[checkedObjectId];
103403
103689
  const objects = this._viewer.scene.objects;
103404
103690
  let numUpdated = 0;
@@ -103441,30 +103727,44 @@ class ModelTreeView {
103441
103727
  this._hierarchy = cfg.hierarchy || "containment";
103442
103728
  this._autoExpandDepth = cfg.autoExpandDepth || 0;
103443
103729
 
103444
- this._createNodes();
103445
- }
103446
-
103447
- _nodeToObjectID(nodeId) {
103448
- return nodeId.substring(this._baseId.length);
103449
- }
103450
-
103451
- _objectToNodeID(objectId) {
103452
- return this._baseId + objectId;
103453
- }
103730
+ if (this._autoAddModels) {
103731
+ const modelIds = Object.keys(this.viewer.metaScene.metaModels);
103732
+ for (let i = 0, len = modelIds.length; i < len; i++) {
103733
+ const modelId = modelIds[i];
103734
+ this.addModel(modelId);
103735
+ }
103736
+ this.viewer.scene.on("modelLoaded", (modelId) => {
103737
+ if (this.viewer.metaScene.metaModels[modelId]) {
103738
+ this.addModel(modelId);
103739
+ }
103740
+ });
103741
+ }
103454
103742
 
103455
- /**
103456
- * @private
103457
- * @param depth
103458
- */
103459
- setAutoExpandDepth(depth = 0) {
103460
- this._autoExpandDepth = depth;
103743
+ this.hierarchy = cfg.hierarchy;
103461
103744
  }
103462
103745
 
103463
103746
  /**
103464
- * @private
103465
- * @param hierarchy
103747
+ * Sets how the nodes are organized within this tree view.
103748
+ *
103749
+ * Accepted values are:
103750
+ *
103751
+ * * "containment" - organizes the nodes to indicate the containment hierarchy of the IFC objects.
103752
+ * * "types" - groups the nodes within their IFC types.
103753
+ * * "storeys" - groups the nodes within ````IfcBuildingStoreys```` and sub-groups them by their IFC types.
103754
+ *
103755
+ * <br>
103756
+ * This can be updated dynamically.
103757
+ *
103758
+ * Default value is "containment".
103759
+ *
103760
+ * @type {String}
103466
103761
  */
103467
- setHierarchy(hierarchy) {
103762
+ set hierarchy(hierarchy) {
103763
+ hierarchy = hierarchy || "containment";
103764
+ if (hierarchy !== "containment" && hierarchy !== "storeys" && hierarchy !== "types") {
103765
+ this.error("Unsupported value for `hierarchy' - defaulting to 'containment'");
103766
+ hierarchy = "containment";
103767
+ }
103468
103768
  if (this._hierarchy === hierarchy) {
103469
103769
  return;
103470
103770
  }
@@ -103472,38 +103772,287 @@ class ModelTreeView {
103472
103772
  this._createNodes();
103473
103773
  }
103474
103774
 
103475
- _createNodes() {
103476
- if (this._rootElement) {
103477
- this._rootElement.parentNode.removeChild(this._rootElement);
103478
- this._rootElement = null;
103479
- }
103480
- this._rootNodes = [];
103481
- this._objectNodes = {};
103482
- this._validate();
103483
- if (this.valid || (this._hierarchy !== "storeys")) {
103484
- this._createEnabledNodes();
103485
- } else {
103486
- this._createDisabledNodes();
103487
- }
103775
+ /**
103776
+ * Gets how the nodes are organized within this tree view.
103777
+ *
103778
+ * @type {String}
103779
+ */
103780
+ get hierarchy() {
103781
+ return this._hierarchy;
103488
103782
  }
103489
103783
 
103490
- _validate() {
103491
- this.errors = [];
103492
- switch (this._hierarchy) {
103493
- case "storeys":
103494
- this.valid = validateMetaModelForTreeViewStoreysHierarchy(this.metaModel, this.errors);
103495
- break;
103496
- case "types":
103497
- this.valid = validateMetaModelForTreeViewTypesHierarchy(this.metaModel, this.errors);
103498
- break;
103499
- case "containment":
103500
- default:
103501
- this.valid = validateMetaModelForTreeViewContainmentHierarchy(this.metaModel, this.errors);
103784
+ /**
103785
+ * Adds a model to this tree view.
103786
+ *
103787
+ * The model will be automatically removed when destroyed.
103788
+ *
103789
+ * To automatically add each model as it's created, instead of manually calling this method each time,
103790
+ * provide a ````autoAddModels: true```` to the TreeViewPlugin constructor.
103791
+ *
103792
+ * @param {String} modelId ID of a model {@link Entity} in {@link Scene#models}.
103793
+ * @param {Object} [options] Options for model in the tree view.
103794
+ * @param {String} [options.rootName] Optional display name for the root node. Ordinary, for "containment"
103795
+ * and "storeys" hierarchy types, the tree would derive the root node name from the model's "IfcProject" element
103796
+ * name. This option allows to override that name when it is not suitable as a display name.
103797
+ */
103798
+ addModel(modelId, options = {}) {
103799
+ if (!this._containerElement) {
103800
+ return;
103801
+ }
103802
+ const model = this.viewer.scene.models[modelId];
103803
+ if (!model) {
103804
+ throw "Model not found: " + modelId;
103805
+ }
103806
+ const metaModel = this.viewer.metaScene.metaModels[modelId];
103807
+ if (!metaModel) {
103808
+ this.error("MetaModel not found: " + modelId);
103809
+ return;
103810
+ }
103811
+ if (this._metaModels[modelId]) {
103812
+ this.warn("Model already added: " + modelId);
103813
+ return;
103814
+ }
103815
+ this._metaModels[modelId] = metaModel;
103816
+ model.on("destroyed", () => {
103817
+ this.removeModel(model.id);
103818
+ });
103819
+ this._createNodes();
103820
+ }
103821
+
103822
+ /**
103823
+ * Removes a model from this tree view.
103824
+ *
103825
+ * Does nothing if model not currently in tree view.
103826
+ *
103827
+ * @param {String} modelId ID of a model {@link Entity} in {@link Scene#models}.
103828
+ */
103829
+ removeModel(modelId) {
103830
+ if (!this._containerElement) {
103831
+ return;
103832
+ }
103833
+ const metaModel = this._metaModels[modelId];
103834
+ if (!metaModel) {
103835
+ return;
103836
+ }
103837
+ delete this._metaModels[modelId];
103838
+ this._createNodes();
103839
+ }
103840
+
103841
+ /**
103842
+ * Highlights the tree view node that represents the given object {@link Entity}.
103843
+ *
103844
+ * This causes the tree view to collapse, then expand to reveal the node, then highlight the node.
103845
+ *
103846
+ * If a node is previously highlighted, de-highlights that node and collapses the tree first.
103847
+ *
103848
+ * Note that if the TreeViewPlugin was configured with ````pruneEmptyNodes: true```` (default configuration), then the
103849
+ * node won't exist in the tree if it has no Entitys in the {@link Scene}. in that case, nothing will happen.
103850
+ *
103851
+ * Within the DOM, the node is represented by an ````<li>```` element. This method will add a ````.highlighted-node```` class to
103852
+ * the element to make it appear highlighted, removing that class when de-highlighting it again. See the CSS rules
103853
+ * in the TreeViewPlugin examples for an example of that class.
103854
+ *
103855
+ * @param {String} objectId ID of the {@link Entity}.
103856
+ */
103857
+ showNode(objectId) {
103858
+ if (this._showListItemElementId) {
103859
+ this.unShowNode();
103860
+ }
103861
+ const node = this._objectNodes[objectId];
103862
+ if (!node) {
103863
+ return; // Node may not exist for the given object if (this._pruneEmptyNodes == true)
103864
+ }
103865
+ const nodeId = node.nodeId;
103866
+ const switchElementId = "switch-" + nodeId;
103867
+ const switchElement = document.getElementById(switchElementId);
103868
+ if (switchElement) {
103869
+ this._expandSwitchElement(switchElement);
103870
+ switchElement.scrollIntoView();
103871
+ return;
103872
+ }
103873
+ const path = [];
103874
+ path.unshift(node);
103875
+ let parent = node.parent;
103876
+ while (parent) {
103877
+ path.unshift(parent);
103878
+ parent = parent.parent;
103879
+ }
103880
+ for (let i = 0, len = path.length; i < len; i++) {
103881
+ const node = path[i];
103882
+ const nodeId = node.nodeId;
103883
+ const switchElementId = "switch-" + nodeId;
103884
+ const switchElement = document.getElementById(switchElementId);
103885
+ if (switchElement) {
103886
+ this._expandSwitchElement(switchElement);
103887
+ }
103888
+ }
103889
+ const listItemElementId = 'node-' + nodeId;
103890
+ const listItemElement = document.getElementById(listItemElementId);
103891
+ listItemElement.scrollIntoView({block: "center"});
103892
+ listItemElement.classList.add("highlighted-node");
103893
+ this._showListItemElementId = listItemElementId;
103894
+ }
103895
+
103896
+ /**
103897
+ * De-highlights the node previously shown with {@link TreeViewPlugin#showNode}.
103898
+ *
103899
+ * Does nothing if no node is currently shown.
103900
+ *
103901
+ * If the node is currently scrolled into view, keeps the node in view.
103902
+ */
103903
+ unShowNode() {
103904
+ if (!this._showListItemElementId) {
103905
+ return;
103906
+ }
103907
+ const listItemElement = document.getElementById(this._showListItemElementId);
103908
+ if (!listItemElement) {
103909
+ this._showListItemElementId = null;
103910
+ return;
103911
+ }
103912
+ listItemElement.classList.remove("highlighted-node");
103913
+ this._showListItemElementId = null;
103914
+ }
103915
+
103916
+ /**
103917
+ * Expands the tree to the given depth.
103918
+ *
103919
+ * Collapses the tree first.
103920
+ *
103921
+ * @param {Number} depth Depth to expand to.
103922
+ */
103923
+ expandToDepth(depth) {
103924
+ this.collapse();
103925
+ const expand = (node, countDepth) => {
103926
+ if (countDepth === depth) {
103927
+ return;
103928
+ }
103929
+ const nodeId = node.nodeId;
103930
+ const switchElementId = "switch-" + nodeId;
103931
+ const switchElement = document.getElementById(switchElementId);
103932
+ if (switchElement) {
103933
+ this._expandSwitchElement(switchElement);
103934
+ const childNodes = node.children;
103935
+ for (var i = 0, len = childNodes.length; i < len; i++) {
103936
+ const childNode = childNodes[i];
103937
+ expand(childNode, countDepth + 1);
103938
+ }
103939
+ }
103940
+ };
103941
+ for (let i = 0, len = this._rootNodes.length; i < len; i++) {
103942
+ const rootNode = this._rootNodes[i];
103943
+ expand(rootNode, 0);
103944
+ }
103945
+ }
103946
+
103947
+ /**
103948
+ * Closes all the nodes in the tree.
103949
+ */
103950
+ collapse() {
103951
+ for (let i = 0, len = this._rootNodes.length; i < len; i++) {
103952
+ const rootNode = this._rootNodes[i];
103953
+ const objectId = rootNode.objectId;
103954
+ this._collapseNode(objectId);
103955
+ }
103956
+ }
103957
+
103958
+ /**
103959
+ * Iterates over a subtree of the tree view's {@link TreeViewNode}s, calling the given callback for each
103960
+ * node in depth-first pre-order.
103961
+ *
103962
+ * @param {TreeViewNode} node Root of the subtree.
103963
+ * @param {Function} callback Callback called at each {@link TreeViewNode}, with the TreeViewNode given as the argument.
103964
+ */
103965
+ withNodeTree(node, callback) {
103966
+ callback(node);
103967
+ const children = node.children;
103968
+ if (!children) {
103969
+ return;
103970
+ }
103971
+ for (let i = 0, len = children.length; i < len; i++) {
103972
+ this.withNodeTree(children[i], callback);
103973
+ }
103974
+ }
103975
+
103976
+ /**
103977
+ * Destroys this TreeViewPlugin.
103978
+ */
103979
+ destroy() {
103980
+ if (!this._containerElement) {
103981
+ return;
103982
+ }
103983
+ this._metaModels = {};
103984
+ if (this._rootElement && !this._destroyed) {
103985
+ this._rootElement.parentNode.removeChild(this._rootElement);
103986
+ this._viewer.scene.off(this._onObjectVisibility);
103987
+ this._destroyed = true;
103988
+ }
103989
+ super.destroy();
103990
+ }
103991
+
103992
+ _createNodes() {
103993
+ if (this._rootElement) {
103994
+ this._rootElement.parentNode.removeChild(this._rootElement);
103995
+ this._rootElement = null;
103996
+ }
103997
+
103998
+ this._rootNodes = [];
103999
+ this._objectNodes = {};
104000
+ this._validate();
104001
+ if (this.valid || (this._hierarchy !== "storeys")) {
104002
+ this._createEnabledNodes();
104003
+ } else {
104004
+ this._createDisabledNodes();
104005
+ }
104006
+ }
104007
+
104008
+ _validate() {
104009
+ this.errors = [];
104010
+ switch (this._hierarchy) {
104011
+ case "storeys":
104012
+ this.valid = this._validateMetaModelForStoreysHierarchy();
104013
+ break;
104014
+ case "types":
104015
+ this.valid = (this._rootNodes.length > 0);
104016
+ break;
104017
+ case "containment":
104018
+ default:
104019
+ this.valid = (this._rootNodes.length > 0);
103502
104020
  break;
103503
104021
  }
103504
104022
  return this.valid;
103505
104023
  }
103506
104024
 
104025
+ _validateMetaModelForStoreysHierarchy(level = 0, ctx, buildingNode) {
104026
+ // ctx = ctx || {
104027
+ // foundIFCBuildingStoreys: false
104028
+ // };
104029
+ // const metaObjectType = metaObject.type;
104030
+ // const children = metaObject.children;
104031
+ // if (metaObjectType === "IfcBuilding") {
104032
+ // buildingNode = true;
104033
+ // } else if (metaObjectType === "IfcBuildingStorey") {
104034
+ // if (!buildingNode) {
104035
+ // errors.push("Can't build storeys hierarchy: IfcBuildingStorey found without parent IfcBuilding");
104036
+ // return false;
104037
+ // }
104038
+ // ctx.foundIFCBuildingStoreys = true;
104039
+ // }
104040
+ // if (children) {
104041
+ // for (let i = 0, len = children.length; i < len; i++) {
104042
+ // const childMetaObject = children[i];
104043
+ // if (!this._validateMetaModelForStoreysHierarchy(childMetaObject, errors, level + 1, ctx, buildingNode)) {
104044
+ // return false;
104045
+ // }
104046
+ // }
104047
+ // }
104048
+ // if (level === 0) {
104049
+ // if (!ctx.foundIFCBuildingStoreys) {
104050
+ // // errors.push("Can't build storeys hierarchy: no IfcBuildingStoreys found");
104051
+ // }
104052
+ // }
104053
+ return true;
104054
+ }
104055
+
103507
104056
  _createEnabledNodes() {
103508
104057
  if (this._pruneEmptyNodes) {
103509
104058
  this._findEmptyNodes();
@@ -103512,7 +104061,7 @@ class ModelTreeView {
103512
104061
  case "storeys":
103513
104062
  this._createStoreysNodes();
103514
104063
  if (this._rootNodes.length === 0) {
103515
- this._treeViewPlugin.error("Failed to build storeys hierarchy for model '" + this.metaModel.id + "' - perhaps this model is not an IFC model?");
104064
+ this.error("Failed to build storeys hierarchy");
103516
104065
  }
103517
104066
  break;
103518
104067
  case "types":
@@ -103532,32 +104081,42 @@ class ModelTreeView {
103532
104081
 
103533
104082
  _createDisabledNodes() {
103534
104083
 
103535
- const metaObject = this._rootMetaObject;
103536
- const metaObjectType = metaObject.type;
103537
- const metaObjectName = metaObject.name;
103538
-
103539
- const rootName = ((metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType);
103540
-
103541
104084
  const ul = document.createElement('ul');
103542
- const li = document.createElement('li');
103543
- ul.appendChild(li);
103544
- this._containerElement.appendChild(ul);
103545
104085
  this._rootElement = ul;
104086
+ this._containerElement.appendChild(ul);
103546
104087
 
103547
- const switchElement = document.createElement('a');
103548
- switchElement.href = '#';
103549
- switchElement.textContent = '!';
103550
- switchElement.classList.add('warn');
103551
- switchElement.classList.add('warning');
103552
- li.appendChild(switchElement);
104088
+ const rootMetaObjects = this._viewer.metaScene.rootMetaObjects;
104089
+ for (let i = 0, len = rootMetaObjects.length; i < len; i++) {
104090
+ const rootMetaObject = rootMetaObjects[i];
104091
+ const metaObjectType = rootMetaObject.type;
104092
+ const metaObjectName = rootMetaObject.name;
104093
+ const rootName = ((metaObjectName && metaObjectName !== ""
104094
+ && metaObjectName !== "Undefined"
104095
+ && metaObjectName !== "Default") ? metaObjectName : metaObjectType);
104096
+ const li = document.createElement('li');
104097
+ ul.appendChild(li);
104098
+ const switchElement = document.createElement('a');
104099
+ switchElement.href = '#';
104100
+ switchElement.textContent = '!';
104101
+ switchElement.classList.add('warn');
104102
+ switchElement.classList.add('warning');
104103
+ li.appendChild(switchElement);
104104
+ const span = document.createElement('span');
104105
+ span.textContent = rootName;
104106
+ li.appendChild(span);
104107
+ }
104108
+ }
103553
104109
 
103554
- const span = document.createElement('span');
103555
- span.textContent = rootName;
103556
- li.appendChild(span);
104110
+
104111
+ _findEmptyNodes() {
104112
+ const rootMetaObjects = this._viewer.metaScene.rootMetaObjects;
104113
+ for (let i = 0, len = rootMetaObjects.length; i < len; i++) {
104114
+ this._findEmptyNodes2(rootMetaObjects[i]);
104115
+ }
103557
104116
  }
103558
104117
 
103559
- _findEmptyNodes(metaObject = this._rootMetaObject, countEntities = 0) {
103560
- const viewer = this._treeViewPlugin.viewer;
104118
+ _findEmptyNodes2(metaObject, countEntities = 0) {
104119
+ const viewer = this.viewer;
103561
104120
  const scene = viewer.scene;
103562
104121
  const children = metaObject.children;
103563
104122
  const objectId = metaObject.id;
@@ -103569,18 +104128,21 @@ class ModelTreeView {
103569
104128
  if (children) {
103570
104129
  for (let i = 0, len = children.length; i < len; i++) {
103571
104130
  const childMetaObject = children[i];
103572
- childMetaObject._countEntities = this._findEmptyNodes(childMetaObject);
104131
+ childMetaObject._countEntities = this._findEmptyNodes2(childMetaObject);
103573
104132
  metaObject._countEntities += childMetaObject._countEntities;
103574
104133
  }
103575
104134
  }
103576
104135
  return metaObject._countEntities;
103577
104136
  }
103578
104137
 
103579
- _createStoreysNodes(
103580
- metaObject = this._rootMetaObject,
103581
- buildingNode,
103582
- storeyNode,
103583
- typeNodes) {
104138
+ _createStoreysNodes() {
104139
+ const rootMetaObjects = this._viewer.metaScene.rootMetaObjects;
104140
+ for (let id in rootMetaObjects) {
104141
+ this._createStoreysNodes2(rootMetaObjects[id], null, null, null);
104142
+ }
104143
+ }
104144
+
104145
+ _createStoreysNodes2(metaObject, buildingNode, storeyNode, typeNodes) {
103584
104146
  if (this._pruneEmptyNodes && (metaObject._countEntities === 0)) {
103585
104147
  return;
103586
104148
  }
@@ -103590,7 +104152,7 @@ class ModelTreeView {
103590
104152
  const objectId = metaObject.id;
103591
104153
  if (metaObjectType === "IfcBuilding") {
103592
104154
  buildingNode = {
103593
- nodeId: this._objectToNodeID(objectId),
104155
+ nodeId: objectId,
103594
104156
  objectId: objectId,
103595
104157
  title: this._rootName || ((metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType),
103596
104158
  type: metaObjectType,
@@ -103605,11 +104167,11 @@ class ModelTreeView {
103605
104167
  this._objectNodes[buildingNode.objectId] = buildingNode;
103606
104168
  } else if (metaObjectType === "IfcBuildingStorey") {
103607
104169
  if (!buildingNode) {
103608
- this._treeViewPlugin.error("Failed to build storeys hierarchy for model '" + this.metaModel.id + "' - model does not have an IfcBuilding object, or is not an IFC model");
104170
+ this.error("Failed to build storeys hierarchy for model '" + this.metaModel.id + "' - model does not have an IfcBuilding object, or is not an IFC model");
103609
104171
  return;
103610
104172
  }
103611
104173
  storeyNode = {
103612
- nodeId: this._objectToNodeID(objectId),
104174
+ nodeId: objectId,
103613
104175
  objectId: objectId,
103614
104176
  title: (metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType,
103615
104177
  type: metaObjectType,
@@ -103632,7 +104194,7 @@ class ModelTreeView {
103632
104194
  let typeNode = typeNodes[metaObjectType];
103633
104195
  if (!typeNode) {
103634
104196
  const typeNodeObjectId = storeyNode.objectId + "." + metaObjectType;
103635
- const typeNodeNodeId = this._objectToNodeID(typeNodeObjectId);
104197
+ const typeNodeNodeId = typeNodeObjectId;
103636
104198
  typeNode = {
103637
104199
  nodeId: typeNodeNodeId,
103638
104200
  objectId: typeNodeObjectId,
@@ -103650,7 +104212,7 @@ class ModelTreeView {
103650
104212
  typeNodes[metaObjectType] = typeNode;
103651
104213
  }
103652
104214
  const node = {
103653
- nodeId: this._objectToNodeID(objectId),
104215
+ nodeId: objectId,
103654
104216
  objectId: objectId,
103655
104217
  title: (metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType,
103656
104218
  type: metaObjectType,
@@ -103669,12 +104231,19 @@ class ModelTreeView {
103669
104231
  if (children) {
103670
104232
  for (let i = 0, len = children.length; i < len; i++) {
103671
104233
  const childMetaObject = children[i];
103672
- this._createStoreysNodes(childMetaObject, buildingNode, storeyNode, typeNodes);
104234
+ this._createStoreysNodes2(childMetaObject, buildingNode, storeyNode, typeNodes);
103673
104235
  }
103674
104236
  }
103675
104237
  }
103676
104238
 
103677
- _createTypesNodes(metaObject = this._rootMetaObject, rootNode, typeNodes) {
104239
+ _createTypesNodes() {
104240
+ const rootMetaObjects = this._viewer.metaScene.rootMetaObjects;
104241
+ for (let id in rootMetaObjects) {
104242
+ this._createTypesNodes2(rootMetaObjects[id], null, null);
104243
+ }
104244
+ }
104245
+
104246
+ _createTypesNodes2(metaObject, rootNode, typeNodes) {
103678
104247
  if (this._pruneEmptyNodes && (metaObject._countEntities === 0)) {
103679
104248
  return;
103680
104249
  }
@@ -103682,9 +104251,9 @@ class ModelTreeView {
103682
104251
  const metaObjectName = metaObject.name;
103683
104252
  const children = metaObject.children;
103684
104253
  const objectId = metaObject.id;
103685
- if (metaObject.id === this._rootMetaObject.id) {
104254
+ if (!metaObject.parent) {
103686
104255
  rootNode = {
103687
- nodeId: this._objectToNodeID(objectId),
104256
+ nodeId: objectId,
103688
104257
  objectId: objectId,
103689
104258
  title: this._rootName || ((metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType),
103690
104259
  type: metaObjectType,
@@ -103706,7 +104275,7 @@ class ModelTreeView {
103706
104275
  let typeNode = typeNodes[metaObjectType];
103707
104276
  if (!typeNode) {
103708
104277
  typeNode = {
103709
- nodeId: this._objectToNodeID(rootNode.objectId + "." + metaObjectType),
104278
+ nodeId: rootNode.objectId + "." + metaObjectType,
103710
104279
  objectId: rootNode.objectId + "." + metaObjectType,
103711
104280
  title: metaObjectType,
103712
104281
  type: metaObjectType,
@@ -103722,7 +104291,7 @@ class ModelTreeView {
103722
104291
  typeNodes[metaObjectType] = typeNode;
103723
104292
  }
103724
104293
  const node = {
103725
- nodeId: this._objectToNodeID(objectId),
104294
+ nodeId: objectId,
103726
104295
  objectId: objectId,
103727
104296
  title: (metaObjectName && metaObjectName !== "" && metaObjectName !== "Default") ? metaObjectName : metaObjectType,
103728
104297
  type: metaObjectType,
@@ -103741,12 +104310,19 @@ class ModelTreeView {
103741
104310
  if (children) {
103742
104311
  for (let i = 0, len = children.length; i < len; i++) {
103743
104312
  const childMetaObject = children[i];
103744
- this._createTypesNodes(childMetaObject, rootNode, typeNodes);
104313
+ this._createTypesNodes2(childMetaObject, rootNode, typeNodes);
103745
104314
  }
103746
104315
  }
103747
104316
  }
103748
104317
 
103749
- _createContainmentNodes(metaObject = this._rootMetaObject, parent) {
104318
+ _createContainmentNodes() {
104319
+ const rootMetaObjects = this._viewer.metaScene.rootMetaObjects;
104320
+ for (let id in rootMetaObjects) {
104321
+ this._createContainmentNodes2(rootMetaObjects[id], null);
104322
+ }
104323
+ }
104324
+
104325
+ _createContainmentNodes2(metaObject, parent) {
103750
104326
  if (this._pruneEmptyNodes && (metaObject._countEntities === 0)) {
103751
104327
  return;
103752
104328
  }
@@ -103755,7 +104331,7 @@ class ModelTreeView {
103755
104331
  const children = metaObject.children;
103756
104332
  const objectId = metaObject.id;
103757
104333
  const node = {
103758
- nodeId: this._objectToNodeID(objectId),
104334
+ nodeId: objectId,
103759
104335
  objectId: objectId,
103760
104336
  title: (!parent) ? (this._rootName || metaObjectName) : (metaObjectName && metaObjectName !== "" && metaObjectName !== "Undefined" && metaObjectName !== "Default") ? metaObjectName : metaObjectType,
103761
104337
  type: metaObjectType,
@@ -103776,7 +104352,7 @@ class ModelTreeView {
103776
104352
  if (children) {
103777
104353
  for (let i = 0, len = children.length; i < len; i++) {
103778
104354
  const childMetaObject = children[i];
103779
- this._createContainmentNodes(childMetaObject, node);
104355
+ this._createContainmentNodes2(childMetaObject, node);
103780
104356
  }
103781
104357
  }
103782
104358
  }
@@ -103806,7 +104382,7 @@ class ModelTreeView {
103806
104382
  }
103807
104383
 
103808
104384
  _getSpatialSortFunc() { // Creates cached sort func with Viewer in scope
103809
- const viewer = this._treeViewPlugin.viewer;
104385
+ const viewer = this.viewer;
103810
104386
  const scene = viewer.scene;
103811
104387
  const camera = scene.camera;
103812
104388
  const metaScene = viewer.metaScene;
@@ -103851,8 +104427,7 @@ class ModelTreeView {
103851
104427
  }
103852
104428
 
103853
104429
  _synchNodesToEntities() {
103854
- const rootMetaObject = this._rootMetaObject;
103855
- const objectIds = rootMetaObject.getObjectIDsInSubtree();
104430
+ const objectIds = Object.keys(this.viewer.metaScene.metaObjects);
103856
104431
  const metaObjects = this._viewer.metaScene.metaObjects;
103857
104432
  const objects = this._viewer.scene.objects;
103858
104433
  for (let i = 0, len = objectIds.length; i < len; i++) {
@@ -103919,7 +104494,7 @@ class ModelTreeView {
103919
104494
  //const nodeId = this._objectToNodeID(node.objectId);
103920
104495
  const nodeId = node.nodeId;
103921
104496
  if (node.xrayed) {
103922
- nodeElement.classList.add('xrayed-node');
104497
+ nodeElement.classList.add('xrayed-node');
103923
104498
  }
103924
104499
  nodeElement.id = 'node-' + nodeId;
103925
104500
  if (node.children.length > 0) {
@@ -103929,7 +104504,7 @@ class ModelTreeView {
103929
104504
  switchElement.id = switchElementId;
103930
104505
  switchElement.textContent = '+';
103931
104506
  switchElement.classList.add('plus');
103932
- switchElement.addEventListener('click', this.switchExpandHandler);
104507
+ switchElement.addEventListener('click', this._switchExpandHandler);
103933
104508
  nodeElement.appendChild(switchElement);
103934
104509
  }
103935
104510
  const checkbox = document.createElement('input');
@@ -103943,19 +104518,19 @@ class ModelTreeView {
103943
104518
  span.textContent = node.title;
103944
104519
  nodeElement.appendChild(span);
103945
104520
  span.oncontextmenu = (e) => {
103946
- this._treeViewPlugin.fire("contextmenu", {
104521
+ this.fire("contextmenu", {
103947
104522
  event: e,
103948
104523
  viewer: this._viewer,
103949
- treeViewPlugin: this._treeViewPlugin,
104524
+ treeViewPlugin: this,
103950
104525
  treeViewNode: node
103951
104526
  });
103952
104527
  e.preventDefault();
103953
104528
  };
103954
104529
  span.onclick = (e) => {
103955
- this._treeViewPlugin.fire("nodeTitleClicked", {
104530
+ this.fire("nodeTitleClicked", {
103956
104531
  event: e,
103957
104532
  viewer: this._viewer,
103958
- treeViewPlugin: this._treeViewPlugin,
104533
+ treeViewPlugin: this,
103959
104534
  treeViewNode: node
103960
104535
  });
103961
104536
  e.preventDefault();
@@ -103963,103 +104538,6 @@ class ModelTreeView {
103963
104538
  return nodeElement;
103964
104539
  }
103965
104540
 
103966
- /**
103967
- * @private
103968
- * @param depth
103969
- */
103970
- expandToDepth(depth) {
103971
- const expand = (node, countDepth) => {
103972
- if (countDepth === depth) {
103973
- return;
103974
- }
103975
- const nodeId = node.nodeId;
103976
- const switchElementId = "switch-" + nodeId;
103977
- const switchElement = document.getElementById(switchElementId);
103978
- if (switchElement) {
103979
- this._expandSwitchElement(switchElement);
103980
- const childNodes = node.children;
103981
- for (var i = 0, len = childNodes.length; i < len; i++) {
103982
- const childNode = childNodes[i];
103983
- expand(childNode, countDepth + 1);
103984
- }
103985
- }
103986
- };
103987
- for (let i = 0, len = this._rootNodes.length; i < len; i++) {
103988
- const rootNode = this._rootNodes[i];
103989
- expand(rootNode, 0);
103990
- }
103991
- }
103992
-
103993
- /**
103994
- * @private
103995
- */
103996
- collapse() {
103997
- for (let i = 0, len = this._rootNodes.length; i < len; i++) {
103998
- const rootNode = this._rootNodes[i];
103999
- const objectId = rootNode.objectId;
104000
- this._collapseNode(objectId);
104001
- }
104002
- }
104003
-
104004
- /**
104005
- * @private
104006
- * @param objectId
104007
- */
104008
- showNode(objectId) {
104009
- if (this._showListItemElementId) {
104010
- this.unShowNode();
104011
- }
104012
- const node = this._objectNodes[objectId];
104013
- if (!node) {
104014
- return; // Node may not exist for the given object if (this._pruneEmptyNodes == true)
104015
- }
104016
- const nodeId = node.nodeId;
104017
- const switchElementId = "switch-" + nodeId;
104018
- const switchElement = document.getElementById(switchElementId);
104019
- if (switchElement) {
104020
- this._expandSwitchElement(switchElement);
104021
- switchElement.scrollIntoView();
104022
- return;
104023
- }
104024
- const path = [];
104025
- path.unshift(node);
104026
- let parent = node.parent;
104027
- while (parent) {
104028
- path.unshift(parent);
104029
- parent = parent.parent;
104030
- }
104031
- for (let i = 0, len = path.length; i < len; i++) {
104032
- const node = path[i];
104033
- const nodeId = node.nodeId;
104034
- const switchElementId = "switch-" + nodeId;
104035
- const switchElement = document.getElementById(switchElementId);
104036
- if (switchElement) {
104037
- this._expandSwitchElement(switchElement);
104038
- }
104039
- }
104040
- const listItemElementId = 'node-' + nodeId;
104041
- const listItemElement = document.getElementById(listItemElementId);
104042
- listItemElement.scrollIntoView({block: "center"});
104043
- listItemElement.classList.add("highlighted-node");
104044
- this._showListItemElementId = listItemElementId;
104045
- }
104046
-
104047
- /**
104048
- * @private
104049
- */
104050
- unShowNode() {
104051
- if (!this._showListItemElementId) {
104052
- return;
104053
- }
104054
- const listItemElement = document.getElementById(this._showListItemElementId);
104055
- if (!listItemElement) {
104056
- this._showListItemElementId = null;
104057
- return;
104058
- }
104059
- listItemElement.classList.remove("highlighted-node");
104060
- this._showListItemElementId = null;
104061
- }
104062
-
104063
104541
  _expandSwitchElement(switchElement) {
104064
104542
  const parentElement = switchElement.parentElement;
104065
104543
  const expanded = parentElement.getElementsByTagName('li')[0];
@@ -104067,7 +104545,7 @@ class ModelTreeView {
104067
104545
  return;
104068
104546
  }
104069
104547
  const nodeId = parentElement.id.replace('node-', '');
104070
- const objectId = this._nodeToObjectID(nodeId);
104548
+ const objectId = nodeId;
104071
104549
  const switchNode = this._objectNodes[objectId];
104072
104550
  const childNodes = switchNode.children;
104073
104551
  const nodeElements = childNodes.map((node) => {
@@ -104081,12 +104559,12 @@ class ModelTreeView {
104081
104559
  switchElement.classList.remove('plus');
104082
104560
  switchElement.classList.add('minus');
104083
104561
  switchElement.textContent = '-';
104084
- switchElement.removeEventListener('click', this.switchExpandHandler);
104085
- switchElement.addEventListener('click', this.switchCollapseHandler);
104562
+ switchElement.removeEventListener('click', this._switchExpandHandler);
104563
+ switchElement.addEventListener('click', this._switchCollapseHandler);
104086
104564
  }
104087
104565
 
104088
104566
  _collapseNode(objectId) {
104089
- const nodeId = this._objectToNodeID(objectId);
104567
+ const nodeId = objectId;
104090
104568
  const switchElementId = "switch-" + nodeId;
104091
104569
  const switchElement = document.getElementById(switchElementId);
104092
104570
  this._collapseSwitchElement(switchElement);
@@ -104108,641 +104586,8 @@ class ModelTreeView {
104108
104586
  switchElement.classList.remove('minus');
104109
104587
  switchElement.classList.add('plus');
104110
104588
  switchElement.textContent = '+';
104111
- switchElement.removeEventListener('click', this.switchCollapseHandler);
104112
- switchElement.addEventListener('click', this.switchExpandHandler);
104113
- }
104114
-
104115
- /**
104116
- * Destroys this ModelTreeView.
104117
- * @private
104118
- */
104119
- destroy() {
104120
- if (this._rootElement && !this._destroyed) {
104121
- this._rootElement.parentNode.removeChild(this._rootElement);
104122
- this._viewer.scene.off(this._onObjectVisibility);
104123
- this._destroyed = true;
104124
- idMap.removeItem(this._id);
104125
- }
104126
- }
104127
- }
104128
-
104129
- /**
104130
- * @desc A {@link Viewer} plugin that provides an HTML tree view to navigate the IFC elements in models.
104131
- * <br>
104132
- *
104133
- * <a href="https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_WestRiverSideHospital" style="border: 1px solid black;"><img src="http://xeokit.io/img/docs/TreeViewPlugin/TreeViewPlugin.png"></a>
104134
- *
104135
- * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_WestRiverSideHospital)]
104136
- *
104137
- * ## Overview
104138
- *
104139
- * * A fast HTML tree view, with zero external dependencies, that works with huge numbers of objects.
104140
- * * Each tree node has a checkbox to control the visibility of its object.
104141
- * * Has three hierarchy modes: "containment", "types" and "storeys".
104142
- * * Automatically contains all models (that have metadata) that are currently in the {@link Scene}.
104143
- * * Sorts tree nodes by default - spatially, from top-to-bottom for ````IfcBuildingStorey```` nodes, and alphanumerically for other nodes.
104144
- * * Allows custom CSS styling.
104145
- * * Use {@link ContextMenu} to create a context menu for the tree nodes.
104146
- *
104147
- * ## Credits
104148
- *
104149
- * TreeViewPlugin is based on techniques described in [*Super Fast Tree View in JavaScript*](https://chrissmith.xyz/super-fast-tree-view-in-javascript/) by [Chris Smith](https://twitter.com/chris22smith).
104150
- *
104151
- * ## Usage
104152
- *
104153
- * In the example below, we'll add a TreeViewPlugin which, by default, will automatically show the structural
104154
- * hierarchy of the IFC elements in each model we load.
104155
- *
104156
- * Then we'll use an {@link XKTLoaderPlugin} to load the Schependomlaan model from an
104157
- * [.xkt file](https://github.com/xeokit/xeokit-sdk/tree/master/examples/models/xkt/schependomlaan).
104158
- *
104159
- * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#BIMOffline_XKT_Schependomlaan)]
104160
- *
104161
- * ````javascript
104162
- * import {Viewer, XKTLoaderPlugin, TreeViewPlugin} from "xeokit-sdk.es.js";
104163
- *
104164
- * const viewer = new Viewer({
104165
- * canvasId: "myCanvas",
104166
- * transparent: true
104167
- * });
104168
- *
104169
- * viewer.camera.eye = [-2.56, 8.38, 8.27];
104170
- * viewer.camera.look = [13.44, 3.31, -14.83];
104171
- * viewer.camera.up = [0.10, 0.98, -0.14];
104172
- *
104173
- * const treeView = new TreeViewPlugin(viewer, {
104174
- * containerElement: document.getElementById("myTreeViewContainer")
104175
- * });
104176
- *
104177
- * const xktLoader = new XKTLoaderPlugin(viewer);
104178
- *
104179
- * const model = xktLoader.load({
104180
- * id: "myModel",
104181
- * src: "./models/xkt/Schependomlaan.xkt",
104182
- * edges: true
104183
- * });
104184
- * ````
104185
- *
104186
- * ## Manually Adding Models
104187
- *
104188
- * Instead of adding models automatically, we can control which models appear in our TreeViewPlugin by adding them manually.
104189
- *
104190
- * In the next example, we'll configure the TreeViewPlugin to not add models automatically. Then, once the model
104191
- * has loaded, we'll add it manually using {@link TreeViewPlugin#addModel}.
104192
- *
104193
- * ````javascript
104194
- * const treeView = new TreeViewPlugin(viewer, {
104195
- * containerElement: document.getElementById("myTreeViewContainer"),
104196
- * autoAddModels: false // <<---------------- Don't auto-add models
104197
- * });
104198
- *
104199
- * const xktLoader = new XKTLoaderPlugin(viewer);
104200
- *
104201
- * const model = xktLoader.load({
104202
- * id: "myModel",
104203
- * src: "./models/xkt/Schependomlaan.xkt",
104204
- * edges: true
104205
- * });
104206
- *
104207
- * model.on("loaded", () => {
104208
- * treeView.addModel(model.id);
104209
- * });
104210
- * ````
104211
- *
104212
- * Adding models manually also allows us to set some options for the model. For example, the ````rootName```` option allows us to provide a custom name for
104213
- * the root node, which is sometimes desirable when the model's "IfcProject" element's name is not suitable:
104214
- *
104215
- * ````javascript
104216
- * model.on("loaded", () => {
104217
- * treeView.addModel(model.id, {
104218
- * rootName: "Schependomlaan Model"
104219
- * });
104220
- * });
104221
- * ````
104222
- *
104223
- * ## Initially Expanding the Hierarchy
104224
- *
104225
- * We can also configure TreeViewPlugin to initially expand each model's nodes to a given depth.
104226
- *
104227
- * Let's automatically expand the first three nodes from the root, for every model added:
104228
- *
104229
- * ````javascript
104230
- * const treeView = new TreeViewPlugin(viewer, {
104231
- * containerElement: document.getElementById("myTreeViewContainer"),
104232
- * autoExpandDepth: 3
104233
- * });
104234
- * ````
104235
- *
104236
- * ## Showing a Node by ID
104237
- *
104238
- * We can show a given node using its ID. This causes the TreeViewPlugin to collapse, then expand and scroll the node into view, then highlight the node.
104239
- *
104240
- * See the documentation for the {@link TreeViewPlugin#showNode} method for more information, including how to define a custom highlighted appearance for the node using CSS.
104241
- *
104242
- * Let's make the TreeViewPlugin show the node corresponding to whatever object {@link Entity} that we pick:
104243
- *
104244
- * ````javascript
104245
- * viewer.cameraControl.on("picked", function (e) {
104246
- * var objectId = e.entity.id;
104247
- * treeView.showNode(objectId);
104248
- * });
104249
- * ````
104250
- *
104251
- * This will de-highlight any node that was previously shown by this method.
104252
- *
104253
- * Note that this method only works if the picked {@link Entity} is an object that belongs to a model that's represented in the TreeViewPlugin.
104254
- *
104255
- * ## Customizing Appearance
104256
- *
104257
- * We can customize the appearance of our TreeViewPlugin by defining custom CSS for its HTML
104258
- * elements. See our example's [source code](https://github.com/xeokit/xeokit-sdk/blob/master/examples/BIMOffline_XKT_Schependomlaan.html)
104259
- * for an example of custom CSS rules.
104260
- *
104261
- * ## Model Hierarchies
104262
- *
104263
- * TreeViewPlugin has three hierarchies for organizing its nodes:
104264
- *
104265
- * * "containment" - organizes the tree nodes to indicate the containment hierarchy of the {@link MetaObject}s.
104266
- * * "types" - groups nodes by their IFC types.
104267
- * * "storeys" - groups nodes within their ````IfcBuildingStoreys````, and sub-groups them by their IFC types.
104268
- *
104269
- * <br>
104270
- * The table below shows what the hierarchies look like:
104271
- * <br>
104272
- *
104273
- * | 1. Containment Hierarchy | 2. Types Hierarchy | 3. Storeys Hierarchy |
104274
- * |---|---|---|
104275
- * | <img src="http://xeokit.io/img/docs/TreeViewPlugin/structureMode.png"> | <img src="http://xeokit.io/img/docs/TreeViewPlugin/typesMode.png"> | <img src="http://xeokit.io/img/docs/TreeViewPlugin/storeysMode.png"> |
104276
- * <br>
104277
- *
104278
- * Let's create a TreeViewPlugin that groups nodes by their building stories and IFC types:
104279
- *
104280
- * ````javascript
104281
- * const treeView = new TreeViewPlugin(viewer, {
104282
- * containerElement: document.getElementById("myTreeViewContainer"),
104283
- * hierarchy: "stories"
104284
- * });
104285
- * ````
104286
- *
104287
- * ## Sorting Nodes
104288
- *
104289
- * TreeViewPlugin sorts its tree nodes by default. For a "storeys" hierarchy, it orders ````IfcBuildingStorey```` nodes
104290
- * spatially, with the node for the highest story at the top, down to the lowest at the bottom.
104291
- *
104292
- * For all the hierarchy types ("containment", "classes" and "storeys"), TreeViewPlugin sorts the other node types
104293
- * alphanumerically on their titles.
104294
- *
104295
- * If for some reason you need to prevent sorting, create your TreeViewPlugin with the option disabled, like so:
104296
- *
104297
- * ````javascript
104298
- * const treeView = new TreeViewPlugin(viewer, {
104299
- * containerElement: document.getElementById("myTreeViewContainer"),
104300
- * hierarchy: "stories",
104301
- * sortNodes: false // <<------ Disable node sorting
104302
- * });
104303
- * ````
104304
- *
104305
- * Note that, for all hierarchy modes, node sorting is only done for each model at the time that it is added to the TreeViewPlugin, and will not
104306
- * update dynamically if we later transform the {@link Entity}s corresponding to the nodes.
104307
- *
104308
- * ## Pruning empty nodes
104309
- *
104310
- * Sometimes a model contains subtrees of objects that don't have any geometry. These are models whose
104311
- * {@link MetaModel} contains trees of {@link MetaObject}s that don't have any {@link Entity}s in the {@link Scene}.
104312
- *
104313
- * For these models, the tree view would contain nodes that don't do anything in the Scene when we interact with them,
104314
- * which is undesirable.
104315
- *
104316
- * By default, TreeViewPlugin will not create nodes for those objects. However, we can override that behaviour if we want
104317
- * to have nodes for those objects (perhaps for debugging the model):
104318
- *
104319
- * ````javascript
104320
- * const treeView = new TreeViewPlugin(viewer, {
104321
- * containerElement: document.getElementById("myTreeViewContainer"),
104322
- * hierarchy: "stories",
104323
- * pruneEmptyNodes: false // <<------ Create nodes for object subtrees without geometry
104324
- * });
104325
- * ````
104326
- *
104327
- * ## Context Menu
104328
- *
104329
- * TreeViewPlugin fires a "contextmenu" event whenever we right-click on a tree node.
104330
- *
104331
- * The event contains:
104332
- *
104333
- * * ````event```` - the original [contextmenu](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event) [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
104334
- * * ````viewer```` - the {@link Viewer}
104335
- * * ````treeViewPlugin```` - the TreeViewPlugin
104336
- * * ````treeViewNode```` - the {@link TreeViewNode} representing the tree node
104337
- *<br><br>
104338
- *
104339
- * Let's use {@link ContextMenu} to show a simple context menu for the node we clicked.
104340
- *
104341
- * [[Run an example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)]
104342
- *
104343
- * ````javascript
104344
- * import {ContextMenu} from "../src/extras/ContextMenu/ContextMenu.js";
104345
- *
104346
- * const treeViewContextMenu = new ContextMenu({
104347
- * items: [
104348
- * [
104349
- * [
104350
- * {
104351
- * title: "Hide",
104352
- * doAction: function (context) {
104353
- * context.treeViewPlugin.withNodeTree(context.treeViewNode, (treeViewNode) => {
104354
- * if (treeViewNode.objectId) {
104355
- * const entity = context.viewer.scene.objects[treeViewNode.objectId];
104356
- * if (entity) {
104357
- * entity.visible = false;
104358
- * }
104359
- * }
104360
- * });
104361
- * }
104362
- * },
104363
- * {
104364
- * title: "Hide all",
104365
- * doAction: function (context) {
104366
- * context.viewer.scene.setObjectsVisible(context.viewer.scene.visibleObjectIds, false);
104367
- * }
104368
- * }
104369
- * ],
104370
- * [
104371
- * {
104372
- * title: "Show",
104373
- * doAction: function (context) {
104374
- * context.treeViewPlugin.withNodeTree(context.treeViewNode, (treeViewNode) => {
104375
- * if (treeViewNode.objectId) {
104376
- * const entity = context.viewer.scene.objects[treeViewNode.objectId];
104377
- * if (entity) {
104378
- * entity.visible = true;
104379
- * entity.xrayed = false;
104380
- * entity.selected = false;
104381
- * }
104382
- * }
104383
- * });
104384
- * }
104385
- * },
104386
- * {
104387
- * title: "Show all",
104388
- * doAction: function (context) {
104389
- * const scene = context.viewer.scene;
104390
- * scene.setObjectsVisible(scene.objectIds, true);
104391
- * scene.setObjectsXRayed(scene.xrayedObjectIds, false);
104392
- * scene.setObjectsSelected(scene.selectedObjectIds, false);
104393
- * }
104394
- * }
104395
- * ]
104396
- * ]
104397
- * ]
104398
- * });
104399
- *
104400
- * treeView.on("contextmenu", (e) => {
104401
- *
104402
- * const event = e.event; // MouseEvent
104403
- * const viewer = e.viewer; // Viewer
104404
- * const treeViewPlugin = e.treeViewPlugin; // TreeViewPlugin
104405
- * const treeViewNode = e.treeViewNode; // TreeViewNode
104406
- *
104407
- * treeViewContextMenu.show(e.event.pageX, e.event.pageY);
104408
- *
104409
- * treeViewContextMenu.context = {
104410
- * viewer: e.viewer,
104411
- * treeViewPlugin: e.treeViewPlugin,
104412
- * treeViewNode: e.treeViewNode
104413
- * };
104414
- * });
104415
- * ````
104416
- *
104417
- * ## Clicking Node Titles
104418
- *
104419
- * TreeViewPlugin fires a "nodeTitleClicked" event whenever we left-click on a tree node.
104420
- *
104421
- * Like the "contextmenu" event, this event contains:
104422
- *
104423
- * * ````event```` - the original [click](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event) [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
104424
- * * ````viewer```` - the {@link Viewer}
104425
- * * ````treeViewPlugin```` - the TreeViewPlugin
104426
- * * ````treeViewNode```` - the {@link TreeViewNode} representing the tree node
104427
- *<br><br>
104428
- *
104429
- * Let's register a callback to isolate and fit-to-view the {@link Entity}(s) represented by the node. This callback is
104430
- * going to X-ray all the other Entitys, fly the camera to fit the Entity(s) for the clicked node, then hide the other Entitys.
104431
- *
104432
- * [[Run an example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)]
104433
- *
104434
- * ````javascript
104435
- * treeView.on("nodeTitleClicked", (e) => {
104436
- * const scene = viewer.scene;
104437
- * const objectIds = [];
104438
- * e.treeViewPlugin.withNodeTree(e.treeViewNode, (treeViewNode) => {
104439
- * if (treeViewNode.objectId) {
104440
- * objectIds.push(treeViewNode.objectId);
104441
- * }
104442
- * });
104443
- * scene.setObjectsXRayed(scene.objectIds, true);
104444
- * scene.setObjectsVisible(scene.objectIds, true);
104445
- * scene.setObjectsXRayed(objectIds, false);
104446
- * viewer.cameraFlight.flyTo({
104447
- * aabb: scene.getAABB(objectIds),
104448
- * duration: 0.5
104449
- * }, () => {
104450
- * setTimeout(function () {
104451
- * scene.setObjectsVisible(scene.xrayedObjectIds, false);
104452
- * scene.setObjectsXRayed(scene.xrayedObjectIds, false);
104453
- * }, 500);
104454
- * });
104455
- * });
104456
- * ````
104457
- *
104458
- * To make the cursor change to a pointer when we hover over the node titles, and also to make the titles change to blue, we'll also define this CSS for the ````<span>```` elements
104459
- * that represent the titles of our TreeViewPlugin nodes:
104460
- *
104461
- * ````css
104462
- * #treeViewContainer ul li span:hover {
104463
- * color: blue;
104464
- * cursor: pointer;
104465
- * }
104466
- * ````
104467
- *
104468
- * @class TreeViewPlugin
104469
- */
104470
- class TreeViewPlugin extends Plugin {
104471
-
104472
- /**
104473
- * @constructor
104474
- *
104475
- * @param {Viewer} viewer The Viewer.
104476
- * @param {*} cfg Plugin configuration.
104477
- * @param {HTMLElement} cfg.containerElement DOM element to contain the TreeViewPlugin.
104478
- * @param {Boolean} [cfg.autoAddModels=true] When ````true```` (default), will automatically add each model as it's created. Set this ````false```` if you want to manually add models using {@link TreeViewPlugin#addModel} instead.
104479
- * @param {Number} [cfg.autoExpandDepth] Optional depth to which to initially expand the tree.
104480
- * @param {String} [cfg.hierarchy="containment"] How to organize the tree nodes: "containment", "storeys" or "types". See the class documentation for details.
104481
- * @param {Boolean} [cfg.sortNodes=true] When true, will sort the children of each node. For a "storeys" hierarchy, the
104482
- * ````IfcBuildingStorey```` nodes will be ordered spatially, from the highest storey down to the lowest, on the
104483
- * vertical World axis. For all hierarchy types, other node types will be ordered in the ascending alphanumeric order of their titles.
104484
- * @param {Boolean} [cfg.pruneEmptyNodes=true] When true, will not contain nodes that don't have content in the {@link Scene}. These are nodes whose {@link MetaObject}s don't have {@link Entity}s.
104485
- */
104486
- constructor(viewer, cfg = {}) {
104487
-
104488
- super("TreeViewPlugin", viewer);
104489
-
104490
- if (!cfg.containerElement) {
104491
- this.error("Config expected: containerElement");
104492
- return;
104493
- }
104494
-
104495
- this._containerElement = cfg.containerElement;
104496
- this._modelTreeViews = {};
104497
- this._autoAddModels = (cfg.autoAddModels !== false);
104498
- this._autoExpandDepth = (cfg.autoExpandDepth || 0);
104499
- this._sortNodes = (cfg.sortNodes !== false);
104500
- this._pruneEmptyNodes = (cfg.pruneEmptyNodes !== false);
104501
-
104502
- if (this._autoAddModels) {
104503
- const modelIds = Object.keys(this.viewer.metaScene.metaModels);
104504
- for (let i = 0, len = modelIds.length; i < len; i++) {
104505
- const modelId = modelIds[i];
104506
- this.addModel(modelId);
104507
- }
104508
- this.viewer.scene.on("modelLoaded", (modelId) => {
104509
- if (this.viewer.metaScene.metaModels[modelId]) {
104510
- this.addModel(modelId);
104511
- }
104512
- });
104513
- }
104514
-
104515
- this.hierarchy = cfg.hierarchy;
104516
- }
104517
-
104518
- /**
104519
- * Returns the map of {@link ModelTreeView}s.
104520
- *
104521
- * Each ModelTreeView is mapped to the ID of its model.
104522
- *
104523
- * @return {*|{}}
104524
- */
104525
- get modelTreeViews() {
104526
- return this._modelTreeViews;
104527
- }
104528
-
104529
- /**
104530
- * Sets how the nodes are organized within this tree view.
104531
- *
104532
- * Accepted values are:
104533
- *
104534
- * * "containment" - organizes the nodes to indicate the containment hierarchy of the IFC objects.
104535
- * * "types" - groups the nodes within their IFC types.
104536
- * * "storeys" - groups the nodes within ````IfcBuildingStoreys```` and sub-groups them by their IFC types.
104537
- *
104538
- * <br>
104539
- * This can be updated dynamically.
104540
- *
104541
- * Default value is "containment".
104542
- *
104543
- * @type {String}
104544
- */
104545
- set hierarchy(hierarchy) {
104546
- hierarchy = hierarchy || "containment";
104547
- if (hierarchy !== "containment" && hierarchy !== "storeys" && hierarchy !== "types") {
104548
- this.error("Unsupported value for `hierarchy' - defaulting to 'containment'");
104549
- hierarchy = "containment";
104550
- }
104551
- this._hierarchy = hierarchy;
104552
- for (let modelId in this._modelTreeViews) {
104553
- if (this._modelTreeViews.hasOwnProperty(modelId)) {
104554
- this._modelTreeViews[modelId].setHierarchy(this._hierarchy);
104555
- }
104556
- }
104557
- }
104558
-
104559
- /**
104560
- * Gets how the nodes are organized within this tree view.
104561
- *
104562
- * @type {String}
104563
- */
104564
- get hierarchy() {
104565
- return this._hierarchy;
104566
- }
104567
-
104568
- /**
104569
- * Adds a model to this tree view.
104570
- *
104571
- * The model will be automatically removed when destroyed.
104572
- *
104573
- * To automatically add each model as it's created, instead of manually calling this method each time,
104574
- * provide a ````autoAddModels: true```` to the TreeViewPlugin constructor.
104575
- *
104576
- * @param {String} modelId ID of a model {@link Entity} in {@link Scene#models}.
104577
- * @param {Object} [options] Options for model in the tree view.
104578
- * @param {String} [options.rootName] Optional display name for the root node. Ordinary, for "containment"
104579
- * and "storeys" hierarchy types, the tree would derive the root node name from the model's "IfcProject" element
104580
- * name. This option allows to override that name when it is not suitable as a display name.
104581
- * @returns {ModelTreeView} ModelTreeView for the newly-added model. If this method succeeded in adding the model,
104582
- * then {@link ModelTreeView#valid} will equal ````true````. Otherwise, that property will be ````false````
104583
- * and {@link ModelTreeView#errors} will contain error messages.
104584
- */
104585
- addModel(modelId, options = {}) {
104586
- if (!this._containerElement) {
104587
- return;
104588
- }
104589
- const model = this.viewer.scene.models[modelId];
104590
- if (!model) {
104591
- throw "Model not found: " + modelId;
104592
- }
104593
- const metaModel = this.viewer.metaScene.metaModels[modelId];
104594
- if (!metaModel) {
104595
- this.error("MetaModel not found: " + modelId);
104596
- return;
104597
- }
104598
- if (this._modelTreeViews[modelId]) {
104599
- this.warn("Model already added: " + modelId);
104600
- return;
104601
- }
104602
- const modelTreeView = new ModelTreeView(this.viewer, this, model, metaModel, {
104603
- containerElement: this._containerElement,
104604
- autoExpandDepth: this._autoExpandDepth,
104605
- hierarchy: this._hierarchy,
104606
- sortNodes: this._sortNodes,
104607
- pruneEmptyNodes: this._pruneEmptyNodes,
104608
- rootName: options.rootName
104609
- });
104610
- this._modelTreeViews[modelId] = modelTreeView;
104611
- model.on("destroyed", () => {
104612
- this.removeModel(model.id);
104613
- });
104614
- return modelTreeView;
104615
- }
104616
-
104617
- /**
104618
- * Removes a model from this tree view.
104619
- *
104620
- * Does nothing if model not currently in tree view.
104621
- *
104622
- * @param {String} modelId ID of a model {@link Entity} in {@link Scene#models}.
104623
- */
104624
- removeModel(modelId) {
104625
- if (!this._containerElement) {
104626
- return;
104627
- }
104628
- const modelTreeView = this._modelTreeViews[modelId];
104629
- if (!modelTreeView) {
104630
- return;
104631
- }
104632
- modelTreeView.destroy();
104633
- delete this._modelTreeViews[modelId];
104634
- }
104635
-
104636
- /**
104637
- * Collapses all trees within this tree view.
104638
- */
104639
- collapse() {
104640
- for (let modelId in this._modelTreeViews) {
104641
- if (this._modelTreeViews.hasOwnProperty(modelId)) {
104642
- const modelTreeView = this._modelTreeViews[modelId];
104643
- modelTreeView.collapse();
104644
- }
104645
- }
104646
- }
104647
-
104648
- /**
104649
- * Highlights the tree view node that represents the given object {@link Entity}.
104650
- *
104651
- * This causes the tree view to collapse, then expand to reveal the node, then highlight the node.
104652
- *
104653
- * If a node is previously highlighted, de-highlights that node and collapses the tree first.
104654
- *
104655
- * Note that if the TreeViewPlugin was configured with ````pruneEmptyNodes: true```` (default configuration), then the
104656
- * node won't exist in the tree if it has no Entitys in the {@link Scene}. in that case, nothing will happen.
104657
- *
104658
- * Within the DOM, the node is represented by an ````<li>```` element. This method will add a ````.highlighted-node```` class to
104659
- * the element to make it appear highlighted, removing that class when de-highlighting it again. See the CSS rules
104660
- * in the TreeViewPlugin examples for an example of that class.
104661
- *
104662
- * @param {String} objectId ID of the {@link Entity}.
104663
- */
104664
- showNode(objectId) {
104665
- this.unShowNode();
104666
- const metaObject = this.viewer.metaScene.metaObjects[objectId];
104667
- if (!metaObject) {
104668
- this.error("MetaObject not found: " + objectId);
104669
- return;
104670
- }
104671
- const metaModel = metaObject.metaModel;
104672
- const modelId = metaModel.id;
104673
- const modelTreeView = this._modelTreeViews[modelId];
104674
- if (!modelTreeView) {
104675
- this.error("Object not in this TreeView: " + objectId);
104676
- return;
104677
- }
104678
- modelTreeView.showNode(objectId);
104679
- }
104680
-
104681
- /**
104682
- * De-highlights the node previously shown with {@link TreeViewPlugin#showNode}.
104683
- *
104684
- * Does nothing if no node is currently shown.
104685
- *
104686
- * If the node is currently scrolled into view, keeps the node in view.
104687
- */
104688
- unShowNode() {
104689
- for (let modelId in this._modelTreeViews) {
104690
- if (this._modelTreeViews.hasOwnProperty(modelId)) {
104691
- const modelTreeView = this._modelTreeViews[modelId];
104692
- modelTreeView.unShowNode();
104693
- }
104694
- }
104695
- }
104696
-
104697
- /**
104698
- * Expands the tree to the given depth.
104699
- *
104700
- * Collapses the tree first.
104701
- *
104702
- * @param {Number} depth Depth to expand to.
104703
- */
104704
- expandToDepth(depth) {
104705
- for (let modelId in this._modelTreeViews) {
104706
- if (this._modelTreeViews.hasOwnProperty(modelId)) {
104707
- const modelTreeView = this._modelTreeViews[modelId];
104708
- modelTreeView.collapse();
104709
- modelTreeView.expandToDepth(depth);
104710
- }
104711
- }
104712
- }
104713
-
104714
- /**
104715
- * Iterates over a subtree of the tree view's {@link TreeViewNode}s, calling the given callback for each
104716
- * node in depth-first pre-order.
104717
- *
104718
- * @param {TreeViewNode} node Root of the subtree.
104719
- * @param {Function} callback Callback called at each {@link TreeViewNode}, with the TreeViewNode given as the argument.
104720
- */
104721
- withNodeTree(node, callback) {
104722
- callback(node);
104723
- const children = node.children;
104724
- if (!children) {
104725
- return;
104726
- }
104727
- for (let i = 0, len = children.length; i < len; i++) {
104728
- this.withNodeTree(children[i], callback);
104729
- }
104730
- }
104731
-
104732
- /**
104733
- * Destroys this TreeViewPlugin.
104734
- */
104735
- destroy() {
104736
- if (!this._containerElement) {
104737
- return;
104738
- }
104739
- for (let modelId in this._modelTreeViews) {
104740
- if (this._modelTreeViews.hasOwnProperty(modelId)) {
104741
- this._modelTreeViews[modelId].destroy();
104742
- }
104743
- }
104744
- this._modelTreeViews = {};
104745
- super.destroy();
104589
+ switchElement.removeEventListener('click', this._switchCollapseHandler);
104590
+ switchElement.addEventListener('click', this._switchExpandHandler);
104746
104591
  }
104747
104592
  }
104748
104593
 
@@ -115864,7 +115709,7 @@ parsers[ParserV10.version] = ParserV10;
115864
115709
  *
115865
115710
  * const textureTranscoder = new KTX2TextureTranscoder({
115866
115711
  * viewer,
115867
- * transcoderPath: "./../dist/basis/" // <------ Path to Basis Universal transcoder
115712
+ * transcoderPath: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/" // <------ Path to Basis Universal transcoder
115868
115713
  * });
115869
115714
  *
115870
115715
  * const xktLoader = new XKTLoaderPlugin(viewer, {
@@ -116445,7 +116290,7 @@ class XKTLoaderPlugin extends Plugin {
116445
116290
  }
116446
116291
 
116447
116292
  options.excludeUnclassifiedObjects = (params.excludeUnclassifiedObjects !== undefined) ? (!!params.excludeUnclassifiedObjects) : this._excludeUnclassifiedObjects;
116448
- options.globalizeObjectIds = (params.globalizeObjectIds !== undefined) ? (!!params.globalizeObjectIds) : this._globalizeObjectIds;
116293
+ options.globalizeObjectIds = (params.globalizeObjectIds !== undefined && params.globalizeObjectIds !== null) ? (!!params.globalizeObjectIds) : this._globalizeObjectIds;
116449
116294
 
116450
116295
  if (params.metaModelSrc || params.metaModelData) {
116451
116296
 
@@ -116454,7 +116299,7 @@ class XKTLoaderPlugin extends Plugin {
116454
116299
  const metaModel = this.viewer.metaScene.createMetaModel(modelId, metaModelData, {
116455
116300
  includeTypes: includeTypes,
116456
116301
  excludeTypes: excludeTypes,
116457
- globalizeObjectIds: this.globalizeObjectIds
116302
+ globalizeObjectIds: options.globalizeObjectIds
116458
116303
  });
116459
116304
 
116460
116305
  if (!metaModel) {