@itwin/editor-frontend 4.0.0-dev.8 → 4.0.0-dev.81

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/lib/cjs/CreateElementTool.d.ts +100 -100
  3. package/lib/cjs/CreateElementTool.js +325 -327
  4. package/lib/cjs/CreateElementTool.js.map +1 -1
  5. package/lib/cjs/DeleteElementsTool.d.ts +13 -13
  6. package/lib/cjs/DeleteElementsTool.js +38 -38
  7. package/lib/cjs/DeleteElementsTool.js.map +1 -1
  8. package/lib/cjs/EditTool.d.ts +38 -38
  9. package/lib/cjs/EditTool.js +80 -81
  10. package/lib/cjs/EditTool.js.map +1 -1
  11. package/lib/cjs/EditToolIpc.d.ts +18 -18
  12. package/lib/cjs/EditToolIpc.js +28 -28
  13. package/lib/cjs/ElementGeometryTool.d.ts +147 -147
  14. package/lib/cjs/ElementGeometryTool.js +704 -709
  15. package/lib/cjs/ElementGeometryTool.js.map +1 -1
  16. package/lib/cjs/ModifyCurveTools.d.ts +139 -139
  17. package/lib/cjs/ModifyCurveTools.js +776 -784
  18. package/lib/cjs/ModifyCurveTools.js.map +1 -1
  19. package/lib/cjs/ModifyElementTool.d.ts +47 -47
  20. package/lib/cjs/ModifyElementTool.js +177 -177
  21. package/lib/cjs/ModifyElementTool.js.map +1 -1
  22. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.d.ts +135 -135
  23. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.js +822 -822
  24. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.js.map +1 -1
  25. package/lib/cjs/ProjectLocation/ProjectGeolocation.d.ts +135 -135
  26. package/lib/cjs/ProjectLocation/ProjectGeolocation.js +532 -532
  27. package/lib/cjs/ProjectLocation/ProjectGeolocation.js.map +1 -1
  28. package/lib/cjs/SketchTools.d.ts +304 -304
  29. package/lib/cjs/SketchTools.js +1704 -1705
  30. package/lib/cjs/SketchTools.js.map +1 -1
  31. package/lib/cjs/SolidModelingTools.d.ts +380 -380
  32. package/lib/cjs/SolidModelingTools.js +1452 -1462
  33. package/lib/cjs/SolidModelingTools.js.map +1 -1
  34. package/lib/cjs/SolidPrimitiveTools.d.ts +318 -318
  35. package/lib/cjs/SolidPrimitiveTools.js +1372 -1378
  36. package/lib/cjs/SolidPrimitiveTools.js.map +1 -1
  37. package/lib/cjs/TransformElementsTool.d.ts +164 -164
  38. package/lib/cjs/TransformElementsTool.js +652 -652
  39. package/lib/cjs/TransformElementsTool.js.map +1 -1
  40. package/lib/cjs/UndoRedoTool.d.ts +16 -16
  41. package/lib/cjs/UndoRedoTool.js +41 -42
  42. package/lib/cjs/UndoRedoTool.js.map +1 -1
  43. package/lib/cjs/editor-frontend.d.ts +17 -17
  44. package/lib/cjs/editor-frontend.js +37 -33
  45. package/lib/cjs/editor-frontend.js.map +1 -1
  46. package/lib/esm/CreateElementTool.d.ts +100 -100
  47. package/lib/esm/CreateElementTool.js +317 -319
  48. package/lib/esm/CreateElementTool.js.map +1 -1
  49. package/lib/esm/DeleteElementsTool.d.ts +13 -13
  50. package/lib/esm/DeleteElementsTool.js +35 -34
  51. package/lib/esm/DeleteElementsTool.js.map +1 -1
  52. package/lib/esm/EditTool.d.ts +38 -38
  53. package/lib/esm/EditTool.js +77 -77
  54. package/lib/esm/EditTool.js.map +1 -1
  55. package/lib/esm/EditToolIpc.d.ts +18 -18
  56. package/lib/esm/EditToolIpc.js +24 -24
  57. package/lib/esm/ElementGeometryTool.d.ts +147 -147
  58. package/lib/esm/ElementGeometryTool.js +696 -701
  59. package/lib/esm/ElementGeometryTool.js.map +1 -1
  60. package/lib/esm/ModifyCurveTools.d.ts +139 -139
  61. package/lib/esm/ModifyCurveTools.js +771 -775
  62. package/lib/esm/ModifyCurveTools.js.map +1 -1
  63. package/lib/esm/ModifyElementTool.d.ts +47 -47
  64. package/lib/esm/ModifyElementTool.js +172 -172
  65. package/lib/esm/ModifyElementTool.js.map +1 -1
  66. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.d.ts +135 -135
  67. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.js +818 -814
  68. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.js.map +1 -1
  69. package/lib/esm/ProjectLocation/ProjectGeolocation.d.ts +135 -135
  70. package/lib/esm/ProjectLocation/ProjectGeolocation.js +529 -526
  71. package/lib/esm/ProjectLocation/ProjectGeolocation.js.map +1 -1
  72. package/lib/esm/SketchTools.d.ts +304 -304
  73. package/lib/esm/SketchTools.js +1700 -1695
  74. package/lib/esm/SketchTools.js.map +1 -1
  75. package/lib/esm/SolidModelingTools.d.ts +380 -380
  76. package/lib/esm/SolidModelingTools.js +1444 -1437
  77. package/lib/esm/SolidModelingTools.js.map +1 -1
  78. package/lib/esm/SolidPrimitiveTools.d.ts +318 -318
  79. package/lib/esm/SolidPrimitiveTools.js +1368 -1367
  80. package/lib/esm/SolidPrimitiveTools.js.map +1 -1
  81. package/lib/esm/TransformElementsTool.d.ts +164 -164
  82. package/lib/esm/TransformElementsTool.js +647 -644
  83. package/lib/esm/TransformElementsTool.js.map +1 -1
  84. package/lib/esm/UndoRedoTool.d.ts +16 -16
  85. package/lib/esm/UndoRedoTool.js +38 -36
  86. package/lib/esm/UndoRedoTool.js.map +1 -1
  87. package/lib/esm/editor-frontend.d.ts +17 -17
  88. package/lib/esm/editor-frontend.js +21 -21
  89. package/package.json +20 -20
