UNPKG

@itwin/core-frontend

Version:
190 lines 8.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.GraphicalEditingScope = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const BriefcaseTxns_1 = require("./BriefcaseTxns"); const IpcApp_1 = require("./IpcApp"); class ModelChanges extends core_bentley_1.SortedArray { geometryGuid; range; constructor(geometryGuid, range) { super((lhs, rhs) => (0, core_bentley_1.compareStrings)(lhs.id, rhs.id), core_bentley_1.DuplicatePolicy.Replace); this.geometryGuid = geometryGuid; this.range = range; } } /** Represents a period of time within an [interactive editing]($docs/learning/InteractiveEditing.md) session during which the * geometry of elements being displayed in one or more [[Viewport]]s is being modified. Outside of such a scope, whenever the * geometry within a [GeometricModel]($backend) changes new [[Tile]]s must be generated to reflect those changes in a viewport. * Regenerating entire tiles each time individual elements change can be time-consuming, which may introduce an unacceptable delay * between making a modification and seeing its result on the screen. * * Within the context of a graphical editing scope, no new tiles are generated. Instead, the geometry for any deleted or modified elements * is hidden in the tile graphics, and additional temporary graphics are displayed for any newly-inserted or modified elements. Only when the * scope exits are new tiles produced. * * The application decides when to enter and exit a graphical editing scope. A single interactive editing session may involve any number of * editing scopes. Typically, applications will enter a new editing scope (after first exiting a previous scope, if one exists): * - When switching from a non-graphical workflow to one that involves editing geometry; or * - When changing which geometric model is being edited; or * - After performing an operation that creates or modifies a "large" number (perhaps hundreds?) of elements. * * An application should typically exit any graphical editing scope before: * - Pulling changesets; or * - Switching from a graphical editing workflow to some non-graphical workflow. * * Graphical editing scopes are only supported for [[BriefcaseConnection]]s opened in read-write mode that contain version 1.0.11 or newer of the BisCore schema. * @see [[BriefcaseConnection.enterEditingScope]] to create a scope for a briefcase. * @see [[BriefcaseConnection.editingScope]] to obtain a briefcase's current scope. * @see [[exit]] to terminate a scope. * @public */ class GraphicalEditingScope extends BriefcaseTxns_1.BriefcaseNotificationHandler { get briefcaseChannelName() { return core_common_1.ipcAppChannels.editingScope; } /** Maps model Id to accumulated changes to geometric elements within the associated model. */ _geometryChanges = new Map(); _disposed = false; _cleanup; /** The connection to the iModel being edited. */ iModel; /** Event raised when a new scope is created for any [[BriefcaseConnection]]. * @see [[onExiting]] and [[onExited]] for complementary events. */ static onEnter = new core_bentley_1.BeEvent(); /** Event raised when this scope is about to exit. * @see [[onEnter]] for the complementary event. * @see [[onExited]] for an event raised after the scope exits. */ onExiting = new core_bentley_1.BeEvent(); /** Event raised when this scope has exited. * @see [[onEnter]] for the complementary event. * @see [[onExiting]] for an event raised just before the scope is exited. */ onExited = new core_bentley_1.BeEvent(); /** Event raised after geometric changes are written to the iModel. */ onGeometryChanges = new core_bentley_1.BeEvent(); /** Don't call this directly - use BriefcaseConnection.enterEditingScope. * @internal */ static async enter(imodel) { if (imodel.editingScope) throw new Error("Cannot create an editing scope for an iModel that already has one"); // Register the scope synchronously, in case enter() is called again for same iModel while awaiting asynchronous initialization. const scope = new GraphicalEditingScope(imodel); try { const scopeStarted = await IpcApp_1.IpcApp.appFunctionIpc.toggleGraphicalEditingScope(imodel.key, true); (0, core_bentley_1.assert)(scopeStarted); // If it didn't, the backend threw an error. } catch (e) { scope[Symbol.dispose](); throw e; } this.onEnter.raiseEvent(scope); return scope; } /** Exits this editing scope. The associated [[BriefcaseConnection]]'s `editingScope` will be reset to `undefined`. * @throws Error if the scope could not be exited, e.g., if it has already been exited. * @see [[BriefcaseConnection.enterEditingScope]] to enter an editing scope. */ async exit() { if (this._disposed || this.iModel.editingScope !== this) throw new Error("Cannot exit editing scope after it is disconnected from the iModel"); this._disposed = true; try { this.onExiting.raiseEvent(this); } finally { const scopeExited = await IpcApp_1.IpcApp.appFunctionIpc.toggleGraphicalEditingScope(this.iModel.key, false); (0, core_bentley_1.assert)(!scopeExited); try { this.onExited.raiseEvent(this); } finally { this[Symbol.dispose](); } } } /** Obtain all geometric changes to elements within the specified model accumulated within this scope. */ getGeometryChangesForModel(modelId) { return this._geometryChanges.get(modelId); } /** Obtain all geometric changes to models accumulated within this scope. */ getGeometryChanges() { return { [Symbol.iterator]: () => this.geometryChangeIterator() }; } /** @internal */ get isDisposed() { return this._disposed; } *geometryChangeIterator() { for (const [key, value] of this._geometryChanges) { yield { id: key, geometryGuid: value.geometryGuid, range: value.range, elements: value, }; } } constructor(iModel) { super(iModel.key); this.iModel = iModel; this._cleanup = this.registerImpl(); } [Symbol.dispose]() { this._disposed = true; this.onExiting.clear(); this.onGeometryChanges.clear(); this.onExited.clear(); this._geometryChanges.clear(); if (this._cleanup) { this._cleanup(); this._cleanup = undefined; } } /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */ dispose() { this[Symbol.dispose](); } /** @internal */ notifyGeometryChanged(props) { const changes = core_common_1.ModelGeometryChanges.iterable(props); const modelIds = []; let deletedIds; for (const modelChanges of changes) { // ###TODO do we care about the model range? let list = this._geometryChanges.get(modelChanges.id); modelIds.push(modelChanges.id); for (const elementChange of modelChanges.elements) { if (!list) { this._geometryChanges.set(modelChanges.id, list = new ModelChanges(modelChanges.geometryGuid, modelChanges.range)); } else { list.geometryGuid = modelChanges.geometryGuid; modelChanges.range.clone(list.range); } list.insert(elementChange); if (core_bentley_1.DbOpcode.Delete === elementChange.type) { if (undefined === deletedIds) deletedIds = new Set(); deletedIds.add(elementChange.id); } } } if (deletedIds) { this.iModel.selectionSet.remove(deletedIds); this.iModel.hilited.remove({ elements: deletedIds }); } this.onGeometryChanges.raiseEvent(changes, this); } } exports.GraphicalEditingScope = GraphicalEditingScope; //# sourceMappingURL=GraphicalEditingScope.js.map