@itwin/frontend-devtools
Version:
Debug menu and supporting UI widgets
196 lines • 9.28 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Tools
*/
import { BentleyError, Id64 } from "@itwin/core-bentley";
import { GeometrySummaryVerbosity, IModelReadRpcInterface } from "@itwin/core-common";
import { CoreTools, EventHandled, IModelApp, LocateFilterStatus, LocateResponse, MessageBoxIconType, MessageBoxType, NotifyMessageDetails, OutputMessagePriority, PrimitiveTool, } from "@itwin/core-frontend";
import { copyStringToClipboard } from "../ClipboardUtilities";
import { parseArgs } from "./parseArgs";
/** Creates a readable text summary of a geometric element or geometry part. The keyin takes the following arguments, all of which are optional:
* - `id=elementId,elementId,elementId` comma-separated list of element Ids where each `elementId` is a hexadecimal element Id such as "0x12cb";
* - `symbology=0|1` where 1 indicates detailed symbology information should be included in the output;
* - `placement=0|1` where 1 indicates detailed geometric element placement should be included; and
* - `verbosity=0|1|2` controlling the verbosity of the output for each geometric primitive in the geometry stream. Higher values = more detailed information. Note verbosity=2 can produce megabytes of data for certain types of geometric primitives like large meshes.
* - `modal=0|1` where 1 indicates the output should appear in a modal dialog.
* - `copy=0|1` where 1 indicates the output should be copied to the clipboard. Defaults to true.
* - `refs=0|1` where 1 indicates that for geometry parts a list of all elements referencing that part should be included in the output. This is extremely computationally expensive.
* If no id is specified, the tool runs in interactive mode: first operating upon the selection set (if any), then allowing the user to select additional elements.
* @beta
*/
export class InspectElementTool extends PrimitiveTool {
static toolId = "InspectElement";
static get minArgs() { return 0; }
static get maxArgs() { return 6; }
_options = {};
_elementIds;
_modal = false;
_useSelection = false;
_doCopy = false;
_explodeParts = false;
constructor(options, elementIds) {
super();
if (undefined !== options)
this._options = { ...options };
this._elementIds = elementIds;
}
setupAndPromptForNextAction() {
this._useSelection = (undefined !== this.targetView && this.targetView.iModel.selectionSet.isActive);
if (!this._useSelection)
IModelApp.accuSnap.enableLocate(true);
this.showPrompt();
}
showPrompt() {
CoreTools.outputPromptByKey(this._useSelection ? "ElementSet.Prompts.ConfirmSelection" : "ElementSet.Prompts.IdentifyElement");
}
autoLockTarget() { }
requireWriteableTarget() { return false; }
async onUnsuspend() {
this.showPrompt();
}
async onPostInstall() {
await super.onPostInstall();
if (undefined !== this._elementIds)
this.process(this._elementIds).then(async () => {
await this.onReinitialize();
}).catch((err) => {
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, err.toString()));
});
else {
this.setupAndPromptForNextAction();
}
}
async onDataButtonDown(ev) {
if (this._useSelection) {
if (undefined !== ev.viewport) {
const ids = [];
ev.viewport.iModel.selectionSet.elements.forEach((id) => {
if (!Id64.isInvalid(id) && !Id64.isTransient(id))
ids.push(id);
});
if (0 === ids.length)
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, CoreTools.translate("ElementSet.Error.NotSupportedElmType")));
else
await this.process(ids);
await this.onReinitialize();
return EventHandled.Yes;
}
}
const hit = await IModelApp.locateManager.doLocate(new LocateResponse(), true, ev.point, ev.viewport, ev.inputSource);
if (undefined === hit || !hit.isElementHit)
return EventHandled.No;
await this.process([hit.sourceId]);
this.setupAndPromptForNextAction();
return EventHandled.No;
}
async onResetButtonUp(_ev) {
await this.onReinitialize();
return EventHandled.No;
}
async onReinitialize() {
if (this._useSelection || undefined !== this._elementIds) {
await this.exitTool();
}
else {
await this.onRestartTool();
}
}
async onRestartTool() {
const tool = new InspectElementTool();
if (!await tool.run())
return this.exitTool();
}
async filterHit(hit, _out) {
return hit.isElementHit ? LocateFilterStatus.Accept : LocateFilterStatus.Reject;
}
async process(elementIds) {
const request = {
elementIds,
options: this._options,
};
let messageDetails;
try {
let str = await IModelReadRpcInterface.getClientForRouting(this.iModel.routingContext.token).getGeometrySummary(this.iModel.getRpcProps(), request);
if (this._explodeParts) {
const regex = /^part id: (0x[a-f0-9]+)/gm;
const partIds = new Set();
let match;
while (null !== (match = regex.exec(str)))
partIds.add(match[1]);
if (partIds.size > 0) {
request.elementIds = Array.from(partIds);
str += `\npart ids: ${JSON.stringify(request.elementIds)}\n`;
str += await IModelReadRpcInterface.getClientForRouting(this.iModel.routingContext.token).getGeometrySummary(this.iModel.getRpcProps(), request);
}
}
if (this._doCopy)
copyStringToClipboard(str);
const brief = `Summary ${this._doCopy ? "copied to clipboard." : "complete."}`;
messageDetails = new NotifyMessageDetails(OutputMessagePriority.Info, brief, str);
if (this._modal) {
const div = document.createElement("div");
const appendText = (toAppend) => {
const txt = document.createElement("div");
txt.innerText = toAppend;
div.append(txt);
};
const lines = str.split("\n");
const maxLines = 30;
let curLine = 0;
for (const line of lines) {
appendText(line);
if (++curLine > maxLines) {
appendText("...");
break;
}
}
await IModelApp.notifications.openMessageBox(MessageBoxType.Ok, div, MessageBoxIconType.Information);
}
}
catch (err) {
messageDetails = new NotifyMessageDetails(OutputMessagePriority.Error, "Error occurred while generating summary", BentleyError.getErrorMessage(err));
}
IModelApp.notifications.outputMessage(messageDetails);
}
async parseAndRun(...inputArgs) {
const args = parseArgs(inputArgs);
const ids = args.get("i");
if (undefined !== ids)
this._elementIds = ids.split(",");
const verbosity = args.getInteger("v");
if (undefined !== verbosity) {
switch (verbosity) {
case 0:
this._options.geometryVerbosity = GeometrySummaryVerbosity.Basic;
break;
case 1:
this._options.geometryVerbosity = GeometrySummaryVerbosity.Detailed;
break;
case 2:
this._options.geometryVerbosity = GeometrySummaryVerbosity.Full;
break;
}
}
const symbology = args.getBoolean("s");
if (undefined !== symbology)
this._options.verboseSymbology = symbology;
const placement = args.getBoolean("p");
if (undefined !== placement)
this._options.includePlacement = placement;
const parts = args.getBoolean("r");
if (true === parts && undefined !== IModelApp.viewManager.selectedView)
this._options.includePartReferences = IModelApp.viewManager.selectedView.view.is3d() ? "3d" : "2d";
const modal = args.getBoolean("m");
if (undefined !== modal)
this._modal = modal;
const doCopy = args.getBoolean("c");
if (undefined !== doCopy)
this._doCopy = doCopy;
this._explodeParts = true === args.getBoolean("e");
return this.run();
}
}
//# sourceMappingURL=InspectElementTool.js.map