bruce-cesium 6.0.4 → 6.0.6

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.
@@ -72,5 +72,5 @@ __exportStar(require("./widgets/controls-view-bar/widget-control-view-bar-search
72
72
  __exportStar(require("./widgets/widget-left-panel"), exports);
73
73
  __exportStar(require("./widgets/tabs-left-panel/widget-left-panel-tab"), exports);
74
74
  __exportStar(require("./widgets/tabs-left-panel/widget-left-panel-tab-bookmarks"), exports);
75
- exports.VERSION = "6.0.4";
75
+ exports.VERSION = "6.0.6";
76
76
  //# sourceMappingURL=bruce-cesium.js.map
@@ -104,6 +104,17 @@ var TilesetCadRenderManager;
104
104
  this.historicPossesInitialLoaded = false;
105
105
  this.historicAnimation = null;
106
106
  this.historicPossesLoadingProm = null;
107
+ // Queue of loaded in features that we haven't processed yet.
108
+ this.featureQueue = [];
109
+ // State is true for 'add' or 'false' for 'remove'.
110
+ this.featureQueueStates = new Map();
111
+ this.featureQueueInterval = null;
112
+ // Cache of feature properties we wanted to find vs what we found.
113
+ // Eg: "entityId" -> "entityId" | "bruceId" | "id" | null.
114
+ // Eg: "geomId" -> null | "geomId";
115
+ // Saves having to do a case-insensitive lookup every time.
116
+ this.featurePropCache = new Map();
117
+ this.featurePropsChecked = 0;
107
118
  const { viewer, register: visualsManager, getters, item, modelSpace } = params;
108
119
  this.viewer = viewer;
109
120
  this.getters = getters;
@@ -138,7 +149,7 @@ var TilesetCadRenderManager;
138
149
  // Single shared getter to avoid multiple assemblies fighting to render.
139
150
  // This lets one complete, then the next start.
140
151
  await shared_getters_1.SharedGetters.Queue.Run("Rendering assembly.", async () => {
141
- var _a, _b, _c;
152
+ var _a, _b;
142
153
  if (this.disposed) {
143
154
  return;
144
155
  }
@@ -196,30 +207,39 @@ var TilesetCadRenderManager;
196
207
  viaCdn: false
197
208
  });
198
209
  }
199
- // ModelTree is needed to map incoming graphics to Entities.
200
- // So we load it first, then start rendering.
201
- try {
202
- // In >v1 the model-tree sits in its own file.
203
- if (((_a = tileset.settings) === null || _a === void 0 ? void 0 : _a["tilesetVersion"]) > 1) {
204
- const modelTreeUrl = bruce_models_1.Tileset.GetFileUrl({
205
- api: api,
206
- file: "model_tree.json",
207
- tilesetId: tileset.id,
208
- cacheToken: tileset.generateVersion,
209
- viaCdn: Boolean(this.item.cdnEnabled == null ? true : this.item.cdnEnabled)
210
- });
211
- this.modelTree = await api.get(modelTreeUrl);
212
- this.modelTreeUpdate.Trigger(this.modelTree);
213
- }
214
- // In v1 the model-tree is inside the tileset.json.
215
- else {
216
- const tilesetJson = await api.get(tileset.loadUrl);
217
- this.modelTree = ((_b = tilesetJson === null || tilesetJson === void 0 ? void 0 : tilesetJson.extensions) === null || _b === void 0 ? void 0 : _b.modelTree) || null;
210
+ // TODO: we shouldn't just assume we'll need it, huge assemblies are 100+ MB.
211
+ // TODO: this is a problem as we need to know what nodes are collapsed...
212
+ // In >v1 the model-tree sits in its own file.
213
+ if (((_a = tileset.settings) === null || _a === void 0 ? void 0 : _a["tilesetVersion"]) > 1) {
214
+ const modelTreeUrl = bruce_models_1.Tileset.GetFileUrl({
215
+ api: api,
216
+ file: "model_tree.json",
217
+ tilesetId: tileset.id,
218
+ cacheToken: tileset.generateVersion,
219
+ viaCdn: Boolean(this.item.cdnEnabled == null ? true : this.item.cdnEnabled)
220
+ });
221
+ api.get(modelTreeUrl).then((blob) => {
222
+ if (this.disposed) {
223
+ return;
224
+ }
225
+ this.modelTree = blob || null;
218
226
  this.modelTreeUpdate.Trigger(this.modelTree);
219
- }
227
+ }).catch((err) => {
228
+ this.modelTreeError = err.message || "Unknown error";
229
+ });
220
230
  }
221
- catch (e) {
222
- console.error(e);
231
+ // In v1 the model-tree is inside the tileset.json.
232
+ else {
233
+ api.get(tileset.loadUrl).then((blob) => {
234
+ var _a;
235
+ if (this.disposed) {
236
+ return;
237
+ }
238
+ this.modelTree = ((_a = blob === null || blob === void 0 ? void 0 : blob.extensions) === null || _a === void 0 ? void 0 : _a.modelTree) || null;
239
+ this.modelTreeUpdate.Trigger(this.modelTree);
240
+ }).catch((err) => {
241
+ this.modelTreeError = err.message || "Unknown error";
242
+ });
223
243
  }
224
244
  if (this.disposed) {
225
245
  return;
@@ -228,7 +248,7 @@ var TilesetCadRenderManager;
228
248
  const rootId = settings.rootEntityId;
229
249
  this.rootId = rootId;
230
250
  // If historic, we'll see if the root Entity has a historic position to use.
231
- if (((_c = this.item.BruceEntity) === null || _c === void 0 ? void 0 : _c.historic) && rootId) {
251
+ if (((_b = this.item.BruceEntity) === null || _b === void 0 ? void 0 : _b.historic) && rootId) {
232
252
  try {
233
253
  const { entity: root } = await bruce_models_1.Entity.Get({
234
254
  entityId: rootId,
@@ -316,7 +336,7 @@ var TilesetCadRenderManager;
316
336
  });
317
337
  cTileset.tileLoad.addEventListener((tile) => {
318
338
  try {
319
- this.mapCTile(tile, true);
339
+ this.queueTile(tile, true);
320
340
  }
321
341
  catch (e) {
322
342
  console.error(e);
@@ -324,7 +344,7 @@ var TilesetCadRenderManager;
324
344
  });
325
345
  cTileset.tileUnload.addEventListener((tile) => {
326
346
  try {
327
- this.mapCTile(tile, false);
347
+ this.queueTile(tile, false);
328
348
  }
329
349
  catch (e) {
330
350
  console.error(e);
@@ -347,34 +367,133 @@ var TilesetCadRenderManager;
347
367
  * @param load indicates if we are loading or unloading the tile.
348
368
  * @returns
349
369
  */
350
- mapCTile(tile, load) {
370
+ queueTile(tile, load) {
351
371
  const content = tile === null || tile === void 0 ? void 0 : tile.content;
352
372
  if (!content) {
353
373
  return;
354
374
  }
355
- const regosToQueue = new Map();
356
375
  for (let i = 0; i < content.featuresLength; i++) {
357
376
  const feature = content.getFeature(i);
358
- let rego = this.mapTilesetFeature(feature, load);
359
- if (!(rego === null || rego === void 0 ? void 0 : rego.entityId)) {
377
+ if (!this.featureQueue.includes(feature)) {
378
+ this.featureQueue.push(feature);
379
+ }
380
+ this.featureQueueStates.set(feature, load);
381
+ }
382
+ this.pingFeatureQueue();
383
+ }
384
+ /**
385
+ * Pings the feature queue to process any queued features.
386
+ * Does nothing if we're already processing.
387
+ * @returns
388
+ */
389
+ pingFeatureQueue() {
390
+ if (!this.featureQueue.length || this.featureQueueInterval || this.disposed) {
391
+ return;
392
+ }
393
+ this.featureQueueInterval = setInterval(() => {
394
+ if (this.disposed) {
395
+ clearInterval(this.featureQueueInterval);
396
+ this.featureQueueInterval = null;
397
+ return;
398
+ }
399
+ this.processFeatureQueueBatch();
400
+ if (!this.featureQueue) {
401
+ clearInterval(this.featureQueueInterval);
402
+ this.featureQueueInterval = null;
403
+ }
404
+ }, 10);
405
+ }
406
+ /**
407
+ * Process a batch of features from the feature queue.
408
+ */
409
+ processFeatureQueueBatch() {
410
+ if (!this.featureQueue.length) {
411
+ return;
412
+ }
413
+ // If features require geomId (don't have bruceId props) then the model tree must be loaded.
414
+ // If it's not loaded, we simply return and keep waiting.
415
+ if (!this.modelTree) {
416
+ /*
417
+ if (!this.featureQueue[0].getProperty("bruceId")) {
418
+ return;
419
+ }
420
+ */
421
+ // TODO: For now always requiring it when it's available.
422
+ // This is because we need to think of a strategy or collapsed items first.
423
+ if (!this.modelTreeError) {
424
+ return;
425
+ }
426
+ }
427
+ const BATCH_SIZE = 5000;
428
+ const batch = this.featureQueue.splice(0, BATCH_SIZE);
429
+ const regosToStyle = new Map();
430
+ const featuresToRemove = [];
431
+ for (const feature of batch) {
432
+ const load = this.featureQueueStates.get(feature);
433
+ this.featureQueueStates.delete(feature);
434
+ if (!load) {
435
+ featuresToRemove.push(feature);
436
+ continue;
437
+ }
438
+ const result = this.mapTilesetFeature(feature);
439
+ if (result === null) {
360
440
  // Override the default hide state set by the style.
361
441
  feature.show = true;
362
442
  continue;
363
443
  }
364
- regosToQueue.set(rego.entityId, rego);
444
+ else if (result) {
445
+ regosToStyle.set(result.entityId, result);
446
+ }
365
447
  }
366
- if (this.styler && regosToQueue.size) {
367
- this.styler.QueueEntities(Array.from(regosToQueue.values()));
448
+ if (featuresToRemove.length) {
449
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
450
+ visuals: featuresToRemove,
451
+ requestRender: false,
452
+ menuItemId: this.item.id,
453
+ doRemove: false
454
+ });
455
+ for (const entityId of removedEntityIds) {
456
+ delete this.loadedCesiumEntities[entityId];
457
+ }
458
+ }
459
+ if (this.styler && regosToStyle.size) {
460
+ this.styler.QueueEntities(Array.from(regosToStyle.values()));
461
+ }
462
+ }
463
+ evaluateFeatureProps(feature) {
464
+ var _a;
465
+ // If we've checked 10 features then we should have a good handle on what props are available.
466
+ if (this.featurePropsChecked >= 10) {
467
+ return;
468
+ }
469
+ this.featurePropsChecked++;
470
+ const featureAny = feature;
471
+ const props = featureAny.getPropertyNames ? featureAny.getPropertyNames() : (_a = featureAny.getPropertyIds) === null || _a === void 0 ? void 0 : _a.call(featureAny);
472
+ if (!props) {
473
+ return;
474
+ }
475
+ for (const prop of props) {
476
+ if (!prop) {
477
+ continue;
478
+ }
479
+ const lowered = prop.toLowerCase();
480
+ if (lowered === "bruceid" || lowered === "id" || lowered === "entityid") {
481
+ this.featurePropCache.set("entityId", prop);
482
+ }
483
+ else if (lowered === "geomid") {
484
+ this.featurePropCache.set("geomId", prop);
485
+ }
486
+ else if (lowered === "brucepath") {
487
+ this.featurePropCache.set("brucePath", prop);
488
+ }
368
489
  }
369
- this.viewer.scene.requestRender();
370
490
  }
371
491
  /**
372
492
  * @param feature
373
- * @param add indicates if we are adding or removing the feature.
374
493
  * @returns
375
494
  */
376
- mapTilesetFeature(feature, add) {
377
- var _a, _b, _c, _d, _e, _f;
495
+ mapTilesetFeature(feature) {
496
+ var _a, _b, _c, _d, _e;
378
497
  const accountId = (_b = ((_a = this.item.tileset) === null || _a === void 0 ? void 0 : _a.ClientAccountID)) !== null && _b !== void 0 ? _b : this.getters.GetAccountId();
379
498
  const canEdit = accountId === this.getters.GetAccountId() && !((_c = this.item.BruceEntity) === null || _c === void 0 ? void 0 : _c.historic);
380
499
  let rego = {
@@ -390,17 +509,15 @@ var TilesetCadRenderManager;
390
509
  tilesetType: bruce_models_1.Tileset.EType.Cad,
391
510
  rootId: this.rootId
392
511
  };
393
- const featureAny = feature;
394
- const propertyNames = featureAny.getPropertyNames ? featureAny.getPropertyNames() : (_e = featureAny.getPropertyIds) === null || _e === void 0 ? void 0 : _e.call(featureAny);
395
- if (!propertyNames) {
396
- return null;
397
- }
398
- const setByGeomId = () => {
399
- const geomIdProp = propertyNames.find(x => String(x).toLowerCase() == "geomid");
512
+ this.evaluateFeatureProps(feature);
513
+ // Becoming legacy.
514
+ // Need to handle collapsed items somehow in newer path first.
515
+ if (!rego.entityId) {
516
+ const geomIdProp = this.featurePropCache.get("geomId");
400
517
  if (geomIdProp) {
401
518
  const geomId = +feature.getProperty(geomIdProp);
402
519
  if (geomId != null && (!!geomId || geomId == 0)) {
403
- const meta = this.getMetaByGeomId(geomId, feature.tileset);
520
+ const meta = this.getMetaByGeomId(geomId);
404
521
  if (meta) {
405
522
  rego.entityId = meta.entityId;
406
523
  rego.entityTypeId = meta.typeId;
@@ -409,15 +526,17 @@ var TilesetCadRenderManager;
409
526
  }
410
527
  }
411
528
  }
412
- };
413
- const setByEntityId = () => {
414
- const bruceIdProp = propertyNames.find(x => (String(x).toLowerCase() == "bruceid"));
415
- if (bruceIdProp) {
416
- rego.entityId = feature.getProperty(bruceIdProp);
529
+ }
530
+ // Latest path.
531
+ if (!rego.entityId) {
532
+ const entityIdProp = this.featurePropCache.get("entityId");
533
+ if (entityIdProp) {
534
+ rego.entityId = feature.getProperty(entityIdProp);
417
535
  }
418
- };
419
- const setByPath = () => {
420
- const brucePathProp = propertyNames.find(x => String(x).toLowerCase() == "brucepath");
536
+ }
537
+ // Super legacy.
538
+ if (!rego.entityId) {
539
+ const brucePathProp = this.featurePropCache.get("brucePath");
421
540
  if (brucePathProp) {
422
541
  const pathStr = feature.getProperty(brucePathProp);
423
542
  if (pathStr) {
@@ -427,20 +546,13 @@ var TilesetCadRenderManager;
427
546
  rego.entityId = path[path.length - 1];
428
547
  }
429
548
  }
430
- };
431
- setByGeomId();
432
- if (!rego.entityId) {
433
- setByEntityId();
434
- }
435
- if (!rego.entityId) {
436
- setByPath();
437
549
  }
438
550
  if (!rego.entityId) {
439
551
  return null;
440
552
  }
441
553
  // Optional menu item restriction.
442
554
  // Allows only showing certain entities from a tileset.
443
- const onlyIds = (_f = this.item.BruceEntity) === null || _f === void 0 ? void 0 : _f.EntityIds;
555
+ const onlyIds = (_e = this.item.BruceEntity) === null || _e === void 0 ? void 0 : _e.EntityIds;
444
556
  if ((onlyIds === null || onlyIds === void 0 ? void 0 : onlyIds.length) && !onlyIds.includes(rego.entityId)) {
445
557
  if (this.item.BruceEntity.Ghosts) {
446
558
  feature.color = Cesium.Color.WHITE.clone().withAlpha(0.5);
@@ -448,76 +560,62 @@ var TilesetCadRenderManager;
448
560
  else {
449
561
  feature.show = false;
450
562
  }
451
- return null;
563
+ return false;
452
564
  }
453
- if (add) {
454
- // Get the initial hide/show state.
455
- const state = this.visualsManager.GetState({
456
- entityId: rego.entityId,
457
- menuItemId: this.item.id,
458
- });
459
- if (this.somethingIsolated == null ||
460
- // 5s cache.
461
- (this.somethingIsolatedDateTime && this.somethingIsolatedDateTime.getTime() + 5000 < new Date().getTime())) {
462
- this.somethingIsolated = this.visualsManager.GetIsIsolatedAny();
463
- this.somethingIsolatedDateTime = new Date();
565
+ // Get the initial hide/show state.
566
+ const state = this.visualsManager.GetState({
567
+ entityId: rego.entityId,
568
+ menuItemId: this.item.id,
569
+ });
570
+ if (this.somethingIsolated == null ||
571
+ // 5s cache.
572
+ (this.somethingIsolatedDateTime && this.somethingIsolatedDateTime.getTime() + 5000 < new Date().getTime())) {
573
+ this.somethingIsolated = this.visualsManager.GetIsIsolatedAny();
574
+ this.somethingIsolatedDateTime = new Date();
575
+ }
576
+ // Override the default hide state set by the style.
577
+ let hide = false;
578
+ if (state) {
579
+ if (state.hidden) {
580
+ hide = true;
464
581
  }
465
- // Override the default hide state set by the style.
466
- let hide = false;
467
- if (state) {
468
- if (state.hidden) {
469
- hide = true;
470
- }
471
- else if (state.opacity === 0) {
472
- hide = true;
473
- }
474
- else if (!state.isolated && this.somethingIsolated) {
475
- hide = true;
476
- }
582
+ else if (state.opacity === 0) {
583
+ hide = true;
477
584
  }
478
- feature.show = !hide;
479
- // Already exists, so we add this graphic as a sibling.
480
- if (this.loadedCesiumEntities[rego.entityId]) {
481
- rego = this.loadedCesiumEntities[rego.entityId];
482
- if (!rego.visual) {
483
- // No parent graphic.
484
- rego.visual = feature;
485
- }
486
- else if (rego.visual == feature) {
487
- // Same graphic. Nothing to do.
585
+ else if (!state.isolated && this.somethingIsolated) {
586
+ hide = true;
587
+ }
588
+ }
589
+ feature.show = !hide;
590
+ // Already exists, so we add this graphic as a sibling.
591
+ if (this.loadedCesiumEntities[rego.entityId]) {
592
+ rego = this.loadedCesiumEntities[rego.entityId];
593
+ if (!rego.visual) {
594
+ // No parent graphic.
595
+ rego.visual = feature;
596
+ }
597
+ else if (rego.visual == feature) {
598
+ // Same graphic. Nothing to do.
599
+ }
600
+ else {
601
+ const visual = rego.visual;
602
+ // Sibling graphic.
603
+ if (!visual._siblingGraphics) {
604
+ visual._siblingGraphics = [];
488
605
  }
489
- else {
490
- const visual = rego.visual;
491
- // Sibling graphic.
492
- if (!visual._siblingGraphics) {
493
- visual._siblingGraphics = [];
494
- }
495
- if (visual._siblingGraphics.indexOf(feature) < 0) {
496
- visual._siblingGraphics.push(feature);
497
- }
606
+ if (visual._siblingGraphics.indexOf(feature) < 0) {
607
+ visual._siblingGraphics.push(feature);
498
608
  }
499
609
  }
500
- this.loadedCesiumEntities[rego.entityId] = rego;
501
- this.visualsManager.AddRego({
502
- rego,
503
- requestRender: false
504
- });
505
- return rego;
506
- }
507
- else {
508
- this.visualsManager.RemoveRegos({
509
- entityId: rego.entityId,
510
- requestRender: false,
511
- menuItemId: this.item.id,
512
- doRemove: false,
513
- source: visuals_register_1.VisualsRegister.EUpdateSource.TILESET_DISPOSE
514
- });
515
- // Might have to do something smarter since siblings could still be OK.
516
- this.loadedCesiumEntities[rego.entityId] = null;
517
- delete this.loadedCesiumEntities[rego.entityId];
518
610
  }
611
+ this.loadedCesiumEntities[rego.entityId] = rego;
612
+ this.visualsManager.AddRego({
613
+ rego,
614
+ requestRender: false
615
+ });
616
+ return rego;
519
617
  }
520
- getMetaByGeomId(geomId, tileset) {
618
+ getMetaByGeomId(geomId) {
521
619
  if (this.treeNodeByGeomId == null) {
522
620
  if (!this.modelTree) {
523
621
  return null;
@@ -557,8 +655,8 @@ var TilesetCadRenderManager;
557
655
  newFirstFoundCollapsedBranch = cache;
558
656
  }
559
657
  }
560
- // Always process children regardless of collapse state
561
- // This ensures all nodes are mapped, even those under collapsed branches
658
+ // Always process children regardless of collapse state.
659
+ // This ensures all nodes are mapped, even those under collapsed branches.
562
660
  if (node.children) {
563
661
  for (let i = node.children.length - 1; i >= 0; i--) {
564
662
  stack.push({
@@ -583,8 +681,8 @@ var TilesetCadRenderManager;
583
681
  }
584
682
  /**
585
683
  * Gets the parent node of a given entity.
586
- * @param entityId The entity ID to find the parent for
587
- * @returns The parent node or null if no parent exists
684
+ * @param entityId The entity ID to find the parent for.
685
+ * @returns The parent node or null if no parent exists.
588
686
  */
589
687
  getParentNode(entityId) {
590
688
  if (this.treeNodeByEntityId == null) {
@@ -601,8 +699,8 @@ var TilesetCadRenderManager;
601
699
  }
602
700
  /**
603
701
  * Gets all child nodes of a given entity.
604
- * @param entityId The entity ID to find children for
605
- * @returns Array of child nodes
702
+ * @param entityId The entity ID to find children for.
703
+ * @returns Array of child nodes.
606
704
  */
607
705
  getChildNodes(entityId) {
608
706
  if (this.treeNodeByEntityId == null) {
@@ -651,7 +749,7 @@ var TilesetCadRenderManager;
651
749
  }
652
750
  /**
653
751
  * Gets the root node of the model tree.
654
- * @returns The root node or null if no tree exists
752
+ * @returns The root node or null if no tree exists.
655
753
  */
656
754
  getRootNode() {
657
755
  if (this.treeNodeByEntityId == null) {
@@ -660,7 +758,7 @@ var TilesetCadRenderManager;
660
758
  }
661
759
  this.buildModelTreeNodes(this.modelTree);
662
760
  }
663
- // Find the node with no parent (root node)
761
+ // Find the node with no parent (root node).
664
762
  for (const nodeId in this.treeNodeByEntityId) {
665
763
  const node = this.treeNodeByEntityId[nodeId];
666
764
  if (!node.parentId) {
@@ -671,8 +769,8 @@ var TilesetCadRenderManager;
671
769
  }
672
770
  /**
673
771
  * Gets the path from root to a given entity.
674
- * @param entityId The entity ID to find the path for
675
- * @returns Array of node IDs representing the path from root to the entity
772
+ * @param entityId The entity ID to find the path for.
773
+ * @returns Array of node IDs representing the path from root to the entity.
676
774
  */
677
775
  getNodePath(entityId) {
678
776
  if (this.treeNodeByEntityId == null) {
@@ -695,8 +793,8 @@ var TilesetCadRenderManager;
695
793
  }
696
794
  /**
697
795
  * Batch operation to get multiple node paths efficiently.
698
- * @param entityIds Array of entity IDs to get paths for
699
- * @returns Map of entityId to path array
796
+ * @param entityIds Array of entity IDs to get paths for.
797
+ * @returns Map of entityId to path array.
700
798
  */
701
799
  getNodePathsBatch(entityIds) {
702
800
  if (this.treeNodeByEntityId == null) {
@@ -723,8 +821,8 @@ var TilesetCadRenderManager;
723
821
  }
724
822
  /**
725
823
  * Batch operation to get multiple parent nodes efficiently.
726
- * @param entityIds Array of entity IDs to get parents for
727
- * @returns Map of entityId to parent node
824
+ * @param entityIds Array of entity IDs to get parents for.
825
+ * @returns Map of entityId to parent node.
728
826
  */
729
827
  getParentNodesBatch(entityIds) {
730
828
  if (this.treeNodeByEntityId == null) {
@@ -747,8 +845,8 @@ var TilesetCadRenderManager;
747
845
  }
748
846
  /**
749
847
  * Batch operation to get multiple child nodes efficiently.
750
- * @param entityIds Array of entity IDs to get children for
751
- * @returns Map of entityId to array of child nodes
848
+ * @param entityIds Array of entity IDs to get children for.
849
+ * @returns Map of entityId to array of child nodes.
752
850
  */
753
851
  getChildNodesBatch(entityIds) {
754
852
  if (this.treeNodeByEntityId == null) {
@@ -758,11 +856,11 @@ var TilesetCadRenderManager;
758
856
  this.buildModelTreeNodes(this.modelTree);
759
857
  }
760
858
  const children = new Map();
761
- // Initialize all entityIds with empty arrays
859
+ // Initialize all entityIds with empty arrays.
762
860
  for (const entityId of entityIds) {
763
861
  children.set(entityId, []);
764
862
  }
765
- // Build a reverse lookup for efficiency
863
+ // Build a reverse lookup for efficiency.
766
864
  for (const nodeId in this.treeNodeByEntityId) {
767
865
  const node = this.treeNodeByEntityId[nodeId];
768
866
  if (node.parentId && children.has(node.parentId)) {
@@ -780,6 +878,11 @@ var TilesetCadRenderManager;
780
878
  }
781
879
  doDispose() {
782
880
  var _a, _b;
881
+ if (this.featureQueueInterval) {
882
+ clearInterval(this.featureQueueInterval);
883
+ this.featureQueueInterval = null;
884
+ this.featureQueue = [];
885
+ }
783
886
  if (this.cTileset) {
784
887
  const viewer = this.viewer;
785
888
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.isDestroyed()) && this.viewer.scene.primitives.contains(this.cTileset)) {