@itwin/editor-frontend 3.5.0-dev.8 → 3.6.0-dev.3

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 (103) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/lib/cjs/DeleteElementsTool.d.ts +0 -2
  3. package/lib/cjs/DeleteElementsTool.d.ts.map +1 -1
  4. package/lib/cjs/DeleteElementsTool.js +3 -5
  5. package/lib/cjs/DeleteElementsTool.js.map +1 -1
  6. package/lib/cjs/EditTool.d.ts +0 -1
  7. package/lib/cjs/EditTool.d.ts.map +1 -1
  8. package/lib/cjs/EditTool.js +7 -7
  9. package/lib/cjs/EditTool.js.map +1 -1
  10. package/lib/cjs/EditToolIpc.d.ts +18 -0
  11. package/lib/cjs/EditToolIpc.d.ts.map +1 -0
  12. package/lib/cjs/EditToolIpc.js +28 -0
  13. package/lib/cjs/EditToolIpc.js.map +1 -0
  14. package/lib/cjs/ElementGeometryTool.d.ts +2 -3
  15. package/lib/cjs/ElementGeometryTool.d.ts.map +1 -1
  16. package/lib/cjs/ElementGeometryTool.js +8 -10
  17. package/lib/cjs/ElementGeometryTool.js.map +1 -1
  18. package/lib/cjs/ModifyCurveTools.d.ts +64 -43
  19. package/lib/cjs/ModifyCurveTools.d.ts.map +1 -1
  20. package/lib/cjs/ModifyCurveTools.js +318 -202
  21. package/lib/cjs/ModifyCurveTools.js.map +1 -1
  22. package/lib/cjs/ModifyElementTool.d.ts +48 -0
  23. package/lib/cjs/ModifyElementTool.d.ts.map +1 -0
  24. package/lib/cjs/ModifyElementTool.js +178 -0
  25. package/lib/cjs/ModifyElementTool.js.map +1 -0
  26. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.d.ts +0 -2
  27. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.d.ts.map +1 -1
  28. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.js +3 -5
  29. package/lib/cjs/ProjectLocation/ProjectExtentsDecoration.js.map +1 -1
  30. package/lib/cjs/SketchTools.d.ts +2 -4
  31. package/lib/cjs/SketchTools.d.ts.map +1 -1
  32. package/lib/cjs/SketchTools.js +8 -10
  33. package/lib/cjs/SketchTools.js.map +1 -1
  34. package/lib/cjs/SolidModelingTools.d.ts +3 -3
  35. package/lib/cjs/SolidModelingTools.d.ts.map +1 -1
  36. package/lib/cjs/SolidModelingTools.js +30 -29
  37. package/lib/cjs/SolidModelingTools.js.map +1 -1
  38. package/lib/cjs/SolidPrimitiveTools.d.ts +69 -5
  39. package/lib/cjs/SolidPrimitiveTools.d.ts.map +1 -1
  40. package/lib/cjs/SolidPrimitiveTools.js +291 -8
  41. package/lib/cjs/SolidPrimitiveTools.js.map +1 -1
  42. package/lib/cjs/TransformElementsTool.d.ts +3 -5
  43. package/lib/cjs/TransformElementsTool.d.ts.map +1 -1
  44. package/lib/cjs/TransformElementsTool.js +10 -12
  45. package/lib/cjs/TransformElementsTool.js.map +1 -1
  46. package/lib/cjs/UndoRedoTool.js +1 -1
  47. package/lib/cjs/UndoRedoTool.js.map +1 -1
  48. package/lib/cjs/editor-frontend.d.ts +5 -3
  49. package/lib/cjs/editor-frontend.d.ts.map +1 -1
  50. package/lib/cjs/editor-frontend.js +5 -3
  51. package/lib/cjs/editor-frontend.js.map +1 -1
  52. package/lib/esm/DeleteElementsTool.d.ts +0 -2
  53. package/lib/esm/DeleteElementsTool.d.ts.map +1 -1
  54. package/lib/esm/DeleteElementsTool.js +3 -5
  55. package/lib/esm/DeleteElementsTool.js.map +1 -1
  56. package/lib/esm/EditTool.d.ts +0 -1
  57. package/lib/esm/EditTool.d.ts.map +1 -1
  58. package/lib/esm/EditTool.js +8 -8
  59. package/lib/esm/EditTool.js.map +1 -1
  60. package/lib/esm/EditToolIpc.d.ts +18 -0
  61. package/lib/esm/EditToolIpc.d.ts.map +1 -0
  62. package/lib/esm/EditToolIpc.js +24 -0
  63. package/lib/esm/EditToolIpc.js.map +1 -0
  64. package/lib/esm/ElementGeometryTool.d.ts +2 -3
  65. package/lib/esm/ElementGeometryTool.d.ts.map +1 -1
  66. package/lib/esm/ElementGeometryTool.js +8 -10
  67. package/lib/esm/ElementGeometryTool.js.map +1 -1
  68. package/lib/esm/ModifyCurveTools.d.ts +64 -43
  69. package/lib/esm/ModifyCurveTools.d.ts.map +1 -1
  70. package/lib/esm/ModifyCurveTools.js +317 -202
  71. package/lib/esm/ModifyCurveTools.js.map +1 -1
  72. package/lib/esm/ModifyElementTool.d.ts +48 -0
  73. package/lib/esm/ModifyElementTool.d.ts.map +1 -0
  74. package/lib/esm/ModifyElementTool.js +173 -0
  75. package/lib/esm/ModifyElementTool.js.map +1 -0
  76. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.d.ts +0 -2
  77. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.d.ts.map +1 -1
  78. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.js +3 -5
  79. package/lib/esm/ProjectLocation/ProjectExtentsDecoration.js.map +1 -1
  80. package/lib/esm/SketchTools.d.ts +2 -4
  81. package/lib/esm/SketchTools.d.ts.map +1 -1
  82. package/lib/esm/SketchTools.js +8 -10
  83. package/lib/esm/SketchTools.js.map +1 -1
  84. package/lib/esm/SolidModelingTools.d.ts +3 -3
  85. package/lib/esm/SolidModelingTools.d.ts.map +1 -1
  86. package/lib/esm/SolidModelingTools.js +30 -29
  87. package/lib/esm/SolidModelingTools.js.map +1 -1
  88. package/lib/esm/SolidPrimitiveTools.d.ts +69 -5
  89. package/lib/esm/SolidPrimitiveTools.d.ts.map +1 -1
  90. package/lib/esm/SolidPrimitiveTools.js +289 -8
  91. package/lib/esm/SolidPrimitiveTools.js.map +1 -1
  92. package/lib/esm/TransformElementsTool.d.ts +3 -5
  93. package/lib/esm/TransformElementsTool.d.ts.map +1 -1
  94. package/lib/esm/TransformElementsTool.js +10 -12
  95. package/lib/esm/TransformElementsTool.js.map +1 -1
  96. package/lib/esm/UndoRedoTool.js +1 -1
  97. package/lib/esm/UndoRedoTool.js.map +1 -1
  98. package/lib/esm/editor-frontend.d.ts +5 -3
  99. package/lib/esm/editor-frontend.d.ts.map +1 -1
  100. package/lib/esm/editor-frontend.js +5 -3
  101. package/lib/esm/editor-frontend.js.map +1 -1
  102. package/lib/public/locales/en/Editor.json +42 -0
  103. package/package.json +15 -15
