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