@@ -1,702 +1,697 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
- * See LICENSE.md in the project root for license terms and full copyright notice.
4
- *--------------------------------------------------------------------------------------------*/
5
- /** @packageDocumentation
6
- * @module Editing
7
- */
8
- import { Id64 } from "@itwin/core-bentley";
9
- import { FeatureAppearance, FeatureAppearanceProvider, RgbColor } from "@itwin/core-common";
10
- import { AccuDrawHintBuilder, BeModifierKeys, CoordSource, CoreTools, ElementSetTool, EventHandled, GraphicBranch, GraphicType, IModelApp, InputSource, readElementGraphics, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
11
- import { Point3d, Range3d, Transform } from "@itwin/core-geometry";
12
- import { BRepEntityType, editorBuiltInCmdIds, SubEntityType, } from "@itwin/editor-common";
13
- import { computeChordToleranceFromPoint } from "./CreateElementTool";
14
- import { EditTools } from "./EditTool";
15
- import { solidModelingIpc } from "./EditToolIpc";
16
- /** @alpha */
17
- export class ElementGeometryGraphicsProvider {
18
- constructor(iModel) {
19
- this.iModel = iModel;
20
- }
21
- /** Call to request a RenderGraphic for the supplied graphic data.
22
- * @see [[cleanupGraphic]] Must be called when the tool exits.
23
- */
24
- async createGraphic(graphicData) {
25
- try {
26
- const graphic = await readElementGraphics(graphicData, this.iModel, Id64.invalid, true, { noFlash: true, noHilite: true });
27
- const graphicOwner = graphic ? IModelApp.renderSystem.createGraphicOwner(graphic) : undefined;
28
- this.cleanupGraphic();
29
- return (undefined !== (this.graphic = graphicOwner));
30
- }
31
- catch {
32
- return false;
33
- }
34
- }
35
- /** Call to dispose of [[RenderGraphic]] held by [[RenderGraphicOwner]].
36
- * @note Must be called when the tool exits to avoid leaks of graphics memory or other webgl resources.
37
- */
38
- cleanupGraphic() {
39
- if (undefined === this.graphic)
40
- return;
41
- this.graphic.disposeGraphic();
42
- this.graphic = undefined;
43
- }
44
- addGraphic(context, transform, opts) {
45
- if (undefined === this.graphic)
46
- return;
47
- if (undefined === transform && undefined === opts) {
48
- context.addGraphic(this.graphic);
49
- return;
50
- }
51
- const branch = new GraphicBranch(false);
52
- branch.add(this.graphic);
53
- const branchGraphic = context.createGraphicBranch(branch, transform ? transform : Transform.createIdentity(), opts);
54
- context.addGraphic(branchGraphic);
55
- }
56
- addDecoration(context, type, transform, opts) {
57
- if (undefined === this.graphic)
58
- return;
59
- const branch = new GraphicBranch(false);
60
- branch.add(this.graphic);
61
- const branchGraphic = context.createGraphicBranch(branch, transform ? transform : Transform.createIdentity(), opts);
62
- context.addDecoration(type, branchGraphic);
63
- }
64
- }
65
- /** @alpha */
66
- export function isSameSubEntity(a, b) {
67
- if (a.type !== b.type)
68
- return false;
69
- if (a.id !== b.id)
70
- return false;
71
- if ((undefined !== a.index ? a.index : 0) !== (undefined !== b.index ? b.index : 0))
72
- return false;
73
- return true;
74
- }
75
- /** @alpha */
76
- export class SubEntityData {
77
- constructor(props) { this._props = props; }
78
- get props() { return this._props; }
79
- set props(value) {
80
- this.cleanupGraphic();
81
- this._props = value;
82
- }
83
- get geometry() { return this._geometry; }
84
- set geometry(value) { this._geometry = value; }
85
- isSame(other) { return isSameSubEntity(this._props, other); }
86
- getAppearance(vp, accepted) {
87
- var _a, _b, _c, _d;
88
- const color = vp.hilite.color;
89
- const rgb = RgbColor.fromColorDef(accepted ? color.inverse() : color);
90
- const transparency = 0.25;
91
- const emphasized = true; // Necessary for obscured sub-entities w/SceneGraphic...
92
- let weight;
93
- switch (this.props.type) {
94
- case SubEntityType.Face:
95
- break;
96
- case SubEntityType.Edge:
97
- const edgeWeight = accepted ? 1 : 3;
98
- weight = ((_b = (_a = this._geometry) === null || _a === void 0 ? void 0 : _a.appearance) === null || _b === void 0 ? void 0 : _b.weight) ? Math.min(this._geometry.appearance.weight + edgeWeight, 31) : edgeWeight;
99
- break;
100
- case SubEntityType.Vertex:
101
- const vertexWeight = accepted ? 8 : 10;
102
- weight = ((_d = (_c = this._geometry) === null || _c === void 0 ? void 0 : _c.appearance) === null || _d === void 0 ? void 0 : _d.weight) ? Math.min(this._geometry.appearance.weight + vertexWeight, 31) : vertexWeight;
103
- break;
104
- }
105
- return FeatureAppearance.fromJSON({ rgb, transparency, weight, emphasized, nonLocatable: true });
106
- }
107
- async createGraphic(iModel) {
108
- var _a;
109
- if (undefined === ((_a = this._geometry) === null || _a === void 0 ? void 0 : _a.graphic))
110
- return false;
111
- if (undefined === this._graphicsProvider)
112
- this._graphicsProvider = new ElementGeometryGraphicsProvider(iModel);
113
- return this._graphicsProvider.createGraphic(this._geometry.graphic);
114
- }
115
- cleanupGraphic() {
116
- if (undefined === this._graphicsProvider)
117
- return;
118
- this._graphicsProvider.cleanupGraphic();
119
- this._graphicsProvider = undefined;
120
- }
121
- get hasGraphic() {
122
- var _a;
123
- return (undefined !== ((_a = this._graphicsProvider) === null || _a === void 0 ? void 0 : _a.graphic));
124
- }
125
- display(context, accepted) {
126
- var _a, _b;
127
- if (undefined === ((_a = this._graphicsProvider) === null || _a === void 0 ? void 0 : _a.graphic))
128
- return;
129
- const range = (((_b = this._geometry) === null || _b === void 0 ? void 0 : _b.range) ? Range3d.fromJSON(this._geometry.range) : undefined);
130
- const pixelSize = context.viewport.getPixelSizeAtPoint(range ? range.center : undefined);
131
- const offsetDir = context.viewport.view.getZVector();
132
- offsetDir.scaleToLength(3 * pixelSize, offsetDir);
133
- const offsetTrans = Transform.createTranslation(offsetDir);
134
- const appearanceProvider = FeatureAppearanceProvider.supplement((app) => {
135
- return app.extendAppearance(this.getAppearance(context.viewport, accepted));
136
- });
137
- this._graphicsProvider.addDecoration(context, GraphicType.Scene, offsetTrans, { appearanceProvider });
138
- }
139
- }
140
- /** @alpha Base class for tools that want to use the backend geometry cache. */
141
- export class ElementGeometryCacheTool extends ElementSetTool {
142
- constructor() {
143
- super(...arguments);
144
- this._checkedIds = new Map();
145
- this._firstResult = true;
146
- }
147
- allowView(vp) { return vp.view.is3d(); }
148
- isCompatibleViewport(vp, isSelectedViewChange) { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && this.allowView(vp)); }
149
- async startCommand() {
150
- if (undefined !== this._startedCmd)
151
- return this._startedCmd;
152
- return EditTools.startCommand({ commandId: editorBuiltInCmdIds.cmdSolidModeling, iModelKey: this.iModel.key });
153
- }
154
- agendaAppearance(isDynamics) {
155
- if (isDynamics) {
156
- if (undefined === this._agendaAppearanceDynamic)
157
- this._agendaAppearanceDynamic = FeatureAppearance.fromTransparency(0.0);
158
- return this._agendaAppearanceDynamic;
159
- }
160
- if (undefined === this._agendaAppearanceDefault)
161
- this._agendaAppearanceDefault = FeatureAppearance.fromTransparency(0.9);
162
- return this._agendaAppearanceDefault;
163
- }
164
- get wantAgendaAppearanceOverride() { return false; }
165
- addFeatureOverrides(overrides, _vp) {
166
- if (this.agenda.isEmpty)
167
- return;
168
- const appearance = this.agendaAppearance(false);
169
- this.agenda.elements.forEach((elementId) => overrides.override({ elementId, appearance }));
170
- }
171
- updateAgendaAppearanceProvider(drop) {
172
- if (!this.wantAgendaAppearanceOverride)
173
- return;
174
- for (const vp of IModelApp.viewManager) {
175
- if (!this.allowView(vp))
176
- continue;
177
- if (drop || this.agenda.isEmpty)
178
- vp.dropFeatureOverrideProvider(this);
179
- else if (!vp.addFeatureOverrideProvider(this))
180
- vp.setFeatureOverrideProviderChanged();
181
- }
182
- }
183
- get geometryCacheFilter() { return undefined; }
184
- onGeometryCacheFilterChanged() { this._checkedIds.clear(); }
185
- async createElementGeometryCache(id) {
186
- // NOTE: Creates cache if it doesn't already exist then test new or existing cache against filter...
187
- try {
188
- this._startedCmd = await this.startCommand();
189
- return await solidModelingIpc.createElementGeometryCache(id, this.geometryCacheFilter);
190
- }
191
- catch (err) {
192
- return false;
193
- }
194
- }
195
- async acceptElementForOperation(id) {
196
- if (Id64.isInvalid(id) || Id64.isTransient(id))
197
- return false;
198
- let accept = this._checkedIds.get(id);
199
- if (undefined === accept) {
200
- if (this.agenda.isEmpty && this._checkedIds.size > 1000)
201
- this._checkedIds.clear(); // Limit auto-locate cache size to something reasonable...
202
- accept = await this.createElementGeometryCache(id);
203
- this._checkedIds.set(id, accept);
204
- }
205
- return accept;
206
- }
207
- async isElementValidForOperation(hit, out) {
208
- if (!await super.isElementValidForOperation(hit, out))
209
- return false;
210
- return this.acceptElementForOperation(hit.sourceId);
211
- }
212
- async postFilterIds(arg) {
213
- const ids = [];
214
- for (const id of Id64.iterable(arg)) {
215
- if (await this.acceptElementForOperation(id))
216
- ids.push(id);
217
- }
218
- return ids;
219
- }
220
- async getGroupIds(id) {
221
- return this.postFilterIds(await super.getGroupIds(id));
222
- }
223
- async getSelectionSetCandidates(ss) {
224
- return this.postFilterIds(await super.getSelectionSetCandidates(ss));
225
- }
226
- async getDragSelectCandidates(vp, origin, corner, method, overlap) {
227
- return this.postFilterIds(await super.getDragSelectCandidates(vp, origin, corner, method, overlap));
228
- }
229
- onDynamicFrame(_ev, context) {
230
- if (undefined === this._graphicsProvider)
231
- return;
232
- if (!this.wantAgendaAppearanceOverride)
233
- return this._graphicsProvider.addGraphic(context);
234
- const appearanceProvider = FeatureAppearanceProvider.supplement((app) => {
235
- return app.extendAppearance(this.agendaAppearance(true));
236
- });
237
- this._graphicsProvider.addGraphic(context, undefined, { appearanceProvider });
238
- }
239
- async onMouseMotion(ev) {
240
- return this.updateGraphic(ev, IModelApp.viewManager.inDynamicsMode);
241
- }
242
- async getGraphicData(_ev) { return undefined; }
243
- async updateGraphic(ev, isDynamics) {
244
- if (!isDynamics || this._graphicsPending)
245
- return; // Continue displaying previous graphic if new graphic is still pending...
246
- this._graphicsPending = true;
247
- const graphicData = await this.getGraphicData(ev);
248
- this._graphicsPending = undefined;
249
- if (undefined !== graphicData) {
250
- if (this._firstResult) {
251
- this.updateAgendaAppearanceProvider();
252
- this._firstResult = false;
253
- }
254
- return this.createGraphic(graphicData);
255
- }
256
- if (undefined !== this._graphicsProvider)
257
- this._graphicsProvider.cleanupGraphic();
258
- }
259
- async createGraphic(graphicData) {
260
- if (undefined === this._graphicsProvider)
261
- this._graphicsProvider = new ElementGeometryGraphicsProvider(this.iModel);
262
- await this._graphicsProvider.createGraphic(graphicData);
263
- }
264
- clearGraphic() {
265
- if (undefined === this._graphicsProvider)
266
- return;
267
- this._graphicsProvider.cleanupGraphic();
268
- this._graphicsProvider = undefined;
269
- }
270
- async clearElementGeometryCache() {
271
- try {
272
- this._startedCmd = await this.startCommand();
273
- await solidModelingIpc.clearElementGeometryCache();
274
- }
275
- catch (err) { }
276
- }
277
- async onUnsuspend() {
278
- if (!this._firstResult)
279
- this.updateAgendaAppearanceProvider();
280
- return super.onUnsuspend();
281
- }
282
- async onSuspend() {
283
- if (!this._firstResult)
284
- this.updateAgendaAppearanceProvider(true);
285
- return super.onSuspend();
286
- }
287
- async onPostInstall() {
288
- await super.onPostInstall();
289
- if (this.wantAgendaAppearanceOverride)
290
- this.agenda.manageHiliteState = false;
291
- }
292
- async onCleanup() {
293
- await super.onCleanup();
294
- await this.clearElementGeometryCache();
295
- this.updateAgendaAppearanceProvider(true);
296
- this.clearGraphic();
297
- }
298
- }
299
- /** @alpha Base class for tools that need to locate faces, edges, and vertices. */
300
- export class LocateSubEntityTool extends ElementGeometryCacheTool {
301
- constructor() {
302
- super(...arguments);
303
- this._acceptedSubEntities = [];
304
- this._summaryIds = new Map();
305
- }
306
- provideToolAssistance(mainInstrText, additionalInstr) {
307
- if (this.wantAdditionalSubEntities) {
308
- const faceKey = this.wantSubEntityType(SubEntityType.Face) ? "Face" : "";
309
- const edgeKey = this.wantSubEntityType(SubEntityType.Edge) ? "Edge" : "";
310
- const vertexKey = this.wantSubEntityType(SubEntityType.Vertex) ? "Vertex" : "";
311
- const subEntityKey = `${faceKey}${edgeKey}${vertexKey}`;
312
- if (0 === subEntityKey.length) {
313
- super.provideToolAssistance(mainInstrText, additionalInstr);
314
- return;
315
- }
316
- if (undefined === mainInstrText)
317
- mainInstrText = EditTools.translate(`LocateSubEntities.Identify.${subEntityKey}`);
318
- const leftMsg = EditTools.translate(`LocateSubEntities.Accept.${subEntityKey}`);
319
- const rightMsg = this.haveAcceptedSubEntities && this.allowSubEntitySelectNext ? EditTools.translate(`LocateSubEntities.AcceptNext.${subEntityKey}`) : CoreTools.translate("ElementSet.Inputs.Cancel");
320
- const mouseInstructions = [];
321
- const touchInstructions = [];
322
- if (!ToolAssistance.createTouchCursorInstructions(touchInstructions))
323
- touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, leftMsg, false, ToolAssistanceInputMethod.Touch));
324
- mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, leftMsg, false, ToolAssistanceInputMethod.Mouse));
325
- touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.TwoTouchTap, rightMsg, false, ToolAssistanceInputMethod.Touch));
326
- mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.RightClick, rightMsg, false, ToolAssistanceInputMethod.Mouse));
327
- if (this.allowSubEntityControlSelect)
328
- mouseInstructions.push(ToolAssistance.createModifierKeyInstruction(ToolAssistance.ctrlKey, ToolAssistanceImage.LeftClickDrag, EditTools.translate(`LocateSubEntities.IdentifyAdditional.${subEntityKey}`), false, ToolAssistanceInputMethod.Mouse));
329
- if (undefined !== additionalInstr) {
330
- for (const instr of additionalInstr) {
331
- if (ToolAssistanceInputMethod.Touch === instr.inputMethod)
332
- touchInstructions.push(instr);
333
- else
334
- mouseInstructions.push(instr);
335
- }
336
- }
337
- const sections = [];
338
- sections.push(ToolAssistance.createSection(mouseInstructions, ToolAssistance.inputsLabel));
339
- sections.push(ToolAssistance.createSection(touchInstructions, ToolAssistance.inputsLabel));
340
- const mainInstruction = ToolAssistance.createInstruction(this.iconSpec, mainInstrText);
341
- const instructions = ToolAssistance.createInstructions(mainInstruction, sections);
342
- IModelApp.notifications.setToolAssistance(instructions);
343
- return;
344
- }
345
- super.provideToolAssistance(mainInstrText, additionalInstr);
346
- }
347
- get wantAgendaAppearanceOverride() { return true; }
348
- get wantGeometrySummary() { return false; }
349
- get wantSubEntitySnap() { return false; }
350
- wantSubEntityType(type) { return SubEntityType.Face === type; }
351
- getMaximumSubEntityHits(type) { return this.wantSubEntityType(type) ? 25 : 0; }
352
- get requiredSubEntityCount() { return 1; }
353
- get haveAcceptedSubEntities() { return (0 !== this._acceptedSubEntities.length); }
354
- get inhibitSubEntityDisplay() { return this.isDynamicsStarted; }
355
- get allowSubEntityControlSelect() { return true; }
356
- get allowSubEntityControlDeselect() { return this.allowSubEntityControlSelect; }
357
- get allowSubEntitySelectNext() { return !this.isDynamicsStarted; }
358
- getBRepEntityTypeForSubEntity(id, subEntity) {
359
- const summary = this._summaryIds.get(id);
360
- if (undefined === summary)
361
- return BRepEntityType.Invalid;
362
- const index = (undefined !== subEntity.index ? subEntity.index : 0);
363
- if (index >= summary.length)
364
- return BRepEntityType.Invalid;
365
- return summary[index];
366
- }
367
- async createElementGeometrySummary(id) {
368
- let summary = this._summaryIds.get(id);
369
- if (undefined === summary) {
370
- if (this.agenda.isEmpty && this._summaryIds.size > 1000)
371
- this._summaryIds.clear(); // Limit auto-locate cache size to something reasonable...
372
- try {
373
- this._startedCmd = await this.startCommand();
374
- if (undefined === (summary = await solidModelingIpc.summarizeElementGeometryCache(id)))
375
- return false;
376
- }
377
- catch (err) {
378
- return false;
379
- }
380
- this._summaryIds.set(id, summary);
381
- }
382
- return true;
383
- }
384
- async createElementGeometryCache(id) {
385
- const accept = await super.createElementGeometryCache(id);
386
- return (accept && this.wantGeometrySummary ? this.createElementGeometrySummary(id) : accept);
387
- }
388
- getAcceptedSubEntityData(index = -1) {
389
- if (-1 === index)
390
- index = this._acceptedSubEntities.length - 1;
391
- if (index < 0 || index > this._acceptedSubEntities.length - 1)
392
- return undefined;
393
- return this._acceptedSubEntities[index];
394
- }
395
- getAcceptedSubEntities() {
396
- const accepted = [];
397
- this._acceptedSubEntities.forEach((entry) => accepted.push(entry.props));
398
- return accepted;
399
- }
400
- drawSubEntity(context, subEntity, accepted) {
401
- subEntity.display(context, accepted);
402
- }
403
- drawAcceptedSubEntities(context) {
404
- this._acceptedSubEntities.forEach((entry) => this.drawSubEntity(context, entry, true));
405
- }
406
- decorate(context) {
407
- if (this.inhibitSubEntityDisplay || !this.allowView(context.viewport))
408
- return;
409
- if (this.haveAcceptedSubEntities)
410
- this.drawAcceptedSubEntities(context);
411
- if (undefined !== this._currentSubEntity)
412
- this.drawSubEntity(context, this._currentSubEntity, false);
413
- }
414
- decorateSuspended(context) {
415
- if (this.inhibitSubEntityDisplay || !this.allowView(context.viewport))
416
- return;
417
- if (this.haveAcceptedSubEntities)
418
- this.drawAcceptedSubEntities(context);
419
- }
420
- getLocateAperture(ev) {
421
- if (undefined === ev.viewport)
422
- return 0.0;
423
- return ev.viewport.pixelsFromInches(InputSource.Touch === ev.inputSource ? IModelApp.locateManager.touchApertureInches : IModelApp.locateManager.apertureInches);
424
- }
425
- getMaxRayDistance(ev, aperture) {
426
- if (undefined === ev.viewport)
427
- return 0.0;
428
- // NOTE: Compute a world coordinate radius for ray test, try getting aperture size at point on element...
429
- const hit = IModelApp.accuSnap.currHit;
430
- const vec = [];
431
- vec[0] = ev.viewport.worldToView(hit ? hit.hitPoint : ev.point);
432
- vec[1] = vec[0].clone();
433
- vec[1].x += 1;
434
- ev.viewport.viewToWorldArray(vec);
435
- // The edge and vertex hits get post-filtered on xy distance, so this is fine for perspective views...
436
- return (aperture * vec[0].distance(vec[1]));
437
- }
438
- getRayOrigin(ev) {
439
- const spacePoint = ev.point.clone();
440
- const vp = ev.viewport;
441
- if (undefined === vp)
442
- return spacePoint;
443
- vp.worldToNpc(spacePoint, spacePoint);
444
- spacePoint.z = 1.0;
445
- vp.npcToWorld(spacePoint, spacePoint);
446
- return spacePoint;
447
- }
448
- wantHiddenEdges(vp) {
449
- return vp.viewFlags.hiddenEdgesVisible();
450
- }
451
- getSubEntityFilter() { return undefined; }
452
- async pickSubEntities(id, boresite, maxFace, maxEdge, maxVertex, maxDistance, hiddenEdgesVisible, filter) {
453
- try {
454
- this._startedCmd = await this.startCommand();
455
- const opts = {
456
- maxFace,
457
- maxEdge,
458
- maxVertex,
459
- maxDistance,
460
- hiddenEdgesVisible,
461
- filter,
462
- };
463
- return await solidModelingIpc.locateSubEntities(id, boresite.origin, boresite.direction, opts);
464
- }
465
- catch (err) {
466
- return undefined;
467
- }
468
- }
469
- async doPickSubEntities(id, ev) {
470
- const vp = ev.viewport;
471
- if (undefined === vp)
472
- return undefined;
473
- const maxFace = this.getMaximumSubEntityHits(SubEntityType.Face);
474
- const maxEdge = this.getMaximumSubEntityHits(SubEntityType.Edge);
475
- const maxVertex = this.getMaximumSubEntityHits(SubEntityType.Vertex);
476
- if (0 === maxFace && 0 === maxEdge && 0 === maxVertex)
477
- return undefined;
478
- const aperture = this.getLocateAperture(ev);
479
- const maxDistance = this.getMaxRayDistance(ev, aperture);
480
- const spacePoint = this.getRayOrigin(ev);
481
- const boresite = AccuDrawHintBuilder.getBoresite(spacePoint, vp);
482
- const hiddenEdgesVisible = this.wantHiddenEdges(vp);
483
- const filter = this.getSubEntityFilter();
484
- let hits = await this.pickSubEntities(id, boresite, maxFace, maxEdge, maxVertex, maxDistance, hiddenEdgesVisible, filter);
485
- // NOTE: Remove erroneous edge/vertex hits in perspective views by checking real xy distance to hit point...
486
- if (undefined === hits || !vp.isCameraOn)
487
- return hits;
488
- if (maxEdge > 0 && hits.length > 1) {
489
- const edgeApertureSquared = (aperture * aperture);
490
- const vertexApertureSquared = ((aperture * 2.0) * (aperture * 2.0));
491
- const e2 = Math.pow(aperture, 2);
492
- const v2 = Math.pow(aperture * 2.0, 2);
493
- if (e2 !== edgeApertureSquared || v2 !== vertexApertureSquared)
494
- return hits;
495
- const rayOrigin = vp.worldToView(boresite.origin);
496
- hits = hits.filter((hit) => {
497
- if (SubEntityType.Face === hit.subEntity.type)
498
- return true;
499
- const hitPoint = vp.worldToView(Point3d.fromJSON(hit.point));
500
- const distance = hitPoint.distanceSquaredXY(rayOrigin);
501
- return (distance <= (SubEntityType.Edge === hit.subEntity.type ? edgeApertureSquared : vertexApertureSquared));
502
- });
503
- }
504
- return hits;
505
- }
506
- async createSubEntityData(id, hit) {
507
- const data = new SubEntityData(hit.subEntity);
508
- const chordTolerance = (this.targetView ? computeChordToleranceFromPoint(this.targetView, Point3d.fromJSON(hit.point)) : undefined);
509
- await this.createSubEntityGraphic(id, data, chordTolerance);
510
- return data;
511
- }
512
- /** Append specified sub-entity to accepted array. */
513
- async addSubEntity(id, props) {
514
- this._acceptedSubEntities.push(await this.createSubEntityData(id, props));
515
- }
516
- /** Remove specified sub-entity from accepted array, or pop last sub-entity if undefined. */
517
- async removeSubEntity(_id, props) {
518
- if (undefined !== props)
519
- this._acceptedSubEntities = this._acceptedSubEntities.filter((entry) => !isSameSubEntity(entry.props, props.subEntity));
520
- else
521
- this._acceptedSubEntities.pop();
522
- }
523
- /** Locate sub-entities for the most recently added (last) agenda entry. Tool sub-classes that wish to identity
524
- * sub-entities from multiple elements are responsible for maintaining the sub-entities per-element.
525
- */
526
- async doLocateSubEntity(ev, newSearch) {
527
- var _a;
528
- if (this.agenda.isEmpty || undefined === ev.viewport)
529
- return false;
530
- const id = this.agenda.elements[this.agenda.length - 1];
531
- if (newSearch) {
532
- this._locatedSubEntities = await this.doPickSubEntities(id, ev);
533
- if (undefined === this._locatedSubEntities || 0 === this._locatedSubEntities.length)
534
- return false;
535
- /** NOTE: Set last button location to point on sub-entity when not snapping.
536
- * If dynamics are enabled on this event, onDynamicFrame is called with this location.
537
- */
538
- if (CoordSource.ElemSnap !== ev.coordsFrom) {
539
- ev.point.setFrom(Point3d.fromJSON(this._locatedSubEntities[0].point));
540
- IModelApp.toolAdmin.setAdjustedDataPoint(ev);
541
- }
542
- }
543
- else {
544
- await this.removeSubEntity(id);
545
- }
546
- const hit = (_a = this._locatedSubEntities) === null || _a === void 0 ? void 0 : _a.shift();
547
- if (undefined !== hit) {
548
- if (undefined === this._acceptedSubEntities.find((entry) => isSameSubEntity(entry.props, hit.subEntity)))
549
- await this.addSubEntity(id, hit);
550
- else if (this.allowSubEntityControlDeselect)
551
- await this.removeSubEntity(id, hit);
552
- }
553
- IModelApp.viewManager.invalidateDecorationsAllViews();
554
- return true;
555
- }
556
- async chooseNextHit(ev) {
557
- if (!this.haveAcceptedSubEntities)
558
- return super.chooseNextHit(ev);
559
- if (!this.allowSubEntitySelectNext) {
560
- await this.onReinitialize(); // Don't cycle through hits after starting dynamics...
561
- }
562
- else {
563
- await this.doLocateSubEntity(ev, false);
564
- if (!this.haveAcceptedSubEntities)
565
- await this.onReinitialize();
566
- }
567
- return EventHandled.No;
568
- }
569
- get wantAdditionalSubEntities() {
570
- return (this._acceptedSubEntities.length < this.requiredSubEntityCount || (this.allowSubEntityControlSelect && this.isControlDown));
571
- }
572
- async gatherInput(ev) {
573
- if (this.wantAdditionalSubEntities) {
574
- await this.doLocateSubEntity(ev, true);
575
- if (this.wantAdditionalSubEntities) {
576
- this.setupAndPromptForNextAction();
577
- return EventHandled.No;
578
- }
579
- this.clearCurrentSubEntity();
580
- }
581
- return super.gatherInput(ev);
582
- }
583
- getCurrentElement() {
584
- if (!this.agenda.isEmpty)
585
- return this.agenda.elements[this.agenda.length - 1];
586
- const hit = IModelApp.accuSnap.currHit;
587
- return (undefined !== hit && hit.isElementHit ? hit.sourceId : undefined);
588
- }
589
- clearCurrentSubEntity() {
590
- if (undefined === this._currentSubEntity)
591
- return;
592
- this._currentSubEntity.cleanupGraphic();
593
- this._currentSubEntity = undefined;
594
- }
595
- async setCurrentSubEntity(id, hit, chordTolerance) {
596
- if (undefined === this._currentSubEntity)
597
- this._currentSubEntity = new SubEntityData(hit.subEntity);
598
- else
599
- this._currentSubEntity.props = hit.subEntity;
600
- return this.createSubEntityGraphic(id, this._currentSubEntity, chordTolerance);
601
- }
602
- async changeCurrentSubEntity(id, current, chordTolerance) {
603
- if (undefined === id || undefined === current) {
604
- if (undefined === this._currentSubEntity || !this._currentSubEntity.hasGraphic)
605
- return false;
606
- this._currentSubEntity.cleanupGraphic();
607
- return true;
608
- }
609
- if (undefined !== this._currentSubEntity && this._currentSubEntity.hasGraphic && this._currentSubEntity.isSame(current.subEntity))
610
- return false;
611
- return this.setCurrentSubEntity(id, current, chordTolerance);
612
- }
613
- async updateCurrentSubEntity(ev) {
614
- if (undefined === ev.viewport)
615
- return false;
616
- const id = this.wantAdditionalSubEntities ? this.getCurrentElement() : undefined;
617
- if (undefined === id)
618
- return this.changeCurrentSubEntity();
619
- if (this._subEntityGraphicPending)
620
- return false;
621
- this._subEntityGraphicPending = true;
622
- const current = await this.doPickSubEntities(id, ev);
623
- const chordTolerance = current ? computeChordToleranceFromPoint(ev.viewport, Point3d.fromJSON(current[0].point)) : 0.0;
624
- const status = await this.changeCurrentSubEntity(id, current ? current[0] : undefined, chordTolerance);
625
- this._subEntityGraphicPending = undefined;
626
- if (status)
627
- IModelApp.viewManager.invalidateDecorationsAllViews();
628
- return status;
629
- }
630
- async createSubEntityGraphic(id, data, chordTolerance) {
631
- try {
632
- const opts = {
633
- wantGraphic: true,
634
- wantRange: true,
635
- wantAppearance: true,
636
- chordTolerance,
637
- };
638
- data.chordTolerance = chordTolerance;
639
- data.geometry = await solidModelingIpc.getSubEntityGeometry(id, data.props, opts);
640
- return await data.createGraphic(this.iModel);
641
- }
642
- catch (err) {
643
- return false;
644
- }
645
- }
646
- async updateGraphic(ev, isDynamics) {
647
- if (isDynamics)
648
- return super.updateGraphic(ev, isDynamics);
649
- await this.updateCurrentSubEntity(ev);
650
- }
651
- async getGraphicData(ev) {
652
- const result = await this.applyAgendaOperation(ev, false);
653
- return result === null || result === void 0 ? void 0 : result.graphic;
654
- }
655
- async applyAgendaOperation(_ev, _isAccept) { return undefined; }
656
- async processAgenda(ev) {
657
- const result = await this.applyAgendaOperation(ev, true);
658
- if (result === null || result === void 0 ? void 0 : result.elementId)
659
- await this.saveChanges();
660
- }
661
- async onModifierKeyTransition(wentDown, modifier, event) {
662
- if (EventHandled.Yes === await super.onModifierKeyTransition(wentDown, modifier, event))
663
- return EventHandled.Yes;
664
- if (BeModifierKeys.Control !== modifier)
665
- return EventHandled.No;
666
- if (IModelApp.toolAdmin.isLocateCircleOn === this.wantAdditionalSubEntities)
667
- return EventHandled.No;
668
- this.setupAndPromptForNextAction();
669
- return EventHandled.Yes;
670
- }
671
- changeLocateState(enableLocate, enableSnap, cursor, coordLockOvr) {
672
- super.changeLocateState(enableLocate, enableSnap, cursor, coordLockOvr);
673
- // Keep showing locate circle when identifying sub-entities even if done locating elements...
674
- if (!IModelApp.toolAdmin.isLocateCircleOn && this.wantAdditionalSubEntities)
675
- IModelApp.toolAdmin.setLocateCircleOn(true);
676
- }
677
- get shouldEnableSnap() {
678
- if (this.isSelectByPoints || !this.wantAccuSnap)
679
- return false;
680
- if (this.isDynamicsStarted)
681
- return true;
682
- const isCtrlSelect = (this.isControlDown && (this.controlKeyContinuesSelection || this.allowSubEntityControlSelect));
683
- if (isCtrlSelect || this.wantAdditionalElements || this.wantAdditionalSubEntities)
684
- return this.wantSubEntitySnap;
685
- return !this.wantSubEntitySnap;
686
- }
687
- setupAccuDraw() { }
688
- setupAndPromptForNextAction() {
689
- this.setupAccuDraw();
690
- super.setupAndPromptForNextAction();
691
- }
692
- clearSubEntityGraphics() {
693
- if (undefined !== this._currentSubEntity)
694
- this._currentSubEntity.cleanupGraphic();
695
- this._acceptedSubEntities.forEach((entry) => entry.cleanupGraphic());
696
- }
697
- async onCleanup() {
698
- this.clearSubEntityGraphics();
699
- return super.onCleanup();
700
- }
701
- }
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /** @packageDocumentation
6
+ * @module Editing
7
+ */
8
+ import { Id64 } from "@itwin/core-bentley";
9
+ import { FeatureAppearance, FeatureAppearanceProvider, RgbColor } from "@itwin/core-common";
10
+ import { AccuDrawHintBuilder, BeModifierKeys, CoordSource, CoreTools, ElementSetTool, EventHandled, GraphicBranch, GraphicType, IModelApp, InputSource, readElementGraphics, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
11
+ import { Point3d, Range3d, Transform } from "@itwin/core-geometry";
12
+ import { BRepEntityType, editorBuiltInCmdIds, SubEntityType, } from "@itwin/editor-common";
13
+ import { computeChordToleranceFromPoint } from "./CreateElementTool";
14
+ import { EditTools } from "./EditTool";
15
+ import { solidModelingIpc } from "./EditToolIpc";
16
+ /** @alpha */
17
+ export class ElementGeometryGraphicsProvider {
18
+ constructor(iModel) {
19
+ this.iModel = iModel;
20
+ }
21
+ /** Call to request a RenderGraphic for the supplied graphic data.
22
+ * @see [[cleanupGraphic]] Must be called when the tool exits.
23
+ */
24
+ async createGraphic(graphicData) {
25
+ try {
26
+ const graphic = await readElementGraphics(graphicData, this.iModel, Id64.invalid, true, { noFlash: true, noHilite: true });
27
+ const graphicOwner = graphic ? IModelApp.renderSystem.createGraphicOwner(graphic) : undefined;
28
+ this.cleanupGraphic();
29
+ return (undefined !== (this.graphic = graphicOwner));
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /** Call to dispose of [[RenderGraphic]] held by [[RenderGraphicOwner]].
36
+ * @note Must be called when the tool exits to avoid leaks of graphics memory or other webgl resources.
37
+ */
38
+ cleanupGraphic() {
39
+ if (undefined === this.graphic)
40
+ return;
41
+ this.graphic.disposeGraphic();
42
+ this.graphic = undefined;
43
+ }
44
+ addGraphic(context, transform, opts) {
45
+ if (undefined === this.graphic)
46
+ return;
47
+ if (undefined === transform && undefined === opts) {
48
+ context.addGraphic(this.graphic);
49
+ return;
50
+ }
51
+ const branch = new GraphicBranch(false);
52
+ branch.add(this.graphic);
53
+ const branchGraphic = context.createGraphicBranch(branch, transform ? transform : Transform.createIdentity(), opts);
54
+ context.addGraphic(branchGraphic);
55
+ }
56
+ addDecoration(context, type, transform, opts) {
57
+ if (undefined === this.graphic)
58
+ return;
59
+ const branch = new GraphicBranch(false);
60
+ branch.add(this.graphic);
61
+ const branchGraphic = context.createGraphicBranch(branch, transform ? transform : Transform.createIdentity(), opts);
62
+ context.addDecoration(type, branchGraphic);
63
+ }
64
+ }
65
+ /** @alpha */
66
+ export function isSameSubEntity(a, b) {
67
+ if (a.type !== b.type)
68
+ return false;
69
+ if (a.id !== b.id)
70
+ return false;
71
+ if ((undefined !== a.index ? a.index : 0) !== (undefined !== b.index ? b.index : 0))
72
+ return false;
73
+ return true;
74
+ }
75
+ /** @alpha */
76
+ export class SubEntityData {
77
+ constructor(props) { this._props = props; }
78
+ get props() { return this._props; }
79
+ set props(value) {
80
+ this.cleanupGraphic();
81
+ this._props = value;
82
+ }
83
+ get geometry() { return this._geometry; }
84
+ set geometry(value) { this._geometry = value; }
85
+ isSame(other) { return isSameSubEntity(this._props, other); }
86
+ getAppearance(vp, accepted) {
87
+ const color = vp.hilite.color;
88
+ const rgb = RgbColor.fromColorDef(accepted ? color.inverse() : color);
89
+ const transparency = 0.25;
90
+ const emphasized = true; // Necessary for obscured sub-entities w/SceneGraphic...
91
+ let weight;
92
+ switch (this.props.type) {
93
+ case SubEntityType.Face:
94
+ break;
95
+ case SubEntityType.Edge:
96
+ const edgeWeight = accepted ? 1 : 3;
97
+ weight = this._geometry?.appearance?.weight ? Math.min(this._geometry.appearance.weight + edgeWeight, 31) : edgeWeight;
98
+ break;
99
+ case SubEntityType.Vertex:
100
+ const vertexWeight = accepted ? 8 : 10;
101
+ weight = this._geometry?.appearance?.weight ? Math.min(this._geometry.appearance.weight + vertexWeight, 31) : vertexWeight;
102
+ break;
103
+ }
104
+ return FeatureAppearance.fromJSON({ rgb, transparency, weight, emphasized, nonLocatable: true });
105
+ }
106
+ async createGraphic(iModel) {
107
+ if (undefined === this._geometry?.graphic)
108
+ return false;
109
+ if (undefined === this._graphicsProvider)
110
+ this._graphicsProvider = new ElementGeometryGraphicsProvider(iModel);
111
+ return this._graphicsProvider.createGraphic(this._geometry.graphic);
112
+ }
113
+ cleanupGraphic() {
114
+ if (undefined === this._graphicsProvider)
115
+ return;
116
+ this._graphicsProvider.cleanupGraphic();
117
+ this._graphicsProvider = undefined;
118
+ }
119
+ get hasGraphic() {
120
+ return (undefined !== this._graphicsProvider?.graphic);
121
+ }
122
+ display(context, accepted) {
123
+ if (undefined === this._graphicsProvider?.graphic)
124
+ return;
125
+ const range = (this._geometry?.range ? Range3d.fromJSON(this._geometry.range) : undefined);
126
+ const pixelSize = context.viewport.getPixelSizeAtPoint(range ? range.center : undefined);
127
+ const offsetDir = context.viewport.view.getZVector();
128
+ offsetDir.scaleToLength(3 * pixelSize, offsetDir);
129
+ const offsetTrans = Transform.createTranslation(offsetDir);
130
+ const appearanceProvider = FeatureAppearanceProvider.supplement((app) => {
131
+ return app.extendAppearance(this.getAppearance(context.viewport, accepted));
132
+ });
133
+ this._graphicsProvider.addDecoration(context, GraphicType.Scene, offsetTrans, { appearanceProvider });
134
+ }
135
+ }
136
+ /** @alpha Base class for tools that want to use the backend geometry cache. */
137
+ export class ElementGeometryCacheTool extends ElementSetTool {
138
+ constructor() {
139
+ super(...arguments);
140
+ this._checkedIds = new Map();
141
+ this._firstResult = true;
142
+ }
143
+ allowView(vp) { return vp.view.is3d(); }
144
+ isCompatibleViewport(vp, isSelectedViewChange) { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && this.allowView(vp)); }
145
+ async startCommand() {
146
+ if (undefined !== this._startedCmd)
147
+ return this._startedCmd;
148
+ return EditTools.startCommand({ commandId: editorBuiltInCmdIds.cmdSolidModeling, iModelKey: this.iModel.key });
149
+ }
150
+ agendaAppearance(isDynamics) {
151
+ if (isDynamics) {
152
+ if (undefined === this._agendaAppearanceDynamic)
153
+ this._agendaAppearanceDynamic = FeatureAppearance.fromTransparency(0.0);
154
+ return this._agendaAppearanceDynamic;
155
+ }
156
+ if (undefined === this._agendaAppearanceDefault)
157
+ this._agendaAppearanceDefault = FeatureAppearance.fromTransparency(0.9);
158
+ return this._agendaAppearanceDefault;
159
+ }
160
+ get wantAgendaAppearanceOverride() { return false; }
161
+ addFeatureOverrides(overrides, _vp) {
162
+ if (this.agenda.isEmpty)
163
+ return;
164
+ const appearance = this.agendaAppearance(false);
165
+ this.agenda.elements.forEach((elementId) => overrides.override({ elementId, appearance }));
166
+ }
167
+ updateAgendaAppearanceProvider(drop) {
168
+ if (!this.wantAgendaAppearanceOverride)
169
+ return;
170
+ for (const vp of IModelApp.viewManager) {
171
+ if (!this.allowView(vp))
172
+ continue;
173
+ if (drop || this.agenda.isEmpty)
174
+ vp.dropFeatureOverrideProvider(this);
175
+ else if (!vp.addFeatureOverrideProvider(this))
176
+ vp.setFeatureOverrideProviderChanged();
177
+ }
178
+ }
179
+ get geometryCacheFilter() { return undefined; }
180
+ onGeometryCacheFilterChanged() { this._checkedIds.clear(); }
181
+ async createElementGeometryCache(id) {
182
+ // NOTE: Creates cache if it doesn't already exist then test new or existing cache against filter...
183
+ try {
184
+ this._startedCmd = await this.startCommand();
185
+ return await solidModelingIpc.createElementGeometryCache(id, this.geometryCacheFilter);
186
+ }
187
+ catch (err) {
188
+ return false;
189
+ }
190
+ }
191
+ async acceptElementForOperation(id) {
192
+ if (Id64.isInvalid(id) || Id64.isTransient(id))
193
+ return false;
194
+ let accept = this._checkedIds.get(id);
195
+ if (undefined === accept) {
196
+ if (this.agenda.isEmpty && this._checkedIds.size > 1000)
197
+ this._checkedIds.clear(); // Limit auto-locate cache size to something reasonable...
198
+ accept = await this.createElementGeometryCache(id);
199
+ this._checkedIds.set(id, accept);
200
+ }
201
+ return accept;
202
+ }
203
+ async isElementValidForOperation(hit, out) {
204
+ if (!await super.isElementValidForOperation(hit, out))
205
+ return false;
206
+ return this.acceptElementForOperation(hit.sourceId);
207
+ }
208
+ async postFilterIds(arg) {
209
+ const ids = [];
210
+ for (const id of Id64.iterable(arg)) {
211
+ if (await this.acceptElementForOperation(id))
212
+ ids.push(id);
213
+ }
214
+ return ids;
215
+ }
216
+ async getGroupIds(id) {
217
+ return this.postFilterIds(await super.getGroupIds(id));
218
+ }
219
+ async getSelectionSetCandidates(ss) {
220
+ return this.postFilterIds(await super.getSelectionSetCandidates(ss));
221
+ }
222
+ async getDragSelectCandidates(vp, origin, corner, method, overlap) {
223
+ return this.postFilterIds(await super.getDragSelectCandidates(vp, origin, corner, method, overlap));
224
+ }
225
+ onDynamicFrame(_ev, context) {
226
+ if (undefined === this._graphicsProvider)
227
+ return;
228
+ if (!this.wantAgendaAppearanceOverride)
229
+ return this._graphicsProvider.addGraphic(context);
230
+ const appearanceProvider = FeatureAppearanceProvider.supplement((app) => {
231
+ return app.extendAppearance(this.agendaAppearance(true));
232
+ });
233
+ this._graphicsProvider.addGraphic(context, undefined, { appearanceProvider });
234
+ }
235
+ async onMouseMotion(ev) {
236
+ return this.updateGraphic(ev, IModelApp.viewManager.inDynamicsMode);
237
+ }
238
+ async getGraphicData(_ev) { return undefined; }
239
+ async updateGraphic(ev, isDynamics) {
240
+ if (!isDynamics || this._graphicsPending)
241
+ return; // Continue displaying previous graphic if new graphic is still pending...
242
+ this._graphicsPending = true;
243
+ const graphicData = await this.getGraphicData(ev);
244
+ this._graphicsPending = undefined;
245
+ if (undefined !== graphicData) {
246
+ if (this._firstResult) {
247
+ this.updateAgendaAppearanceProvider();
248
+ this._firstResult = false;
249
+ }
250
+ return this.createGraphic(graphicData);
251
+ }
252
+ if (undefined !== this._graphicsProvider)
253
+ this._graphicsProvider.cleanupGraphic();
254
+ }
255
+ async createGraphic(graphicData) {
256
+ if (undefined === this._graphicsProvider)
257
+ this._graphicsProvider = new ElementGeometryGraphicsProvider(this.iModel);
258
+ await this._graphicsProvider.createGraphic(graphicData);
259
+ }
260
+ clearGraphic() {
261
+ if (undefined === this._graphicsProvider)
262
+ return;
263
+ this._graphicsProvider.cleanupGraphic();
264
+ this._graphicsProvider = undefined;
265
+ }
266
+ async clearElementGeometryCache() {
267
+ try {
268
+ this._startedCmd = await this.startCommand();
269
+ await solidModelingIpc.clearElementGeometryCache();
270
+ }
271
+ catch (err) { }
272
+ }
273
+ async onUnsuspend() {
274
+ if (!this._firstResult)
275
+ this.updateAgendaAppearanceProvider();
276
+ return super.onUnsuspend();
277
+ }
278
+ async onSuspend() {
279
+ if (!this._firstResult)
280
+ this.updateAgendaAppearanceProvider(true);
281
+ return super.onSuspend();
282
+ }
283
+ async onPostInstall() {
284
+ await super.onPostInstall();
285
+ if (this.wantAgendaAppearanceOverride)
286
+ this.agenda.manageHiliteState = false;
287
+ }
288
+ async onCleanup() {
289
+ await super.onCleanup();
290
+ await this.clearElementGeometryCache();
291
+ this.updateAgendaAppearanceProvider(true);
292
+ this.clearGraphic();
293
+ }
294
+ }
295
+ /** @alpha Base class for tools that need to locate faces, edges, and vertices. */
296
+ export class LocateSubEntityTool extends ElementGeometryCacheTool {
297
+ constructor() {
298
+ super(...arguments);
299
+ this._acceptedSubEntities = [];
300
+ this._summaryIds = new Map();
301
+ }
302
+ provideToolAssistance(mainInstrText, additionalInstr) {
303
+ if (this.wantAdditionalSubEntities) {
304
+ const faceKey = this.wantSubEntityType(SubEntityType.Face) ? "Face" : "";
305
+ const edgeKey = this.wantSubEntityType(SubEntityType.Edge) ? "Edge" : "";
306
+ const vertexKey = this.wantSubEntityType(SubEntityType.Vertex) ? "Vertex" : "";
307
+ const subEntityKey = `${faceKey}${edgeKey}${vertexKey}`;
308
+ if (0 === subEntityKey.length) {
309
+ super.provideToolAssistance(mainInstrText, additionalInstr);
310
+ return;
311
+ }
312
+ if (undefined === mainInstrText)
313
+ mainInstrText = EditTools.translate(`LocateSubEntities.Identify.${subEntityKey}`);
314
+ const leftMsg = EditTools.translate(`LocateSubEntities.Accept.${subEntityKey}`);
315
+ const rightMsg = this.haveAcceptedSubEntities && this.allowSubEntitySelectNext ? EditTools.translate(`LocateSubEntities.AcceptNext.${subEntityKey}`) : CoreTools.translate("ElementSet.Inputs.Cancel");
316
+ const mouseInstructions = [];
317
+ const touchInstructions = [];
318
+ if (!ToolAssistance.createTouchCursorInstructions(touchInstructions))
319
+ touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, leftMsg, false, ToolAssistanceInputMethod.Touch));
320
+ mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, leftMsg, false, ToolAssistanceInputMethod.Mouse));
321
+ touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.TwoTouchTap, rightMsg, false, ToolAssistanceInputMethod.Touch));
322
+ mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.RightClick, rightMsg, false, ToolAssistanceInputMethod.Mouse));
323
+ if (this.allowSubEntityControlSelect)
324
+ mouseInstructions.push(ToolAssistance.createModifierKeyInstruction(ToolAssistance.ctrlKey, ToolAssistanceImage.LeftClickDrag, EditTools.translate(`LocateSubEntities.IdentifyAdditional.${subEntityKey}`), false, ToolAssistanceInputMethod.Mouse));
325
+ if (undefined !== additionalInstr) {
326
+ for (const instr of additionalInstr) {
327
+ if (ToolAssistanceInputMethod.Touch === instr.inputMethod)
328
+ touchInstructions.push(instr);
329
+ else
330
+ mouseInstructions.push(instr);
331
+ }
332
+ }
333
+ const sections = [];
334
+ sections.push(ToolAssistance.createSection(mouseInstructions, ToolAssistance.inputsLabel));
335
+ sections.push(ToolAssistance.createSection(touchInstructions, ToolAssistance.inputsLabel));
336
+ const mainInstruction = ToolAssistance.createInstruction(this.iconSpec, mainInstrText);
337
+ const instructions = ToolAssistance.createInstructions(mainInstruction, sections);
338
+ IModelApp.notifications.setToolAssistance(instructions);
339
+ return;
340
+ }
341
+ super.provideToolAssistance(mainInstrText, additionalInstr);
342
+ }
343
+ get wantAgendaAppearanceOverride() { return true; }
344
+ get wantGeometrySummary() { return false; }
345
+ get wantSubEntitySnap() { return false; }
346
+ wantSubEntityType(type) { return SubEntityType.Face === type; }
347
+ getMaximumSubEntityHits(type) { return this.wantSubEntityType(type) ? 25 : 0; }
348
+ get requiredSubEntityCount() { return 1; }
349
+ get haveAcceptedSubEntities() { return (0 !== this._acceptedSubEntities.length); }
350
+ get inhibitSubEntityDisplay() { return this.isDynamicsStarted; }
351
+ get allowSubEntityControlSelect() { return true; }
352
+ get allowSubEntityControlDeselect() { return this.allowSubEntityControlSelect; }
353
+ get allowSubEntitySelectNext() { return !this.isDynamicsStarted; }
354
+ getBRepEntityTypeForSubEntity(id, subEntity) {
355
+ const summary = this._summaryIds.get(id);
356
+ if (undefined === summary)
357
+ return BRepEntityType.Invalid;
358
+ const index = (undefined !== subEntity.index ? subEntity.index : 0);
359
+ if (index >= summary.length)
360
+ return BRepEntityType.Invalid;
361
+ return summary[index];
362
+ }
363
+ async createElementGeometrySummary(id) {
364
+ let summary = this._summaryIds.get(id);
365
+ if (undefined === summary) {
366
+ if (this.agenda.isEmpty && this._summaryIds.size > 1000)
367
+ this._summaryIds.clear(); // Limit auto-locate cache size to something reasonable...
368
+ try {
369
+ this._startedCmd = await this.startCommand();
370
+ if (undefined === (summary = await solidModelingIpc.summarizeElementGeometryCache(id)))
371
+ return false;
372
+ }
373
+ catch (err) {
374
+ return false;
375
+ }
376
+ this._summaryIds.set(id, summary);
377
+ }
378
+ return true;
379
+ }
380
+ async createElementGeometryCache(id) {
381
+ const accept = await super.createElementGeometryCache(id);
382
+ return (accept && this.wantGeometrySummary ? this.createElementGeometrySummary(id) : accept);
383
+ }
384
+ getAcceptedSubEntityData(index = -1) {
385
+ if (-1 === index)
386
+ index = this._acceptedSubEntities.length - 1;
387
+ if (index < 0 || index > this._acceptedSubEntities.length - 1)
388
+ return undefined;
389
+ return this._acceptedSubEntities[index];
390
+ }
391
+ getAcceptedSubEntities() {
392
+ const accepted = [];
393
+ this._acceptedSubEntities.forEach((entry) => accepted.push(entry.props));
394
+ return accepted;
395
+ }
396
+ drawSubEntity(context, subEntity, accepted) {
397
+ subEntity.display(context, accepted);
398
+ }
399
+ drawAcceptedSubEntities(context) {
400
+ this._acceptedSubEntities.forEach((entry) => this.drawSubEntity(context, entry, true));
401
+ }
402
+ decorate(context) {
403
+ if (this.inhibitSubEntityDisplay || !this.allowView(context.viewport))
404
+ return;
405
+ if (this.haveAcceptedSubEntities)
406
+ this.drawAcceptedSubEntities(context);
407
+ if (undefined !== this._currentSubEntity)
408
+ this.drawSubEntity(context, this._currentSubEntity, false);
409
+ }
410
+ decorateSuspended(context) {
411
+ if (this.inhibitSubEntityDisplay || !this.allowView(context.viewport))
412
+ return;
413
+ if (this.haveAcceptedSubEntities)
414
+ this.drawAcceptedSubEntities(context);
415
+ }
416
+ getLocateAperture(ev) {
417
+ if (undefined === ev.viewport)
418
+ return 0.0;
419
+ return ev.viewport.pixelsFromInches(InputSource.Touch === ev.inputSource ? IModelApp.locateManager.touchApertureInches : IModelApp.locateManager.apertureInches);
420
+ }
421
+ getMaxRayDistance(ev, aperture) {
422
+ if (undefined === ev.viewport)
423
+ return 0.0;
424
+ // NOTE: Compute a world coordinate radius for ray test, try getting aperture size at point on element...
425
+ const hit = IModelApp.accuSnap.currHit;
426
+ const vec = [];
427
+ vec[0] = ev.viewport.worldToView(hit ? hit.hitPoint : ev.point);
428
+ vec[1] = vec[0].clone();
429
+ vec[1].x += 1;
430
+ ev.viewport.viewToWorldArray(vec);
431
+ // The edge and vertex hits get post-filtered on xy distance, so this is fine for perspective views...
432
+ return (aperture * vec[0].distance(vec[1]));
433
+ }
434
+ getRayOrigin(ev) {
435
+ const spacePoint = ev.point.clone();
436
+ const vp = ev.viewport;
437
+ if (undefined === vp)
438
+ return spacePoint;
439
+ vp.worldToNpc(spacePoint, spacePoint);
440
+ spacePoint.z = 1.0;
441
+ vp.npcToWorld(spacePoint, spacePoint);
442
+ return spacePoint;
443
+ }
444
+ wantHiddenEdges(vp) {
445
+ return vp.viewFlags.hiddenEdgesVisible();
446
+ }
447
+ getSubEntityFilter() { return undefined; }
448
+ async pickSubEntities(id, boresite, maxFace, maxEdge, maxVertex, maxDistance, hiddenEdgesVisible, filter) {
449
+ try {
450
+ this._startedCmd = await this.startCommand();
451
+ const opts = {
452
+ maxFace,
453
+ maxEdge,
454
+ maxVertex,
455
+ maxDistance,
456
+ hiddenEdgesVisible,
457
+ filter,
458
+ };
459
+ return await solidModelingIpc.locateSubEntities(id, boresite.origin, boresite.direction, opts);
460
+ }
461
+ catch (err) {
462
+ return undefined;
463
+ }
464
+ }
465
+ async doPickSubEntities(id, ev) {
466
+ const vp = ev.viewport;
467
+ if (undefined === vp)
468
+ return undefined;
469
+ const maxFace = this.getMaximumSubEntityHits(SubEntityType.Face);
470
+ const maxEdge = this.getMaximumSubEntityHits(SubEntityType.Edge);
471
+ const maxVertex = this.getMaximumSubEntityHits(SubEntityType.Vertex);
472
+ if (0 === maxFace && 0 === maxEdge && 0 === maxVertex)
473
+ return undefined;
474
+ const aperture = this.getLocateAperture(ev);
475
+ const maxDistance = this.getMaxRayDistance(ev, aperture);
476
+ const spacePoint = this.getRayOrigin(ev);
477
+ const boresite = AccuDrawHintBuilder.getBoresite(spacePoint, vp);
478
+ const hiddenEdgesVisible = this.wantHiddenEdges(vp);
479
+ const filter = this.getSubEntityFilter();
480
+ let hits = await this.pickSubEntities(id, boresite, maxFace, maxEdge, maxVertex, maxDistance, hiddenEdgesVisible, filter);
481
+ // NOTE: Remove erroneous edge/vertex hits in perspective views by checking real xy distance to hit point...
482
+ if (undefined === hits || !vp.isCameraOn)
483
+ return hits;
484
+ if (maxEdge > 0 && hits.length > 1) {
485
+ const edgeApertureSquared = (aperture * aperture);
486
+ const vertexApertureSquared = ((aperture * 2.0) * (aperture * 2.0));
487
+ const e2 = Math.pow(aperture, 2);
488
+ const v2 = Math.pow(aperture * 2.0, 2);
489
+ if (e2 !== edgeApertureSquared || v2 !== vertexApertureSquared)
490
+ return hits;
491
+ const rayOrigin = vp.worldToView(boresite.origin);
492
+ hits = hits.filter((hit) => {
493
+ if (SubEntityType.Face === hit.subEntity.type)
494
+ return true;
495
+ const hitPoint = vp.worldToView(Point3d.fromJSON(hit.point));
496
+ const distance = hitPoint.distanceSquaredXY(rayOrigin);
497
+ return (distance <= (SubEntityType.Edge === hit.subEntity.type ? edgeApertureSquared : vertexApertureSquared));
498
+ });
499
+ }
500
+ return hits;
501
+ }
502
+ async createSubEntityData(id, hit) {
503
+ const data = new SubEntityData(hit.subEntity);
504
+ const chordTolerance = (this.targetView ? computeChordToleranceFromPoint(this.targetView, Point3d.fromJSON(hit.point)) : undefined);
505
+ await this.createSubEntityGraphic(id, data, chordTolerance);
506
+ return data;
507
+ }
508
+ /** Append specified sub-entity to accepted array. */
509
+ async addSubEntity(id, props) {
510
+ this._acceptedSubEntities.push(await this.createSubEntityData(id, props));
511
+ }
512
+ /** Remove specified sub-entity from accepted array, or pop last sub-entity if undefined. */
513
+ async removeSubEntity(_id, props) {
514
+ if (undefined !== props)
515
+ this._acceptedSubEntities = this._acceptedSubEntities.filter((entry) => !isSameSubEntity(entry.props, props.subEntity));
516
+ else
517
+ this._acceptedSubEntities.pop();
518
+ }
519
+ /** Locate sub-entities for the most recently added (last) agenda entry. Tool sub-classes that wish to identity
520
+ * sub-entities from multiple elements are responsible for maintaining the sub-entities per-element.
521
+ */
522
+ async doLocateSubEntity(ev, newSearch) {
523
+ if (this.agenda.isEmpty || undefined === ev.viewport)
524
+ return false;
525
+ const id = this.agenda.elements[this.agenda.length - 1];
526
+ if (newSearch) {
527
+ this._locatedSubEntities = await this.doPickSubEntities(id, ev);
528
+ if (undefined === this._locatedSubEntities || 0 === this._locatedSubEntities.length)
529
+ return false;
530
+ /** NOTE: Set last button location to point on sub-entity when not snapping.
531
+ * If dynamics are enabled on this event, onDynamicFrame is called with this location.
532
+ */
533
+ if (CoordSource.ElemSnap !== ev.coordsFrom) {
534
+ ev.point.setFrom(Point3d.fromJSON(this._locatedSubEntities[0].point));
535
+ IModelApp.toolAdmin.setAdjustedDataPoint(ev);
536
+ }
537
+ }
538
+ else {
539
+ await this.removeSubEntity(id);
540
+ }
541
+ const hit = this._locatedSubEntities?.shift();
542
+ if (undefined !== hit) {
543
+ if (undefined === this._acceptedSubEntities.find((entry) => isSameSubEntity(entry.props, hit.subEntity)))
544
+ await this.addSubEntity(id, hit);
545
+ else if (this.allowSubEntityControlDeselect)
546
+ await this.removeSubEntity(id, hit);
547
+ }
548
+ IModelApp.viewManager.invalidateDecorationsAllViews();
549
+ return true;
550
+ }
551
+ async chooseNextHit(ev) {
552
+ if (!this.haveAcceptedSubEntities)
553
+ return super.chooseNextHit(ev);
554
+ if (!this.allowSubEntitySelectNext) {
555
+ await this.onReinitialize(); // Don't cycle through hits after starting dynamics...
556
+ }
557
+ else {
558
+ await this.doLocateSubEntity(ev, false);
559
+ if (!this.haveAcceptedSubEntities)
560
+ await this.onReinitialize();
561
+ }
562
+ return EventHandled.No;
563
+ }
564
+ get wantAdditionalSubEntities() {
565
+ return (this._acceptedSubEntities.length < this.requiredSubEntityCount || (this.allowSubEntityControlSelect && this.isControlDown));
566
+ }
567
+ async gatherInput(ev) {
568
+ if (this.wantAdditionalSubEntities) {
569
+ await this.doLocateSubEntity(ev, true);
570
+ if (this.wantAdditionalSubEntities) {
571
+ this.setupAndPromptForNextAction();
572
+ return EventHandled.No;
573
+ }
574
+ this.clearCurrentSubEntity();
575
+ }
576
+ return super.gatherInput(ev);
577
+ }
578
+ getCurrentElement() {
579
+ if (!this.agenda.isEmpty)
580
+ return this.agenda.elements[this.agenda.length - 1];
581
+ const hit = IModelApp.accuSnap.currHit;
582
+ return (undefined !== hit && hit.isElementHit ? hit.sourceId : undefined);
583
+ }
584
+ clearCurrentSubEntity() {
585
+ if (undefined === this._currentSubEntity)
586
+ return;
587
+ this._currentSubEntity.cleanupGraphic();
588
+ this._currentSubEntity = undefined;
589
+ }
590
+ async setCurrentSubEntity(id, hit, chordTolerance) {
591
+ if (undefined === this._currentSubEntity)
592
+ this._currentSubEntity = new SubEntityData(hit.subEntity);
593
+ else
594
+ this._currentSubEntity.props = hit.subEntity;
595
+ return this.createSubEntityGraphic(id, this._currentSubEntity, chordTolerance);
596
+ }
597
+ async changeCurrentSubEntity(id, current, chordTolerance) {
598
+ if (undefined === id || undefined === current) {
599
+ if (undefined === this._currentSubEntity || !this._currentSubEntity.hasGraphic)
600
+ return false;
601
+ this._currentSubEntity.cleanupGraphic();
602
+ return true;
603
+ }
604
+ if (undefined !== this._currentSubEntity && this._currentSubEntity.hasGraphic && this._currentSubEntity.isSame(current.subEntity))
605
+ return false;
606
+ return this.setCurrentSubEntity(id, current, chordTolerance);
607
+ }
608
+ async updateCurrentSubEntity(ev) {
609
+ if (undefined === ev.viewport)
610
+ return false;
611
+ const id = this.wantAdditionalSubEntities ? this.getCurrentElement() : undefined;
612
+ if (undefined === id)
613
+ return this.changeCurrentSubEntity();
614
+ if (this._subEntityGraphicPending)
615
+ return false;
616
+ this._subEntityGraphicPending = true;
617
+ const current = await this.doPickSubEntities(id, ev);
618
+ const chordTolerance = current ? computeChordToleranceFromPoint(ev.viewport, Point3d.fromJSON(current[0].point)) : 0.0;
619
+ const status = await this.changeCurrentSubEntity(id, current ? current[0] : undefined, chordTolerance);
620
+ this._subEntityGraphicPending = undefined;
621
+ if (status)
622
+ IModelApp.viewManager.invalidateDecorationsAllViews();
623
+ return status;
624
+ }
625
+ async createSubEntityGraphic(id, data, chordTolerance) {
626
+ try {
627
+ const opts = {
628
+ wantGraphic: true,
629
+ wantRange: true,
630
+ wantAppearance: true,
631
+ chordTolerance,
632
+ };
633
+ data.chordTolerance = chordTolerance;
634
+ data.geometry = await solidModelingIpc.getSubEntityGeometry(id, data.props, opts);
635
+ return await data.createGraphic(this.iModel);
636
+ }
637
+ catch (err) {
638
+ return false;
639
+ }
640
+ }
641
+ async updateGraphic(ev, isDynamics) {
642
+ if (isDynamics)
643
+ return super.updateGraphic(ev, isDynamics);
644
+ await this.updateCurrentSubEntity(ev);
645
+ }
646
+ async getGraphicData(ev) {
647
+ const result = await this.applyAgendaOperation(ev, false);
648
+ return result?.graphic;
649
+ }
650
+ async applyAgendaOperation(_ev, _isAccept) { return undefined; }
651
+ async processAgenda(ev) {
652
+ const result = await this.applyAgendaOperation(ev, true);
653
+ if (result?.elementId)
654
+ await this.saveChanges();
655
+ }
656
+ async onModifierKeyTransition(wentDown, modifier, event) {
657
+ if (EventHandled.Yes === await super.onModifierKeyTransition(wentDown, modifier, event))
658
+ return EventHandled.Yes;
659
+ if (BeModifierKeys.Control !== modifier)
660
+ return EventHandled.No;
661
+ if (IModelApp.toolAdmin.isLocateCircleOn === this.wantAdditionalSubEntities)
662
+ return EventHandled.No;
663
+ this.setupAndPromptForNextAction();
664
+ return EventHandled.Yes;
665
+ }
666
+ changeLocateState(enableLocate, enableSnap, cursor, coordLockOvr) {
667
+ super.changeLocateState(enableLocate, enableSnap, cursor, coordLockOvr);
668
+ // Keep showing locate circle when identifying sub-entities even if done locating elements...
669
+ if (!IModelApp.toolAdmin.isLocateCircleOn && this.wantAdditionalSubEntities)
670
+ IModelApp.toolAdmin.setLocateCircleOn(true);
671
+ }
672
+ get shouldEnableSnap() {
673
+ if (this.isSelectByPoints || !this.wantAccuSnap)
674
+ return false;
675
+ if (this.isDynamicsStarted)
676
+ return true;
677
+ const isCtrlSelect = (this.isControlDown && (this.controlKeyContinuesSelection || this.allowSubEntityControlSelect));
678
+ if (isCtrlSelect || this.wantAdditionalElements || this.wantAdditionalSubEntities)
679
+ return this.wantSubEntitySnap;
680
+ return !this.wantSubEntitySnap;
681
+ }
682
+ setupAccuDraw() { }
683
+ setupAndPromptForNextAction() {
684
+ this.setupAccuDraw();
685
+ super.setupAndPromptForNextAction();
686
+ }
687
+ clearSubEntityGraphics() {
688
+ if (undefined !== this._currentSubEntity)
689
+ this._currentSubEntity.cleanupGraphic();
690
+ this._acceptedSubEntities.forEach((entry) => entry.cleanupGraphic());
691
+ }
692
+ async onCleanup() {
693
+ this.clearSubEntityGraphics();
694
+ return super.onCleanup();
695
+ }
696
+ }
702
697
  //# sourceMappingURL=ElementGeometryTool.js.map