@@ -2,14 +2,15 @@
2
2
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
- import { AccuDrawHintBuilder, ElementSetTool, IModelApp, LengthDescription, NotifyMessageDetails, OutputMessagePriority } from "@itwin/core-frontend";
6
- import { BentleyError, Code, ElementGeometry, ElementGeometryOpcode, FeatureAppearance } from "@itwin/core-common";
7
- import { EditTools } from "./EditTool";
8
- import { AngleSweep, Arc3d, AxisOrder, CurvePrimitive, FrameBuilder, Geometry, JointOptions, LineSegment3d, LineString3d, Loop, Matrix3d, Path, RegionOps, Vector3d } from "@itwin/core-geometry";
9
- import { editorBuiltInCmdIds } from "@itwin/editor-common";
10
- import { computeChordToleranceFromPoint, DynamicGraphicsProvider } from "./CreateElementTool";
11
5
  import { DialogProperty, PropertyDescriptionHelper } from "@itwin/appui-abstract";
12
- import { Id64 } from "@itwin/core-bentley";
6
+ import { CompressedId64Set } from "@itwin/core-bentley";
7
+ import { BentleyError, Code, ElementGeometry, ElementGeometryOpcode, } from "@itwin/core-common";
8
+ import { AccuDrawHintBuilder, IModelApp, LengthDescription, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
9
+ import { AngleSweep, Arc3d, AxisOrder, CurveChainWithDistanceIndex, CurveCollection, CurvePrimitive, FrameBuilder, Geometry, JointOptions, LineSegment3d, LineString3d, Loop, Matrix3d, Path, Plane3dByOriginAndUnitNormal, RegionBinaryOpType, RegionOps, UnionRegion, Vector3d, } from "@itwin/core-geometry";
10
+ import { editorBuiltInCmdIds } from "@itwin/editor-common";
11
+ import { EditTools } from "./EditTool";
12
+ import { basicManipulationIpc } from "./EditToolIpc";
13
+ import { ModifyElementWithDynamicsTool } from "./ModifyElementTool";
13
14
  /** @alpha */
14
15
  export class CurveData {
15
16
  constructor(props, params, geom) {
@@ -18,79 +19,13 @@ export class CurveData {
18
19
  this.geom = geom;
19
20
  }
20
21
  }
21
- /** @alpha Base class for applying an offset to path and loops. */
22
- export class ModifyCurveTool extends ElementSetTool {
23
- constructor() {
24
- super(...arguments);
25
- this._checkedIds = new Map();
26
- this._firstResult = true;
27
- }
28
- allowView(_vp) { return true; }
29
- isCompatibleViewport(vp, isSelectedViewChange) { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && this.allowView(vp)); }
22
+ /** @alpha Base class for modifying all types of curve geometry. */
23
+ export class ModifyCurveTool extends ModifyElementWithDynamicsTool {
30
24
  async startCommand() {
31
25
  if (undefined !== this._startedCmd)
32
26
  return this._startedCmd;
33
27
  return EditTools.startCommand(editorBuiltInCmdIds.cmdBasicManipulation, this.iModel.key);
34
28
  }
35
- static callCommand(method, ...args) {
36
- return EditTools.callCommand(method, ...args);
37
- }
38
- agendaAppearance(isDynamics) {
39
- if (isDynamics) {
40
- if (undefined === this._agendaAppearanceDynamic)
41
- this._agendaAppearanceDynamic = FeatureAppearance.fromTransparency(0.0);
42
- return this._agendaAppearanceDynamic;
43
- }
44
- if (undefined === this._agendaAppearanceDefault)
45
- this._agendaAppearanceDefault = FeatureAppearance.fromTransparency(0.9);
46
- return this._agendaAppearanceDefault;
47
- }
48
- get wantAgendaAppearanceOverride() { return false; }
49
- addFeatureOverrides(overrides, _vp) {
50
- if (this.agenda.isEmpty)
51
- return;
52
- const appearance = this.agendaAppearance(false);
53
- this.agenda.elements.forEach((elementId) => overrides.override({ elementId, appearance }));
54
- }
55
- updateAgendaAppearanceProvider(drop) {
56
- if (!this.wantAgendaAppearanceOverride)
57
- return;
58
- for (const vp of IModelApp.viewManager) {
59
- if (!this.allowView(vp))
60
- continue;
61
- if (drop || this.agenda.isEmpty)
62
- vp.dropFeatureOverrideProvider(this);
63
- else if (!vp.addFeatureOverrideProvider(this))
64
- vp.setFeatureOverrideProviderChanged();
65
- }
66
- }
67
- clearGraphics() {
68
- if (undefined === this._graphicsProvider)
69
- return;
70
- this._graphicsProvider.cleanupGraphic();
71
- this._graphicsProvider = undefined;
72
- }
73
- async createGraphics(ev) {
74
- if (!IModelApp.viewManager.inDynamicsMode)
75
- return; // Don't need to create graphic if dynamics aren't yet active...
76
- const geometry = this.getGeometryProps(ev, false);
77
- if (undefined === geometry)
78
- return;
79
- const elemProps = this.getElementProps(ev);
80
- if (undefined === (elemProps === null || elemProps === void 0 ? void 0 : elemProps.placement))
81
- return;
82
- if (undefined === this._graphicsProvider) {
83
- if (this._firstResult) {
84
- this.updateAgendaAppearanceProvider();
85
- this._firstResult = false;
86
- }
87
- this._graphicsProvider = new DynamicGraphicsProvider(this.iModel, this.toolId);
88
- }
89
- // Set chord tolerance for non-linear curve primitives...
90
- if (ev.viewport)
91
- this._graphicsProvider.chordTolerance = computeChordToleranceFromPoint(ev.viewport, ev.point);
92
- await this._graphicsProvider.createGraphic(elemProps.category, elemProps.placement, geometry);
93
- }
94
29
  static isSingleCurve(info) {
95
30
  const it = new ElementGeometry.Iterator(info);
96
31
  it.requestWorldCoordinates();
@@ -108,13 +43,30 @@ export class ModifyCurveTool extends ElementSetTool {
108
43
  }
109
44
  return;
110
45
  }
46
+ static isInPlane(curve, plane) {
47
+ if ("curvePrimitive" === curve.geometryCategory)
48
+ return curve.isInPlane(plane);
49
+ if (!curve.children)
50
+ return false;
51
+ for (const child of curve.children) {
52
+ if (child instanceof CurvePrimitive) {
53
+ if (!child.isInPlane(plane))
54
+ return false;
55
+ }
56
+ else if (child instanceof CurveCollection) {
57
+ if (!this.isInPlane(child, plane))
58
+ return false;
59
+ }
60
+ }
61
+ return true;
62
+ }
111
63
  acceptCurve(_curve) { return true; }
112
64
  modifyCurve(_ev, _isAccept) { return undefined; }
113
65
  async getCurveData(id) {
114
66
  try {
115
67
  this._startedCmd = await this.startCommand();
116
68
  const reject = [ElementGeometryOpcode.Polyface, ElementGeometryOpcode.SolidPrimitive, ElementGeometryOpcode.BsplineSurface, ElementGeometryOpcode.BRep];
117
- const info = await ModifyCurveTool.callCommand("requestElementGeometry", id, { maxDisplayable: 1, reject, geometry: { curves: true, surfaces: true, solids: false } });
69
+ const info = await basicManipulationIpc.requestElementGeometry(id, { maxDisplayable: 1, reject, geometry: { curves: true, surfaces: true, solids: false } });
118
70
  if (undefined === info)
119
71
  return undefined;
120
72
  const data = ModifyCurveTool.isSingleCurve(info);
@@ -132,23 +84,8 @@ export class ModifyCurveTool extends ElementSetTool {
132
84
  return undefined;
133
85
  }
134
86
  }
135
- onGeometryFilterChanged() { this._checkedIds.clear(); }
136
- async acceptElementForOperation(id) {
137
- if (Id64.isInvalid(id) || Id64.isTransient(id))
138
- return false;
139
- let accept = this._checkedIds.get(id);
140
- if (undefined === accept) {
141
- if (this.agenda.isEmpty && this._checkedIds.size > 1000)
142
- this._checkedIds.clear(); // Limit auto-locate cache size to something reasonable...
143
- accept = (undefined !== await this.getCurveData(id));
144
- this._checkedIds.set(id, accept);
145
- }
146
- return accept;
147
- }
148
- async isElementValidForOperation(hit, out) {
149
- if (!await super.isElementValidForOperation(hit, out))
150
- return false;
151
- return this.acceptElementForOperation(hit.sourceId);
87
+ async doAcceptElementForOperation(id) {
88
+ return (undefined !== await this.getCurveData(id));
152
89
  }
153
90
  async onAgendaModified() {
154
91
  this.curveData = undefined;
@@ -157,33 +94,22 @@ export class ModifyCurveTool extends ElementSetTool {
157
94
  const id = this.agenda.elements[this.agenda.length - 1];
158
95
  this.curveData = await this.getCurveData(id);
159
96
  }
160
- onDynamicFrame(_ev, context) {
161
- if (undefined !== this._graphicsProvider)
162
- this._graphicsProvider.addGraphic(context);
163
- }
164
- async onMouseMotion(ev) {
165
- return this.createGraphics(ev);
166
- }
167
97
  setupAccuDraw() {
168
98
  const hints = new AccuDrawHintBuilder();
169
99
  hints.enableSmartRotation = true;
170
100
  hints.sendHints(false);
171
101
  }
172
- setupAndPromptForNextAction() {
173
- this.setupAccuDraw();
174
- super.setupAndPromptForNextAction();
175
- }
176
102
  getGeometryProps(ev, isAccept) {
177
103
  if (undefined === this.curveData)
178
104
  return;
179
- const offset = this.modifyCurve(ev, isAccept);
180
- if (undefined === offset)
105
+ const geom = this.modifyCurve(ev, isAccept);
106
+ if (undefined === geom)
181
107
  return;
182
108
  const builder = new ElementGeometry.Builder();
183
109
  builder.setLocalToWorldFromPlacement(this.curveData.props.placement);
184
110
  if (!builder.appendGeometryParamsChange(this.curveData.params))
185
111
  return;
186
- if (!builder.appendGeometryQuery(offset))
112
+ if (!builder.appendGeometryQuery(geom))
187
113
  return;
188
114
  return { format: "flatbuffer", data: builder.entries };
189
115
  }
@@ -198,29 +124,19 @@ export class ModifyCurveTool extends ElementSetTool {
198
124
  }
199
125
  return this.curveData.props;
200
126
  }
201
- async applyAgendaOperation(ev) {
202
- const geometry = this.getGeometryProps(ev, true);
203
- if (undefined === geometry)
204
- return false;
205
- const elemProps = this.getElementProps(ev);
206
- if (undefined === elemProps)
207
- return false;
208
- if ("flatbuffer" === geometry.format)
209
- elemProps.elementGeometryBuilderParams = { entryArray: geometry.data };
210
- else
211
- elemProps.geom = geometry.data;
127
+ async doUpdateElement(elemProps) {
212
128
  try {
213
129
  this._startedCmd = await this.startCommand();
214
130
  if (undefined === elemProps.id) {
215
131
  const repeatOperation = this.wantContinueWithPreviousResult;
216
132
  if (repeatOperation)
217
133
  this.agenda.clear();
218
- const newId = await ModifyCurveTool.callCommand("insertGeometricElement", elemProps);
134
+ const newId = await basicManipulationIpc.insertGeometricElement(elemProps);
219
135
  if (repeatOperation && this.agenda.add(newId))
220
136
  await this.onAgendaModified();
221
137
  }
222
138
  else {
223
- await ModifyCurveTool.callCommand("updateGeometricElement", elemProps);
139
+ await basicManipulationIpc.updateGeometricElement(elemProps);
224
140
  }
225
141
  return true;
226
142
  }
@@ -229,10 +145,6 @@ export class ModifyCurveTool extends ElementSetTool {
229
145
  return false;
230
146
  }
231
147
  }
232
- async processAgenda(ev) {
233
- if (await this.applyAgendaOperation(ev))
234
- return this.saveChanges();
235
- }
236
148
  get wantModifyOriginal() { return true; }
237
149
  get wantContinueWithPreviousResult() { return false; }
238
150
  async onProcessComplete() {
@@ -241,31 +153,9 @@ export class ModifyCurveTool extends ElementSetTool {
241
153
  return;
242
154
  return super.onProcessComplete();
243
155
  }
244
- async onUnsuspend() {
245
- if (!this._firstResult)
246
- this.updateAgendaAppearanceProvider();
247
- return super.onUnsuspend();
248
- }
249
- async onSuspend() {
250
- if (!this._firstResult)
251
- this.updateAgendaAppearanceProvider(true);
252
- return super.onSuspend();
253
- }
254
- async onPostInstall() {
255
- await super.onPostInstall();
256
- if (this.wantAgendaAppearanceOverride)
257
- this.agenda.manageHiliteState = false;
258
- }
259
- async onCleanup() {
260
- this.clearGraphics();
261
- this.updateAgendaAppearanceProvider(true);
262
- return super.onCleanup();
263
- }
264
156
  }
265
157
  /** @alpha Tool for applying an offset to paths and loops. */
266
158
  export class OffsetCurveTool extends ModifyCurveTool {
267
- get wantAccuSnap() { return true; }
268
- get wantDynamics() { return true; }
269
159
  get useDistanceProperty() {
270
160
  if (!this._useDistanceProperty)
271
161
  this._useDistanceProperty = new DialogProperty(PropertyDescriptionHelper.buildLockPropertyDescription("useOffsetDistance"), false);
@@ -306,13 +196,7 @@ export class OffsetCurveTool extends ModifyCurveTool {
306
196
  acceptCurve(curve) {
307
197
  if ("curvePrimitive" === curve.geometryCategory)
308
198
  return true;
309
- switch (curve.curveCollectionType) {
310
- case "path":
311
- case "loop":
312
- return true;
313
- default:
314
- return false;
315
- }
199
+ return (curve.isOpenPath || curve.isClosedPath);
316
200
  }
317
201
  modifyCurve(ev, isAccept) {
318
202
  var _a;
@@ -429,18 +313,12 @@ export class BreakCurveTool extends ModifyCurveTool {
429
313
  super(...arguments);
430
314
  this.modifyOriginal = true;
431
315
  }
432
- get wantAccuSnap() { return true; }
316
+ get wantDynamics() { return false; }
433
317
  get wantModifyOriginal() { return this.modifyOriginal; }
434
318
  acceptCurve(curve) {
435
319
  if ("curvePrimitive" === curve.geometryCategory)
436
320
  return true;
437
- switch (curve.curveCollectionType) {
438
- case "path":
439
- case "loop":
440
- return true;
441
- default:
442
- return false;
443
- }
321
+ return (curve.isOpenPath || curve.isClosedPath);
444
322
  }
445
323
  doBreakCurve(ev) {
446
324
  var _a;
@@ -556,34 +434,16 @@ export class BreakCurveTool extends ModifyCurveTool {
556
434
  }
557
435
  BreakCurveTool.toolId = "BreakCurve";
558
436
  BreakCurveTool.iconSpec = "icon-scale"; // Need better icon...
559
- /** @alpha Tool for extending/shortening curves. */
437
+ /** @alpha Tool to extend or trim a path or open curve */
560
438
  export class ExtendCurveTool extends ModifyCurveTool {
561
- get wantAccuSnap() { return true; }
562
- get wantDynamics() { return true; }
563
439
  get wantAgendaAppearanceOverride() { return true; }
564
440
  acceptCurve(curve) {
565
- if ("curvePrimitive" !== curve.geometryCategory)
566
- return false;
567
- return curve.isExtensibleFractionSpace;
441
+ if ("curvePrimitive" === curve.geometryCategory)
442
+ return curve.isExtensibleFractionSpace;
443
+ return curve.isOpenPath;
568
444
  }
569
- modifyCurve(ev, _isAccept) {
570
- var _a;
571
- if (undefined === ev.viewport || undefined === this.anchorPoint)
572
- return undefined;
573
- const geom = (_a = this.curveData) === null || _a === void 0 ? void 0 : _a.geom;
574
- if (undefined === geom)
575
- return undefined;
576
- const matrix = AccuDrawHintBuilder.getCurrentRotation(ev.viewport, true, true);
577
- const localToWorld = FrameBuilder.createRightHandedFrame(matrix === null || matrix === void 0 ? void 0 : matrix.getColumn(2), geom);
578
- if (undefined === localToWorld)
579
- return undefined;
580
- const worldToLocal = localToWorld.inverse();
581
- if (undefined === worldToLocal)
582
- return undefined;
583
- const spacePoint = AccuDrawHintBuilder.projectPointToPlaneInView(ev.point, localToWorld.getOrigin(), localToWorld.matrix.getColumn(2), ev.viewport);
584
- if (undefined === spacePoint)
585
- return undefined;
586
- const pickDetail = geom.closestPoint(this.anchorPoint, false);
445
+ extendCurve(geom, pickPoint, spacePoint) {
446
+ const pickDetail = geom.closestPoint(pickPoint, false);
587
447
  if (undefined === (pickDetail === null || pickDetail === void 0 ? void 0 : pickDetail.curve))
588
448
  return undefined;
589
449
  const closeDetail = geom.closestPoint(spacePoint, true);
@@ -606,41 +466,126 @@ export class ExtendCurveTool extends ModifyCurveTool {
606
466
  return fullArc;
607
467
  }
608
468
  }
609
- return closeDetail.curve.clonePartialCurve(pickDetail.fraction > 0.5 ? 0.0 : 1.0, closeDetail.fraction);
469
+ return geom.clonePartialCurve(pickDetail.fraction > 0.5 ? 0.0 : 1.0, closeDetail.fraction);
610
470
  }
611
- setupAccuDraw() {
471
+ extendPathEnd(geom, closeDetail, isStart) {
472
+ if (undefined === closeDetail.curve)
473
+ return undefined;
474
+ const curve = closeDetail.curve.clonePartialCurve(isStart ? closeDetail.fraction : 0.0, isStart ? 1.0 : closeDetail.fraction);
475
+ if (undefined === curve)
476
+ return undefined;
477
+ if (curve instanceof Arc3d && closeDetail.curve instanceof Arc3d && (curve.sweep.isCCW !== closeDetail.curve.sweep.isCCW))
478
+ curve.sweep.cloneComplement(true, curve.sweep); // Preserve current sweep direction...
479
+ const result = geom.clone();
480
+ result.children[isStart ? 0 : geom.children.length - 1] = curve;
481
+ return result;
482
+ }
483
+ extendPath(geom, pickPoint, spacePoint) {
612
484
  var _a;
485
+ if (geom.children.length < 2)
486
+ return this.extendCurve(geom.children[0], pickPoint, spacePoint);
487
+ const pathAsPrimitive = CurveChainWithDistanceIndex.createCapture(geom);
488
+ const closeDetail = pathAsPrimitive.closestPoint(spacePoint, true);
489
+ if (undefined === (closeDetail === null || closeDetail === void 0 ? void 0 : closeDetail.curve) || undefined === ((_a = closeDetail.childDetail) === null || _a === void 0 ? void 0 : _a.curve))
490
+ return undefined;
491
+ if (undefined !== this.modifyingEnd) {
492
+ if (closeDetail.childDetail.curve === this.modifyingEnd) {
493
+ this.modifyingEnd = undefined; // Ok to unlock extending first/last curve in path...
494
+ }
495
+ else {
496
+ const pathEndDetail = this.modifyingEnd.closestPoint(spacePoint, true);
497
+ if (undefined === (pathEndDetail === null || pathEndDetail === void 0 ? void 0 : pathEndDetail.curve))
498
+ return undefined;
499
+ return this.extendPathEnd(geom, pathEndDetail, (pathEndDetail.curve === geom.children[0]));
500
+ }
501
+ }
502
+ // NOTE: Special case extend instead of using CurveChainWithDistanceIndex.clonePartialCurve...
503
+ if (closeDetail.fraction < 0.0) {
504
+ this.modifyingEnd = closeDetail.childDetail.curve;
505
+ return this.extendPathEnd(geom, closeDetail.childDetail, true);
506
+ }
507
+ else if (closeDetail.fraction > 1.0) {
508
+ this.modifyingEnd = closeDetail.childDetail.curve;
509
+ return this.extendPathEnd(geom, closeDetail.childDetail, false);
510
+ }
511
+ const pickDetail = pathAsPrimitive.closestPoint(pickPoint, false);
512
+ if (undefined === (pickDetail === null || pickDetail === void 0 ? void 0 : pickDetail.curve))
513
+ return undefined;
514
+ const result = pathAsPrimitive.clonePartialCurve(pickDetail.fraction > 0.5 ? 0.0 : 1.0, closeDetail.fraction);
515
+ if (undefined === result)
516
+ return undefined;
517
+ return Path.create(...result.path.children);
518
+ }
519
+ modifyCurve(ev, _isAccept) {
520
+ var _a;
521
+ if (undefined === ev.viewport || undefined === this.anchorPoint)
522
+ return undefined;
523
+ const geom = (_a = this.curveData) === null || _a === void 0 ? void 0 : _a.geom;
524
+ if (undefined === geom)
525
+ return undefined;
526
+ const matrix = AccuDrawHintBuilder.getCurrentRotation(ev.viewport, true, true);
527
+ const localToWorld = FrameBuilder.createRightHandedFrame(matrix === null || matrix === void 0 ? void 0 : matrix.getColumn(2), geom);
528
+ if (undefined === localToWorld)
529
+ return undefined;
530
+ const worldToLocal = localToWorld.inverse();
531
+ if (undefined === worldToLocal)
532
+ return undefined;
533
+ const spacePoint = AccuDrawHintBuilder.projectPointToPlaneInView(ev.point, localToWorld.getOrigin(), localToWorld.matrix.getColumn(2), ev.viewport);
534
+ if (undefined === spacePoint)
535
+ return undefined;
536
+ if (geom instanceof CurvePrimitive)
537
+ return this.extendCurve(geom, this.anchorPoint, spacePoint);
538
+ else if (geom instanceof Path)
539
+ return this.extendPath(geom, this.anchorPoint, spacePoint);
540
+ return undefined;
541
+ }
542
+ async onAgendaModified() {
543
+ IModelApp.accuSnap.neverFlash(this.agenda.elements); // Don't flash snapped segment for better preview when trimming curve/path...
544
+ return super.onAgendaModified();
545
+ }
546
+ setupAccuDraw() {
547
+ var _a, _b, _c;
613
548
  const hints = new AccuDrawHintBuilder();
614
549
  if (this.agenda.isEmpty) {
615
550
  hints.enableSmartRotation = true;
616
551
  }
617
552
  else {
618
553
  const geom = (_a = this.curveData) === null || _a === void 0 ? void 0 : _a.geom;
619
- if (undefined === geom || "curvePrimitive" !== geom.geometryCategory)
554
+ if (undefined === geom || undefined === this.anchorPoint)
555
+ return;
556
+ let pickDetail;
557
+ if (geom instanceof CurvePrimitive)
558
+ pickDetail = geom.closestPoint(this.anchorPoint, false);
559
+ else if (geom instanceof Path)
560
+ pickDetail = CurveChainWithDistanceIndex.createCapture(geom).closestPoint(this.anchorPoint, false);
561
+ if (undefined === (pickDetail === null || pickDetail === void 0 ? void 0 : pickDetail.curve))
620
562
  return;
621
- if (geom instanceof Arc3d) {
622
- const matrix = geom.matrixClone();
563
+ const curve = (undefined !== ((_b = pickDetail.childDetail) === null || _b === void 0 ? void 0 : _b.curve) ? (_c = pickDetail.childDetail) === null || _c === void 0 ? void 0 : _c.curve : pickDetail.curve);
564
+ if (curve instanceof Arc3d) {
565
+ const matrix = curve.matrixClone();
623
566
  matrix.normalizeColumnsInPlace();
624
- hints.setOrigin(geom.center);
567
+ hints.setOrigin(curve.center);
625
568
  hints.setMatrix(matrix);
626
569
  hints.setModePolar();
627
570
  }
628
- else if (geom instanceof LineSegment3d) {
629
- const pickDetail = (undefined !== this.anchorPoint ? geom.closestPoint(this.anchorPoint, false) : undefined);
630
- if (undefined !== (pickDetail === null || pickDetail === void 0 ? void 0 : pickDetail.curve)) {
631
- hints.setOrigin(pickDetail.fraction > 0.5 ? geom.point0Ref : geom.point1Ref);
632
- hints.setXAxis(Vector3d.createStartEnd(pickDetail.fraction > 0.5 ? geom.point0Ref : geom.point1Ref, pickDetail.fraction > 0.5 ? geom.point1Ref : geom.point0Ref));
633
- }
571
+ else if (curve instanceof LineSegment3d) {
572
+ hints.setOrigin(pickDetail.fraction > 0.5 ? curve.point0Ref : curve.point1Ref);
573
+ hints.setXAxis(Vector3d.createStartEnd(pickDetail.fraction > 0.5 ? curve.point0Ref : curve.point1Ref, pickDetail.fraction > 0.5 ? curve.point1Ref : curve.point0Ref));
634
574
  hints.setModeRectangular();
635
575
  }
636
- else if (geom instanceof LineString3d) {
637
- const pickDetail = (undefined !== this.anchorPoint ? geom.closestPoint(this.anchorPoint, false) : undefined);
638
- if (undefined !== (pickDetail === null || pickDetail === void 0 ? void 0 : pickDetail.curve) && geom.numPoints() > 1) {
639
- hints.setOrigin(geom.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? geom.numPoints() - 2 : 1));
640
- hints.setXAxis(Vector3d.createStartEnd(geom.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? geom.numPoints() - 2 : 1), geom.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? geom.numPoints() - 1 : 0)));
576
+ else if (curve instanceof LineString3d) {
577
+ if (curve.numPoints() > 1) {
578
+ hints.setOrigin(curve.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? curve.numPoints() - 2 : 1));
579
+ hints.setXAxis(Vector3d.createStartEnd(curve.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? curve.numPoints() - 2 : 1), curve.packedPoints.getPoint3dAtUncheckedPointIndex(pickDetail.fraction > 0.5 ? curve.numPoints() - 1 : 0)));
641
580
  }
642
581
  hints.setModeRectangular();
643
582
  }
583
+ else {
584
+ const ray = curve.fractionToPointAndUnitTangent(pickDetail.fraction > 0.5 ? 0.0 : 1.0);
585
+ hints.setOrigin(ray.origin);
586
+ hints.setXAxis(ray.direction);
587
+ hints.setModeRectangular();
588
+ }
644
589
  }
645
590
  hints.sendHints(false);
646
591
  }
@@ -658,4 +603,174 @@ export class ExtendCurveTool extends ModifyCurveTool {
658
603
  }
659
604
  ExtendCurveTool.toolId = "ExtendCurve";
660
605
  ExtendCurveTool.iconSpec = "icon-scale"; // Need better icon...
606
+ /** @alpha */
607
+ export var RegionBooleanMode;
608
+ (function (RegionBooleanMode) {
609
+ /** Create region from union of all input regions */
610
+ RegionBooleanMode[RegionBooleanMode["Unite"] = 0] = "Unite";
611
+ /** Create region from subtraction from the first input region */
612
+ RegionBooleanMode[RegionBooleanMode["Subtract"] = 1] = "Subtract";
613
+ /** Create region from intersection of all input regions */
614
+ RegionBooleanMode[RegionBooleanMode["Intersect"] = 2] = "Intersect";
615
+ })(RegionBooleanMode || (RegionBooleanMode = {}));
616
+ /** @alpha Tool to unite, subtract, or intersect planar regions. */
617
+ export class RegionBooleanTool extends ModifyCurveTool {
618
+ get makeCopyProperty() {
619
+ if (!this._makeCopyProperty)
620
+ this._makeCopyProperty = new DialogProperty(PropertyDescriptionHelper.buildToggleDescription("regionBooleanKeep", EditTools.translate("RegionBoolean.Label.KeepOriginal")), false);
621
+ return this._makeCopyProperty;
622
+ }
623
+ get makeCopy() { return this.makeCopyProperty.value; }
624
+ set makeCopy(value) { this.makeCopyProperty.value = value; }
625
+ static modeMessage(str) { return EditTools.translate(`RegionBoolean.Mode.${str}`); }
626
+ get modeProperty() {
627
+ if (!this._modeProperty)
628
+ this._modeProperty = new DialogProperty(PropertyDescriptionHelper.buildEnumPicklistEditorDescription("regionBooleanMode", EditTools.translate("RegionBoolean.Label.Mode"), RegionBooleanTool.getModeChoices()), RegionBooleanMode.Unite);
629
+ return this._modeProperty;
630
+ }
631
+ get mode() { return this.modeProperty.value; }
632
+ set mode(mode) { this.modeProperty.value = mode; }
633
+ get clearSelectionSet() { return false; } // Don't clear for subtract so that mode can be changed...
634
+ get allowSelectionSet() { return RegionBooleanMode.Subtract !== this.mode; }
635
+ get allowDragSelect() { return RegionBooleanMode.Subtract !== this.mode; }
636
+ get controlKeyContinuesSelection() { return true; }
637
+ get requiredElementCount() { return 2; }
638
+ get wantAccuSnap() { return false; }
639
+ get wantDynamics() { return false; }
640
+ get wantModifyOriginal() { return !this.makeCopy; }
641
+ async onAgendaModified() { } // No intermediate result preview, defer to processAgenda...
642
+ acceptCurve(curve) {
643
+ if ("curvePrimitive" === curve.geometryCategory)
644
+ return false;
645
+ return curve.isAnyRegionType;
646
+ }
647
+ regionBinaryOp() {
648
+ switch (this.mode) {
649
+ case RegionBooleanMode.Subtract:
650
+ return RegionBinaryOpType.AMinusB;
651
+ case RegionBooleanMode.Intersect:
652
+ return RegionBinaryOpType.Intersection;
653
+ default:
654
+ return RegionBinaryOpType.Union;
655
+ }
656
+ }
657
+ regionFromSignedLoops(loops) {
658
+ switch (loops.negativeAreaLoops.length) {
659
+ case 0:
660
+ return undefined;
661
+ case 1:
662
+ return loops.negativeAreaLoops[0];
663
+ default:
664
+ return RegionOps.sortOuterAndHoleLoopsXY(loops.negativeAreaLoops);
665
+ }
666
+ }
667
+ regionBooleanXY(tools, op) {
668
+ if (tools.length < 2)
669
+ return undefined;
670
+ const loopsA = (RegionBinaryOpType.Union !== op ? tools[0] : tools);
671
+ const loopsB = (RegionBinaryOpType.Union !== op ? tools.slice(1) : undefined);
672
+ // TODO: Need to be able to specify group operation for loopsB to correctly support intersect w/o doing 2 at time...
673
+ const areas = RegionOps.regionBooleanXY(loopsA, loopsB, op);
674
+ if (undefined === areas)
675
+ return undefined;
676
+ // TODO: Holes are expected to be returned as negative area loops but currently are not...
677
+ const loops = RegionOps.constructAllXYRegionLoops(areas);
678
+ if (1 === loops.length)
679
+ return this.regionFromSignedLoops(loops[0]);
680
+ if (loops.length > 1) {
681
+ const unionRegion = UnionRegion.create();
682
+ for (const loop of loops) {
683
+ const child = this.regionFromSignedLoops(loop);
684
+ if (undefined === child)
685
+ continue;
686
+ unionRegion.tryAddChild(child);
687
+ }
688
+ if (unionRegion.children.length > 1)
689
+ return unionRegion;
690
+ }
691
+ return undefined;
692
+ }
693
+ async doRegionBoolean(_ev) {
694
+ this.curveData = undefined;
695
+ if (this.agenda.length < this.requiredElementCount)
696
+ return;
697
+ const targetData = await this.getCurveData(this.agenda.elements[0]);
698
+ if (undefined === (targetData === null || targetData === void 0 ? void 0 : targetData.geom))
699
+ return;
700
+ const targetLocalToWorld = FrameBuilder.createRightHandedFrame(undefined, targetData.geom);
701
+ if (undefined === targetLocalToWorld)
702
+ return;
703
+ const targetWorldToLocal = targetLocalToWorld.inverse();
704
+ if (undefined === targetWorldToLocal)
705
+ return;
706
+ const targetPlane = Plane3dByOriginAndUnitNormal.create(targetLocalToWorld.getOrigin(), targetLocalToWorld.matrix.getColumn(2));
707
+ if (undefined === targetPlane)
708
+ return;
709
+ if (!targetData.geom.tryTransformInPlace(targetWorldToLocal))
710
+ return;
711
+ const tools = [targetData.geom];
712
+ for (const id of this.agenda.elements) {
713
+ if (id === targetData.props.id)
714
+ continue;
715
+ const curveData = await this.getCurveData(id);
716
+ if (undefined === (curveData === null || curveData === void 0 ? void 0 : curveData.geom))
717
+ return;
718
+ if (!ModifyCurveTool.isInPlane(curveData.geom, targetPlane)) {
719
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, EditTools.translate("RegionBoolean.Error.NonCoplanar")));
720
+ return;
721
+ }
722
+ if (!curveData.geom.tryTransformInPlace(targetWorldToLocal))
723
+ return;
724
+ tools.push(curveData.geom);
725
+ }
726
+ const result = this.regionBooleanXY(tools, this.regionBinaryOp());
727
+ if (undefined === result || !result.tryTransformInPlace(targetLocalToWorld))
728
+ return;
729
+ this.curveData = targetData;
730
+ this.curveData.geom = result;
731
+ }
732
+ modifyCurve(_ev, _isAccept) {
733
+ var _a;
734
+ return (_a = this.curveData) === null || _a === void 0 ? void 0 : _a.geom;
735
+ }
736
+ async applyAgendaOperation(ev) {
737
+ if (!await super.applyAgendaOperation(ev))
738
+ return false;
739
+ if (this.wantModifyOriginal && this.agenda.length > 1)
740
+ await basicManipulationIpc.deleteElements(CompressedId64Set.sortAndCompress(this.agenda.elements.slice(1)));
741
+ return true;
742
+ }
743
+ async processAgenda(ev) {
744
+ await this.doRegionBoolean(ev);
745
+ return super.processAgenda(ev);
746
+ }
747
+ async onRestartTool() {
748
+ const tool = new RegionBooleanTool();
749
+ if (!await tool.run())
750
+ return this.exitTool();
751
+ }
752
+ async applyToolSettingPropertyChange(updatedValue) {
753
+ if (!this.changeToolSettingPropertyValue(updatedValue))
754
+ return false;
755
+ if (this.modeProperty.name === updatedValue.propertyName)
756
+ await this.onReinitialize();
757
+ return true;
758
+ }
759
+ supplyToolSettingsProperties() {
760
+ this.initializeToolSettingPropertyValues([this.makeCopyProperty, this.modeProperty]);
761
+ const toolSettings = new Array();
762
+ toolSettings.push(this.modeProperty.toDialogItem({ rowPriority: 1, columnIndex: 0 }));
763
+ toolSettings.push(this.makeCopyProperty.toDialogItem({ rowPriority: 2, columnIndex: 0 }));
764
+ return toolSettings;
765
+ }
766
+ }
767
+ RegionBooleanTool.toolId = "RegionBoolean";
768
+ RegionBooleanTool.iconSpec = "icon-scale"; // Need better icon...
769
+ RegionBooleanTool.getModeChoices = () => {
770
+ return [
771
+ { label: RegionBooleanTool.modeMessage("Unite"), value: RegionBooleanMode.Unite },
772
+ { label: RegionBooleanTool.modeMessage("Subtract"), value: RegionBooleanMode.Subtract },
773
+ { label: RegionBooleanTool.modeMessage("Intersect"), value: RegionBooleanMode.Intersect },
774
+ ];
775
+ };
661
776
  //# sourceMappingURL=ModifyCurveTools.js.map