UNPKG

@itwin/core-frontend

Version:
213 lines • 9 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module IModelConnection */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Tiles = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const IModelApp_1 = require("./IModelApp"); const internal_1 = require("./tile/internal"); class TreeOwner { _tileTree; _loadStatus = internal_1.TileTreeLoadStatus.NotLoaded; _supplier; _iModel; id; get tileTree() { return this._tileTree; } get loadStatus() { return this._loadStatus; } get iModel() { return this._iModel; } constructor(id, supplier, iModel) { this.id = id; this._supplier = supplier; this._iModel = iModel; } load() { this._load(); // eslint-disable-line @typescript-eslint/no-floating-promises return this.tileTree; } async loadTree() { await this._load(); return this.tileTree; } [Symbol.dispose]() { this._tileTree = (0, core_bentley_1.dispose)(this._tileTree); this._loadStatus = internal_1.TileTreeLoadStatus.NotLoaded; } async _load() { if (internal_1.TileTreeLoadStatus.NotLoaded !== this.loadStatus) return; this._loadStatus = internal_1.TileTreeLoadStatus.Loading; let tree; let newStatus; try { tree = await this._supplier.createTileTree(this.id, this._iModel); newStatus = undefined !== tree && !tree.rootTile.contentRange.isNull ? internal_1.TileTreeLoadStatus.Loaded : internal_1.TileTreeLoadStatus.NotFound; } catch (err) { newStatus = (err.errorNumber && err.errorNumber === core_bentley_1.IModelStatus.ServerTimeout) ? internal_1.TileTreeLoadStatus.NotLoaded : internal_1.TileTreeLoadStatus.NotFound; } if (internal_1.TileTreeLoadStatus.Loading === this._loadStatus) { this._tileTree = tree; this._loadStatus = newStatus; IModelApp_1.IModelApp.tileAdmin.onTileTreeLoad.raiseEvent(this); } } } /** Provides access to [[TileTree]]s associated with an [[IModelConnection]]. * The tile trees are accessed indirectly via their corresponding [[TileTreeOwner]]s. * Loaded tile trees will be discarded after the iModel is closed, after a period of disuse, or when the contents of a [[GeometricModelState]] they represent * change. * @see [[IModelConnection.tiles]]. * @public * @extensions */ class Tiles { _iModel; _treesBySupplier = new Map(); _disposed = false; /** @internal */ get isDisposed() { return this._disposed; } /** @internal */ constructor(iModel) { this._iModel = iModel; iModel.onEcefLocationChanged.addListener(() => { for (const supplier of this._treesBySupplier.keys()) { if (supplier.isEcefDependent) this.dropSupplier(supplier); } }); // When project extents change, purge tile trees for spatial models. iModel.onProjectExtentsChanged.addListener(async () => { if (!iModel.isBriefcaseConnection() || !iModel.editingScope) await this.purgeModelTrees(this.getSpatialModels()); }); } /** @internal */ [Symbol.dispose]() { this.reset(); this._disposed = true; } /** Intended strictly for tests. * @internal */ reset() { for (const supplier of this._treesBySupplier) supplier[1].forEach((_key, value) => value[Symbol.dispose]()); this._treesBySupplier.clear(); } /** @internal */ async purgeTileTrees(modelIds) { return IModelApp_1.IModelApp.tileAdmin.purgeTileTrees(this._iModel, modelIds); } getModelsAnimatedByScheduleScript(scriptSourceElementId) { const modelIds = new Set(); for (const supplier of this._treesBySupplier.keys()) if (supplier.addModelsAnimatedByScript) supplier.addModelsAnimatedByScript(modelIds, scriptSourceElementId, this.getTreeOwnersForSupplier(supplier)); return modelIds; } /** Update the [[Tile]]s for any [[TileTree]]s that use the [RenderSchedule.Script]($common) hosted by the specified * [RenderTimeline]($backend) or [DisplayStyle]($backend) element. This method should be invoked after * the host element is updated in the database with a new script, so that any [[Viewport]]s displaying tiles produced * based on the previous version of the script are updated to use the new version of the script. * @param scriptSourceElementId The Id of the RenderTimeline or DisplayStyle element that hosts the script. * @public */ async updateForScheduleScript(scriptSourceElementId) { return this.purgeModelTrees(this.getModelsAnimatedByScheduleScript(scriptSourceElementId)); } async purgeModelTrees(modelIds) { if (0 === modelIds.size) return; const ids = Array.from(modelIds); await this.purgeTileTrees(ids); IModelApp_1.IModelApp.viewManager.refreshForModifiedModels(ids); } getSpatialModels() { const modelIds = new Set(); for (const supplier of this._treesBySupplier.keys()) if (supplier.addSpatialModels) supplier.addSpatialModels(modelIds, this.getTreeOwnersForSupplier(supplier)); return modelIds; } /** Obtain the owner of a TileTree. * The `id` is unique within all tile trees associated with `supplier`; its specific structure is an implementation detail known only to the supplier. * A [[TileTreeReference]] uses this method to obtain the tile tree to which it refers. */ getTileTreeOwner(id, supplier) { let trees = this._treesBySupplier.get(supplier); if (undefined === trees) { trees = new core_bentley_1.Dictionary((lhs, rhs) => supplier.compareTileTreeIds(lhs, rhs)); this._treesBySupplier.set(supplier, trees); } let tree = trees.get(id); if (undefined === tree) { tree = new TreeOwner(id, supplier, this._iModel); trees.set(id, tree); } return tree; } /** Remove tile tree matching the provided tile tree supplier/id * @internal */ resetTileTreeOwner(id, supplier) { const trees = this._treesBySupplier.get(supplier); const tree = trees?.get(id); if (tree) { tree[Symbol.dispose](); trees?.delete(id); } } /** Disposes of all [[TileTree]]s belonging to `supplier` and removes `supplier` from the set of known tile tree suppliers. */ dropSupplier(supplier) { const trees = this._treesBySupplier.get(supplier); if (undefined === trees) return; trees.forEach((_key, value) => value[Symbol.dispose]()); this._treesBySupplier.delete(supplier); } /** Invokes a function on each extant TileTreeOwner. */ forEachTreeOwner(func) { for (const dict of this._treesBySupplier.values()) dict.forEach((_key, value) => func(value)); } /** Iterate over all of the TileTreeOwners. */ *[Symbol.iterator]() { for (const [supplier, dict] of this._treesBySupplier) { for (const entry of dict) yield { supplier, id: entry.key, owner: entry.value }; } } /** Obtain the TileTreeOwners supplied by the specified supplier. */ getTreeOwnersForSupplier(supplier) { function* iterator(trees) { if (trees) for (const entry of trees) yield { id: entry.key, owner: entry.value }; } return { [Symbol.iterator]: () => iterator(this._treesBySupplier.get(supplier)), }; } /** Unload any tile trees which have not been drawn since at least the specified time, excluding any of the specified TileTrees. * @internal */ purge(olderThan, exclude) { // NB: It would be nice to be able to detect completely useless leftover Owners or Suppliers, but we can't know if any TileTreeReferences exist pointing to a given Owner. for (const entry of this._treesBySupplier) { const dict = entry[1]; dict.forEach((_treeId, owner) => { const tree = owner.tileTree; if (undefined !== tree && tree.lastSelectedTime.milliseconds < olderThan.milliseconds) if (undefined === exclude || !exclude.has(tree)) owner[Symbol.dispose](); }); } } } exports.Tiles = Tiles; //# sourceMappingURL=Tiles.js.map