@itwin/core-markup 4.0.0-dev.6 → 4.0.0-dev.60

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 (45) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/lib/cjs/Markup.d.ts +310 -310
  3. package/lib/cjs/Markup.js +419 -420
  4. package/lib/cjs/Markup.js.map +1 -1
  5. package/lib/cjs/MarkupTool.d.ts +38 -38
  6. package/lib/cjs/MarkupTool.js +88 -88
  7. package/lib/cjs/MarkupTool.js.map +1 -1
  8. package/lib/cjs/RedlineTool.d.ts +145 -145
  9. package/lib/cjs/RedlineTool.js +512 -512
  10. package/lib/cjs/RedlineTool.js.map +1 -1
  11. package/lib/cjs/SelectTool.d.ts +126 -126
  12. package/lib/cjs/SelectTool.js +741 -741
  13. package/lib/cjs/SelectTool.js.map +1 -1
  14. package/lib/cjs/SvgJsExt.d.ts +85 -85
  15. package/lib/cjs/SvgJsExt.js +185 -185
  16. package/lib/cjs/TextEdit.d.ts +43 -43
  17. package/lib/cjs/TextEdit.js +196 -196
  18. package/lib/cjs/TextEdit.js.map +1 -1
  19. package/lib/cjs/Undo.d.ts +46 -46
  20. package/lib/cjs/Undo.js +168 -168
  21. package/lib/cjs/core-markup.d.ts +18 -18
  22. package/lib/cjs/core-markup.js +38 -34
  23. package/lib/cjs/core-markup.js.map +1 -1
  24. package/lib/esm/Markup.d.ts +310 -310
  25. package/lib/esm/Markup.js +415 -415
  26. package/lib/esm/Markup.js.map +1 -1
  27. package/lib/esm/MarkupTool.d.ts +38 -38
  28. package/lib/esm/MarkupTool.js +85 -84
  29. package/lib/esm/MarkupTool.js.map +1 -1
  30. package/lib/esm/RedlineTool.d.ts +145 -145
  31. package/lib/esm/RedlineTool.js +508 -498
  32. package/lib/esm/RedlineTool.js.map +1 -1
  33. package/lib/esm/SelectTool.d.ts +126 -126
  34. package/lib/esm/SelectTool.js +735 -734
  35. package/lib/esm/SelectTool.js.map +1 -1
  36. package/lib/esm/SvgJsExt.d.ts +85 -85
  37. package/lib/esm/SvgJsExt.js +180 -180
  38. package/lib/esm/TextEdit.d.ts +43 -43
  39. package/lib/esm/TextEdit.js +193 -191
  40. package/lib/esm/TextEdit.js.map +1 -1
  41. package/lib/esm/Undo.d.ts +46 -46
  42. package/lib/esm/Undo.js +164 -164
  43. package/lib/esm/core-markup.d.ts +18 -18
  44. package/lib/esm/core-markup.js +22 -22
  45. package/package.json +15 -15
@@ -1,499 +1,509 @@
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 MarkupTools
7
- */
8
- // cspell:ignore rtmp stmp
9
- import { Point3d, Vector3d } from "@itwin/core-geometry";
10
- import { CoordinateLockOverrides, CoreTools, EventHandled, IModelApp, QuantityType, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
11
- import { SVG } from "@svgdotjs/svg.js";
12
- import { MarkupApp } from "./Markup";
13
- import { MarkupTool } from "./MarkupTool";
14
- /** Base class for tools that place new Markup elements
15
- * @public
16
- */
17
- export class RedlineTool extends MarkupTool {
18
- constructor() {
19
- super(...arguments);
20
- this._minPoints = 1;
21
- this._nRequiredPoints = 2;
22
- this._points = [];
23
- }
24
- onAdded(el) {
25
- const markup = this.markup;
26
- const undo = markup.undo;
27
- undo.performOperation(this.keyin, () => undo.onAdded(el));
28
- markup.selected.restart(el);
29
- }
30
- isComplete(_ev) { return this._points.length >= this._nRequiredPoints; }
31
- setupAndPromptForNextAction() {
32
- super.setupAndPromptForNextAction();
33
- this.markup.disablePick();
34
- IModelApp.toolAdmin.setCursor(0 === this._points.length ? IModelApp.viewManager.crossHairCursor : IModelApp.viewManager.dynamicsCursor);
35
- }
36
- createMarkup(_svgMarkup, _ev, _isDynamics) { }
37
- clearDynamicsMarkup(_isDynamics) { this.markup.svgDynamics.clear(); }
38
- async onRestartTool() { return this.exitTool(); } // Default to single shot and return control to select tool...
39
- async onCleanup() { this.clearDynamicsMarkup(false); }
40
- async onReinitialize() {
41
- this.clearDynamicsMarkup(false);
42
- return super.onReinitialize();
43
- }
44
- async onUndoPreviousStep() {
45
- return (0 === this._points.length) ? false : (this.onReinitialize(), true);
46
- }
47
- async onMouseMotion(ev) {
48
- if (undefined === ev.viewport || this._points.length < this._minPoints)
49
- return;
50
- this.clearDynamicsMarkup(true);
51
- this.createMarkup(this.markup.svgDynamics, ev, true);
52
- }
53
- async onDataButtonDown(ev) {
54
- if (undefined === ev.viewport)
55
- return EventHandled.No;
56
- this._points.push(MarkupApp.convertVpToVb(ev.viewPoint));
57
- if (!this.isComplete(ev)) {
58
- this.setupAndPromptForNextAction();
59
- return EventHandled.No;
60
- }
61
- this.createMarkup(this.markup.svgMarkup, ev, false);
62
- await this.onReinitialize();
63
- return EventHandled.No;
64
- }
65
- async onResetButtonUp(_ev) {
66
- await this.onReinitialize();
67
- return EventHandled.No;
68
- }
69
- provideToolAssistance(mainInstrKey, singlePoint = false) {
70
- const mainInstruction = ToolAssistance.createInstruction(this.iconSpec, IModelApp.localization.getLocalizedString(mainInstrKey));
71
- const mouseInstructions = [];
72
- const touchInstructions = [];
73
- const acceptMsg = CoreTools.translate("ElementSet.Inputs.AcceptPoint");
74
- const rejectMsg = CoreTools.translate("ElementSet.Inputs.Exit");
75
- if (!ToolAssistance.createTouchCursorInstructions(touchInstructions))
76
- touchInstructions.push(ToolAssistance.createInstruction(singlePoint ? ToolAssistanceImage.OneTouchTap : ToolAssistanceImage.OneTouchDrag, acceptMsg, false, ToolAssistanceInputMethod.Touch));
77
- mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, acceptMsg, false, ToolAssistanceInputMethod.Mouse));
78
- touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.TwoTouchTap, rejectMsg, false, ToolAssistanceInputMethod.Touch));
79
- mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.RightClick, rejectMsg, false, ToolAssistanceInputMethod.Mouse));
80
- const sections = [];
81
- sections.push(ToolAssistance.createSection(mouseInstructions, ToolAssistance.inputsLabel));
82
- sections.push(ToolAssistance.createSection(touchInstructions, ToolAssistance.inputsLabel));
83
- const instructions = ToolAssistance.createInstructions(mainInstruction, sections);
84
- IModelApp.notifications.setToolAssistance(instructions);
85
- }
86
- }
87
- /** Tool for placing Markup Lines
88
- * @public
89
- */
90
- export class LineTool extends RedlineTool {
91
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
92
- createMarkup(svgMarkup, ev, isDynamics) {
93
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
94
- return;
95
- const start = this._points[0];
96
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
97
- const element = svgMarkup.line(start.x, start.y, end.x, end.y);
98
- this.setCurrentStyle(element, false);
99
- if (!isDynamics)
100
- this.onAdded(element);
101
- }
102
- }
103
- LineTool.toolId = "Markup.Line";
104
- LineTool.iconSpec = "icon-line";
105
- /** Tool for placing Markup Rectangles
106
- * @public
107
- */
108
- export class RectangleTool extends RedlineTool {
109
- constructor(_cornerRadius) {
110
- super();
111
- this._cornerRadius = _cornerRadius;
112
- } // Specify radius to create a rectangle with rounded corners.
113
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
114
- createMarkup(svgMarkup, ev, isDynamics) {
115
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
116
- return;
117
- const start = this._points[0];
118
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
119
- const vec = start.vectorTo(end);
120
- const width = Math.abs(vec.x);
121
- const height = Math.abs(vec.y);
122
- if (width < 1 || height < 1)
123
- return;
124
- const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
125
- const element = svgMarkup.rect(width, height).move(offset.x, offset.y);
126
- this.setCurrentStyle(element, true);
127
- if (undefined !== this._cornerRadius)
128
- element.radius(this._cornerRadius);
129
- if (!isDynamics)
130
- this.onAdded(element);
131
- }
132
- }
133
- RectangleTool.toolId = "Markup.Rectangle";
134
- RectangleTool.iconSpec = "icon-rectangle";
135
- /** Tool for placing Markup Polygons
136
- * @public
137
- */
138
- export class PolygonTool extends RedlineTool {
139
- constructor(_numSides) {
140
- super();
141
- this._numSides = _numSides;
142
- } // Specify number of polygon sides. Default if undefined is 5.
143
- showPrompt() { this.provideToolAssistance(MarkupTool.toolKey + (0 === this._points.length ? "Polygon.Prompts.FirstPoint" : "Polygon.Prompts.NextPoint")); }
144
- getPoints(points, center, edge, numSides, inscribe) {
145
- if (numSides < 3 || numSides > 100)
146
- return false;
147
- let radius = center.distanceXY(edge);
148
- if (radius < 1)
149
- return false;
150
- const delta = (Math.PI * 2.0) / numSides;
151
- const vec = center.vectorTo(edge);
152
- let angle = Vector3d.unitX().planarRadiansTo(vec, Vector3d.unitZ());
153
- if (!inscribe) {
154
- const theta = delta * 0.5;
155
- angle -= theta;
156
- radius /= Math.cos(theta);
157
- }
158
- const rtmp = Point3d.create();
159
- const stmp = Point3d.create();
160
- for (let i = 0; i < numSides; i++, angle += delta) {
161
- rtmp.x = radius * Math.cos(angle);
162
- rtmp.y = radius * Math.sin(angle);
163
- rtmp.z = 0.0;
164
- center.plus(rtmp, stmp);
165
- points.push(stmp.x);
166
- points.push(stmp.y);
167
- }
168
- return true;
169
- }
170
- createMarkup(svgMarkup, ev, isDynamics) {
171
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
172
- return;
173
- const center = this._points[0];
174
- const edge = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
175
- const pts = [];
176
- if (!this.getPoints(pts, center, edge, undefined !== this._numSides ? this._numSides : 5, true))
177
- return;
178
- const element = svgMarkup.polygon(pts);
179
- this.setCurrentStyle(element, true);
180
- if (!isDynamics)
181
- this.onAdded(element);
182
- }
183
- }
184
- PolygonTool.toolId = "Markup.Polygon";
185
- PolygonTool.iconSpec = "icon-polygon";
186
- /** Tool for placing Markup Clouds
187
- * @public
188
- */
189
- export class CloudTool extends RedlineTool {
190
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
191
- createMarkup(svgMarkup, ev, isDynamics) {
192
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
193
- return;
194
- const start = this._points[0];
195
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
196
- const vec = start.vectorTo(end);
197
- const width = Math.abs(vec.x);
198
- const height = Math.abs(vec.y);
199
- if (width < 10 || height < 10)
200
- return;
201
- if (undefined === this._cloud) {
202
- this._cloud = svgMarkup.path(MarkupApp.props.active.cloud.path);
203
- }
204
- else if (!isDynamics) {
205
- svgMarkup.add(this._cloud);
206
- }
207
- const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
208
- this._cloud.move(offset.x, offset.y);
209
- this._cloud.width(width);
210
- this._cloud.height(height);
211
- this.setCurrentStyle(this._cloud, true);
212
- if (!isDynamics)
213
- this.onAdded(this._cloud);
214
- }
215
- clearDynamicsMarkup(isDynamics) {
216
- if (!isDynamics)
217
- super.clearDynamicsMarkup(isDynamics); // For dynamics we don't create a new cloud each frame, we just set the width/height...
218
- }
219
- }
220
- CloudTool.toolId = "Markup.Cloud";
221
- CloudTool.iconSpec = "icon-cloud";
222
- /** Tool for placing Markup Circles
223
- * @public
224
- */
225
- export class CircleTool extends RedlineTool {
226
- showPrompt() { this.provideToolAssistance(MarkupTool.toolKey + (0 === this._points.length ? "Circle.Prompts.FirstPoint" : "Circle.Prompts.NextPoint")); }
227
- createMarkup(svgMarkup, ev, isDynamics) {
228
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
229
- return;
230
- const start = this._points[0];
231
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
232
- const radius = start.distanceXY(end);
233
- if (radius < 1)
234
- return;
235
- const element = svgMarkup.circle(radius * 2.0).center(start.x, start.y);
236
- this.setCurrentStyle(element, true);
237
- if (!isDynamics)
238
- this.onAdded(element);
239
- }
240
- }
241
- CircleTool.toolId = "Markup.Circle";
242
- CircleTool.iconSpec = "icon-circle";
243
- /** Tool for placing Markup Ellipses
244
- * @public
245
- */
246
- export class EllipseTool extends RedlineTool {
247
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
248
- createMarkup(svgMarkup, ev, isDynamics) {
249
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
250
- return;
251
- const start = this._points[0];
252
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
253
- const vec = start.vectorTo(end);
254
- const width = Math.abs(vec.x);
255
- const height = Math.abs(vec.y);
256
- if (width < 1 || height < 1)
257
- return;
258
- const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
259
- const element = svgMarkup.ellipse(width, height).move(offset.x, offset.y);
260
- this.setCurrentStyle(element, true);
261
- if (!isDynamics)
262
- this.onAdded(element);
263
- }
264
- }
265
- EllipseTool.toolId = "Markup.Ellipse";
266
- EllipseTool.iconSpec = "icon-ellipse";
267
- /** Tool for placing Markup Arrows
268
- * @public
269
- */
270
- export class ArrowTool extends RedlineTool {
271
- /** ctor for ArrowTool
272
- * @param _arrowPos "start", "end", or "both". If undefined = "end".
273
- */
274
- constructor(_arrowPos) {
275
- super();
276
- this._arrowPos = _arrowPos;
277
- }
278
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
279
- getOrCreateArrowMarker(color) {
280
- // NOTE: Flashing doesn't currently affect markers, need support for "context-stroke" and "context-fill". For now encode color in name...
281
- const arrowProps = MarkupApp.props.active.arrow;
282
- const arrowLength = arrowProps.length;
283
- const arrowWidth = arrowProps.width;
284
- const arrowMarkerId = `ArrowMarker${arrowLength}x${arrowWidth}-${color}`;
285
- let marker = SVG(`#${arrowMarkerId}`);
286
- if (null === marker) {
287
- marker = this.markup.svgMarkup.marker(arrowLength, arrowWidth).id(arrowMarkerId);
288
- marker.polygon([0, 0, arrowLength, arrowWidth * 0.5, 0, arrowWidth]);
289
- marker.attr("orient", "auto-start-reverse");
290
- marker.attr("overflow", "visible"); // Don't clip the stroke that is being applied to allow the specified start/end to be used directly while hiding the arrow tail fully under the arrow head...
291
- marker.attr("refX", arrowLength);
292
- marker.css({ stroke: color, fill: color });
293
- }
294
- return marker;
295
- }
296
- createMarkup(svgMarkup, ev, isDynamics) {
297
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
298
- return;
299
- const start = this._points[0];
300
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
301
- const vec = start.vectorTo(end);
302
- if (!vec.normalizeInPlace())
303
- return;
304
- const element = svgMarkup.line(start.x, start.y, end.x, end.y);
305
- this.setCurrentStyle(element, false);
306
- const marker = this.getOrCreateArrowMarker(element.css("stroke"));
307
- const addToStart = ("start" === this._arrowPos || "both" === this._arrowPos);
308
- const addToEnd = ("end" === this._arrowPos || "both" === this._arrowPos);
309
- if (addToStart)
310
- element.marker("start", marker);
311
- if (addToEnd || !addToStart)
312
- element.marker("end", marker);
313
- if (!isDynamics)
314
- this.onAdded(element);
315
- }
316
- }
317
- ArrowTool.toolId = "Markup.Arrow";
318
- ArrowTool.iconSpec = "icon-callout";
319
- /** Tool for measuring distances and placing Markups of them
320
- * @public
321
- */
322
- export class DistanceTool extends ArrowTool {
323
- constructor() {
324
- super(...arguments);
325
- this._startPointWorld = new Point3d();
326
- }
327
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
328
- setupAndPromptForNextAction() {
329
- IModelApp.accuSnap.enableSnap(true);
330
- IModelApp.toolAdmin.toolState.coordLockOvr = CoordinateLockOverrides.None;
331
- super.setupAndPromptForNextAction();
332
- }
333
- getFormattedDistance(distance) {
334
- const formatterSpec = IModelApp.quantityFormatter.findFormatterSpecByQuantityType(QuantityType.Length);
335
- if (undefined === formatterSpec)
336
- return undefined;
337
- return IModelApp.quantityFormatter.formatQuantity(distance, formatterSpec);
338
- }
339
- createMarkup(svgMarkup, ev, isDynamics) {
340
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
341
- return;
342
- const start = this._points[0];
343
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
344
- const vec = start.vectorTo(end);
345
- if (!vec.normalizeInPlace())
346
- return;
347
- const formatterSpec = IModelApp.quantityFormatter.findFormatterSpecByQuantityType(QuantityType.Length);
348
- if (undefined === formatterSpec)
349
- return;
350
- const distanceLine = svgMarkup.line(start.x, start.y, end.x, end.y);
351
- this.setCurrentStyle(distanceLine, false);
352
- const marker = this.getOrCreateArrowMarker(distanceLine.css("stroke"));
353
- distanceLine.marker("start", marker);
354
- distanceLine.marker("end", marker);
355
- const loc = start.interpolate(0.5, end);
356
- const distance = IModelApp.quantityFormatter.formatQuantity(this._startPointWorld.distance(ev.point), formatterSpec);
357
- const text = svgMarkup.plain(distance).addClass(MarkupApp.textClass).attr("text-anchor", "middle").translate(loc.x, loc.y);
358
- this.setCurrentTextStyle(text);
359
- const textWithBg = this.createBoxedText(svgMarkup, text);
360
- if (!isDynamics) {
361
- const markup = this.markup;
362
- const undo = markup.undo;
363
- undo.performOperation(this.keyin, () => {
364
- undo.onAdded(distanceLine);
365
- undo.onAdded(textWithBg);
366
- });
367
- markup.selected.restart(textWithBg); // Select text+box so that user can freely position relative to distance line...
368
- }
369
- }
370
- async onDataButtonDown(ev) {
371
- if (undefined === await IModelApp.quantityFormatter.getFormatterSpecByQuantityType(QuantityType.Length)) {
372
- await this.onReinitialize();
373
- return EventHandled.No;
374
- }
375
- if (0 === this._points.length)
376
- this._startPointWorld.setFrom(ev.point);
377
- return super.onDataButtonDown(ev);
378
- }
379
- }
380
- DistanceTool.toolId = "Markup.Distance";
381
- DistanceTool.iconSpec = "icon-distance";
382
- /** Tool for placing Markup freehand sketches
383
- * @public
384
- */
385
- export class SketchTool extends RedlineTool {
386
- constructor() {
387
- super(...arguments);
388
- this._minDistSquared = 100;
389
- }
390
- showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
391
- createMarkup(svgMarkup, ev, isDynamics) {
392
- if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
393
- return;
394
- const pts = [];
395
- const evPt = MarkupApp.convertVpToVb(ev.viewPoint);
396
- this._points.forEach((pt) => {
397
- pts.push(pt.x);
398
- pts.push(pt.y);
399
- });
400
- if (isDynamics && !evPt.isAlmostEqualXY(this._points[this._points.length - 1])) {
401
- pts.push(evPt.x);
402
- pts.push(evPt.y);
403
- }
404
- const isClosed = (this._points.length > 2 && (this._points[0].distanceSquaredXY(isDynamics ? evPt : this._points[this._points.length - 1]) < this._minDistSquared * 2));
405
- const element = isClosed ? svgMarkup.polygon(pts) : svgMarkup.polyline(pts);
406
- this.setCurrentStyle(element, isClosed);
407
- if (!isDynamics)
408
- this.onAdded(element);
409
- }
410
- async onMouseMotion(ev) {
411
- const evPt = MarkupApp.convertVpToVb(ev.viewPoint);
412
- if (undefined !== ev.viewport && this._points.length > 0 && evPt.distanceSquaredXY(this._points[this._points.length - 1]) > this._minDistSquared)
413
- this._points.push(evPt);
414
- return super.onMouseMotion(ev);
415
- }
416
- }
417
- SketchTool.toolId = "Markup.Sketch";
418
- SketchTool.iconSpec = "icon-draw";
419
- /** Tool for placing SVG symbols on a Markup
420
- * @public
421
- */
422
- export class SymbolTool extends RedlineTool {
423
- constructor(_symbolData, _applyCurrentStyle) {
424
- super();
425
- this._symbolData = _symbolData;
426
- this._applyCurrentStyle = _applyCurrentStyle;
427
- }
428
- async onInstall() {
429
- if (undefined === this._symbolData)
430
- return false;
431
- return super.onInstall();
432
- }
433
- showPrompt() {
434
- this.provideToolAssistance(0 === this._points.length ? (`${MarkupTool.toolKey}Symbol.Prompts.FirstPoint`) : `${CoreTools.tools}ElementSet.Prompts.OppositeCorner`, true);
435
- }
436
- createMarkup(svgMarkup, ev, isDynamics) {
437
- if (undefined === this._symbolData)
438
- return;
439
- if (this._points.length < this._minPoints)
440
- return;
441
- const start = this._points[0];
442
- const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[this._points.length - 1];
443
- const vec = start.vectorTo(end);
444
- const width = Math.abs(vec.x);
445
- const height = Math.abs(vec.y);
446
- if ((width < 10 || height < 10) && (isDynamics || this._points.length !== this._minPoints))
447
- return;
448
- if (undefined === this._symbol) {
449
- const symbol = svgMarkup.group().svg(this._symbolData); // creating group instead of using symbol because of inability to flash/hilite multi-color symbol instance...
450
- if (0 === symbol.children().length) {
451
- symbol.remove();
452
- this._symbolData = undefined;
453
- }
454
- try {
455
- symbol.flatten(symbol);
456
- }
457
- catch { }
458
- this._symbol = symbol;
459
- }
460
- else if (!isDynamics) {
461
- svgMarkup.add(this._symbol);
462
- }
463
- const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
464
- if (!isDynamics && this._points.length === this._minPoints)
465
- this._symbol.size(ev.viewport.viewRect.width * 0.1).center(offset.x, offset.y);
466
- else if (!ev.isShiftKey)
467
- this._symbol.size(width).move(offset.x, offset.y);
468
- else
469
- this._symbol.size(width, height).move(offset.x, offset.y);
470
- if (this._applyCurrentStyle) {
471
- const active = MarkupApp.props.active; // Apply color and transparency only; using active stroke-width, etc. for pre-defined symbols is likely undesirable...
472
- this._symbol.forElementsOfGroup((child) => {
473
- const css = window.getComputedStyle(child.node);
474
- const toValue = (val, newVal) => (!val || val === "none") ? "none" : newVal;
475
- child.css({ "fill": toValue(css.fill, active.element.fill), "stroke": toValue(css.stroke, active.element.stroke), "fill-opacity": active.element["fill-opacity"], "stroke-opacity": active.element["stroke-opacity"] });
476
- });
477
- }
478
- if (!isDynamics)
479
- this.onAdded(this._symbol);
480
- }
481
- async onDataButtonUp(ev) {
482
- if (undefined === ev.viewport || this._points.length !== this._minPoints)
483
- return EventHandled.No;
484
- this.createMarkup(this.markup.svgMarkup, ev, false);
485
- await this.onReinitialize();
486
- return EventHandled.No;
487
- }
488
- async onResetButtonUp(_ev) {
489
- await this.onReinitialize();
490
- return EventHandled.No;
491
- }
492
- clearDynamicsMarkup(isDynamics) {
493
- if (!isDynamics)
494
- super.clearDynamicsMarkup(isDynamics); // For dynamics we don't create a new symbol each frame, we just set the width/height...
495
- }
496
- }
497
- SymbolTool.toolId = "Markup.Symbol";
498
- SymbolTool.iconSpec = "icon-symbol";
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 MarkupTools
7
+ */
8
+ // cspell:ignore rtmp stmp
9
+ import { Point3d, Vector3d } from "@itwin/core-geometry";
10
+ import { CoordinateLockOverrides, CoreTools, EventHandled, IModelApp, QuantityType, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
11
+ import { SVG } from "@svgdotjs/svg.js";
12
+ import { MarkupApp } from "./Markup";
13
+ import { MarkupTool } from "./MarkupTool";
14
+ /** Base class for tools that place new Markup elements
15
+ * @public
16
+ */
17
+ export class RedlineTool extends MarkupTool {
18
+ constructor() {
19
+ super(...arguments);
20
+ this._minPoints = 1;
21
+ this._nRequiredPoints = 2;
22
+ this._points = [];
23
+ }
24
+ onAdded(el) {
25
+ const markup = this.markup;
26
+ const undo = markup.undo;
27
+ undo.performOperation(this.keyin, () => undo.onAdded(el));
28
+ markup.selected.restart(el);
29
+ }
30
+ isComplete(_ev) { return this._points.length >= this._nRequiredPoints; }
31
+ setupAndPromptForNextAction() {
32
+ super.setupAndPromptForNextAction();
33
+ this.markup.disablePick();
34
+ IModelApp.toolAdmin.setCursor(0 === this._points.length ? IModelApp.viewManager.crossHairCursor : IModelApp.viewManager.dynamicsCursor);
35
+ }
36
+ createMarkup(_svgMarkup, _ev, _isDynamics) { }
37
+ clearDynamicsMarkup(_isDynamics) { this.markup.svgDynamics.clear(); }
38
+ async onRestartTool() { return this.exitTool(); } // Default to single shot and return control to select tool...
39
+ async onCleanup() { this.clearDynamicsMarkup(false); }
40
+ async onReinitialize() {
41
+ this.clearDynamicsMarkup(false);
42
+ return super.onReinitialize();
43
+ }
44
+ async onUndoPreviousStep() {
45
+ return (0 === this._points.length) ? false : (this.onReinitialize(), true);
46
+ }
47
+ async onMouseMotion(ev) {
48
+ if (undefined === ev.viewport || this._points.length < this._minPoints)
49
+ return;
50
+ this.clearDynamicsMarkup(true);
51
+ this.createMarkup(this.markup.svgDynamics, ev, true);
52
+ }
53
+ async onDataButtonDown(ev) {
54
+ if (undefined === ev.viewport)
55
+ return EventHandled.No;
56
+ this._points.push(MarkupApp.convertVpToVb(ev.viewPoint));
57
+ if (!this.isComplete(ev)) {
58
+ this.setupAndPromptForNextAction();
59
+ return EventHandled.No;
60
+ }
61
+ this.createMarkup(this.markup.svgMarkup, ev, false);
62
+ await this.onReinitialize();
63
+ return EventHandled.No;
64
+ }
65
+ async onResetButtonUp(_ev) {
66
+ await this.onReinitialize();
67
+ return EventHandled.No;
68
+ }
69
+ provideToolAssistance(mainInstrKey, singlePoint = false) {
70
+ const mainInstruction = ToolAssistance.createInstruction(this.iconSpec, IModelApp.localization.getLocalizedString(mainInstrKey));
71
+ const mouseInstructions = [];
72
+ const touchInstructions = [];
73
+ const acceptMsg = CoreTools.translate("ElementSet.Inputs.AcceptPoint");
74
+ const rejectMsg = CoreTools.translate("ElementSet.Inputs.Exit");
75
+ if (!ToolAssistance.createTouchCursorInstructions(touchInstructions))
76
+ touchInstructions.push(ToolAssistance.createInstruction(singlePoint ? ToolAssistanceImage.OneTouchTap : ToolAssistanceImage.OneTouchDrag, acceptMsg, false, ToolAssistanceInputMethod.Touch));
77
+ mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, acceptMsg, false, ToolAssistanceInputMethod.Mouse));
78
+ touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.TwoTouchTap, rejectMsg, false, ToolAssistanceInputMethod.Touch));
79
+ mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.RightClick, rejectMsg, false, ToolAssistanceInputMethod.Mouse));
80
+ const sections = [];
81
+ sections.push(ToolAssistance.createSection(mouseInstructions, ToolAssistance.inputsLabel));
82
+ sections.push(ToolAssistance.createSection(touchInstructions, ToolAssistance.inputsLabel));
83
+ const instructions = ToolAssistance.createInstructions(mainInstruction, sections);
84
+ IModelApp.notifications.setToolAssistance(instructions);
85
+ }
86
+ }
87
+ /** Tool for placing Markup Lines
88
+ * @public
89
+ */
90
+ class LineTool extends RedlineTool {
91
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
92
+ createMarkup(svgMarkup, ev, isDynamics) {
93
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
94
+ return;
95
+ const start = this._points[0];
96
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
97
+ const element = svgMarkup.line(start.x, start.y, end.x, end.y);
98
+ this.setCurrentStyle(element, false);
99
+ if (!isDynamics)
100
+ this.onAdded(element);
101
+ }
102
+ }
103
+ LineTool.toolId = "Markup.Line";
104
+ LineTool.iconSpec = "icon-line";
105
+ export { LineTool };
106
+ /** Tool for placing Markup Rectangles
107
+ * @public
108
+ */
109
+ class RectangleTool extends RedlineTool {
110
+ constructor(_cornerRadius) {
111
+ super();
112
+ this._cornerRadius = _cornerRadius;
113
+ } // Specify radius to create a rectangle with rounded corners.
114
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
115
+ createMarkup(svgMarkup, ev, isDynamics) {
116
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
117
+ return;
118
+ const start = this._points[0];
119
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
120
+ const vec = start.vectorTo(end);
121
+ const width = Math.abs(vec.x);
122
+ const height = Math.abs(vec.y);
123
+ if (width < 1 || height < 1)
124
+ return;
125
+ const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
126
+ const element = svgMarkup.rect(width, height).move(offset.x, offset.y);
127
+ this.setCurrentStyle(element, true);
128
+ if (undefined !== this._cornerRadius)
129
+ element.radius(this._cornerRadius);
130
+ if (!isDynamics)
131
+ this.onAdded(element);
132
+ }
133
+ }
134
+ RectangleTool.toolId = "Markup.Rectangle";
135
+ RectangleTool.iconSpec = "icon-rectangle";
136
+ export { RectangleTool };
137
+ /** Tool for placing Markup Polygons
138
+ * @public
139
+ */
140
+ class PolygonTool extends RedlineTool {
141
+ constructor(_numSides) {
142
+ super();
143
+ this._numSides = _numSides;
144
+ } // Specify number of polygon sides. Default if undefined is 5.
145
+ showPrompt() { this.provideToolAssistance(MarkupTool.toolKey + (0 === this._points.length ? "Polygon.Prompts.FirstPoint" : "Polygon.Prompts.NextPoint")); }
146
+ getPoints(points, center, edge, numSides, inscribe) {
147
+ if (numSides < 3 || numSides > 100)
148
+ return false;
149
+ let radius = center.distanceXY(edge);
150
+ if (radius < 1)
151
+ return false;
152
+ const delta = (Math.PI * 2.0) / numSides;
153
+ const vec = center.vectorTo(edge);
154
+ let angle = Vector3d.unitX().planarRadiansTo(vec, Vector3d.unitZ());
155
+ if (!inscribe) {
156
+ const theta = delta * 0.5;
157
+ angle -= theta;
158
+ radius /= Math.cos(theta);
159
+ }
160
+ const rtmp = Point3d.create();
161
+ const stmp = Point3d.create();
162
+ for (let i = 0; i < numSides; i++, angle += delta) {
163
+ rtmp.x = radius * Math.cos(angle);
164
+ rtmp.y = radius * Math.sin(angle);
165
+ rtmp.z = 0.0;
166
+ center.plus(rtmp, stmp);
167
+ points.push(stmp.x);
168
+ points.push(stmp.y);
169
+ }
170
+ return true;
171
+ }
172
+ createMarkup(svgMarkup, ev, isDynamics) {
173
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
174
+ return;
175
+ const center = this._points[0];
176
+ const edge = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
177
+ const pts = [];
178
+ if (!this.getPoints(pts, center, edge, undefined !== this._numSides ? this._numSides : 5, true))
179
+ return;
180
+ const element = svgMarkup.polygon(pts);
181
+ this.setCurrentStyle(element, true);
182
+ if (!isDynamics)
183
+ this.onAdded(element);
184
+ }
185
+ }
186
+ PolygonTool.toolId = "Markup.Polygon";
187
+ PolygonTool.iconSpec = "icon-polygon";
188
+ export { PolygonTool };
189
+ /** Tool for placing Markup Clouds
190
+ * @public
191
+ */
192
+ class CloudTool extends RedlineTool {
193
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
194
+ createMarkup(svgMarkup, ev, isDynamics) {
195
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
196
+ return;
197
+ const start = this._points[0];
198
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
199
+ const vec = start.vectorTo(end);
200
+ const width = Math.abs(vec.x);
201
+ const height = Math.abs(vec.y);
202
+ if (width < 10 || height < 10)
203
+ return;
204
+ if (undefined === this._cloud) {
205
+ this._cloud = svgMarkup.path(MarkupApp.props.active.cloud.path);
206
+ }
207
+ else if (!isDynamics) {
208
+ svgMarkup.add(this._cloud);
209
+ }
210
+ const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
211
+ this._cloud.move(offset.x, offset.y);
212
+ this._cloud.width(width);
213
+ this._cloud.height(height);
214
+ this.setCurrentStyle(this._cloud, true);
215
+ if (!isDynamics)
216
+ this.onAdded(this._cloud);
217
+ }
218
+ clearDynamicsMarkup(isDynamics) {
219
+ if (!isDynamics)
220
+ super.clearDynamicsMarkup(isDynamics); // For dynamics we don't create a new cloud each frame, we just set the width/height...
221
+ }
222
+ }
223
+ CloudTool.toolId = "Markup.Cloud";
224
+ CloudTool.iconSpec = "icon-cloud";
225
+ export { CloudTool };
226
+ /** Tool for placing Markup Circles
227
+ * @public
228
+ */
229
+ class CircleTool extends RedlineTool {
230
+ showPrompt() { this.provideToolAssistance(MarkupTool.toolKey + (0 === this._points.length ? "Circle.Prompts.FirstPoint" : "Circle.Prompts.NextPoint")); }
231
+ createMarkup(svgMarkup, ev, isDynamics) {
232
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
233
+ return;
234
+ const start = this._points[0];
235
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
236
+ const radius = start.distanceXY(end);
237
+ if (radius < 1)
238
+ return;
239
+ const element = svgMarkup.circle(radius * 2.0).center(start.x, start.y);
240
+ this.setCurrentStyle(element, true);
241
+ if (!isDynamics)
242
+ this.onAdded(element);
243
+ }
244
+ }
245
+ CircleTool.toolId = "Markup.Circle";
246
+ CircleTool.iconSpec = "icon-circle";
247
+ export { CircleTool };
248
+ /** Tool for placing Markup Ellipses
249
+ * @public
250
+ */
251
+ class EllipseTool extends RedlineTool {
252
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartCorner" : "ElementSet.Prompts.OppositeCorner")); }
253
+ createMarkup(svgMarkup, ev, isDynamics) {
254
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
255
+ return;
256
+ const start = this._points[0];
257
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
258
+ const vec = start.vectorTo(end);
259
+ const width = Math.abs(vec.x);
260
+ const height = Math.abs(vec.y);
261
+ if (width < 1 || height < 1)
262
+ return;
263
+ const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
264
+ const element = svgMarkup.ellipse(width, height).move(offset.x, offset.y);
265
+ this.setCurrentStyle(element, true);
266
+ if (!isDynamics)
267
+ this.onAdded(element);
268
+ }
269
+ }
270
+ EllipseTool.toolId = "Markup.Ellipse";
271
+ EllipseTool.iconSpec = "icon-ellipse";
272
+ export { EllipseTool };
273
+ /** Tool for placing Markup Arrows
274
+ * @public
275
+ */
276
+ class ArrowTool extends RedlineTool {
277
+ /** ctor for ArrowTool
278
+ * @param _arrowPos "start", "end", or "both". If undefined = "end".
279
+ */
280
+ constructor(_arrowPos) {
281
+ super();
282
+ this._arrowPos = _arrowPos;
283
+ }
284
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
285
+ getOrCreateArrowMarker(color) {
286
+ // NOTE: Flashing doesn't currently affect markers, need support for "context-stroke" and "context-fill". For now encode color in name...
287
+ const arrowProps = MarkupApp.props.active.arrow;
288
+ const arrowLength = arrowProps.length;
289
+ const arrowWidth = arrowProps.width;
290
+ const arrowMarkerId = `ArrowMarker${arrowLength}x${arrowWidth}-${color}`;
291
+ let marker = SVG(`#${arrowMarkerId}`);
292
+ if (null === marker) {
293
+ marker = this.markup.svgMarkup.marker(arrowLength, arrowWidth).id(arrowMarkerId);
294
+ marker.polygon([0, 0, arrowLength, arrowWidth * 0.5, 0, arrowWidth]);
295
+ marker.attr("orient", "auto-start-reverse");
296
+ marker.attr("overflow", "visible"); // Don't clip the stroke that is being applied to allow the specified start/end to be used directly while hiding the arrow tail fully under the arrow head...
297
+ marker.attr("refX", arrowLength);
298
+ marker.css({ stroke: color, fill: color });
299
+ }
300
+ return marker;
301
+ }
302
+ createMarkup(svgMarkup, ev, isDynamics) {
303
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
304
+ return;
305
+ const start = this._points[0];
306
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
307
+ const vec = start.vectorTo(end);
308
+ if (!vec.normalizeInPlace())
309
+ return;
310
+ const element = svgMarkup.line(start.x, start.y, end.x, end.y);
311
+ this.setCurrentStyle(element, false);
312
+ const marker = this.getOrCreateArrowMarker(element.css("stroke"));
313
+ const addToStart = ("start" === this._arrowPos || "both" === this._arrowPos);
314
+ const addToEnd = ("end" === this._arrowPos || "both" === this._arrowPos);
315
+ if (addToStart)
316
+ element.marker("start", marker);
317
+ if (addToEnd || !addToStart)
318
+ element.marker("end", marker);
319
+ if (!isDynamics)
320
+ this.onAdded(element);
321
+ }
322
+ }
323
+ ArrowTool.toolId = "Markup.Arrow";
324
+ ArrowTool.iconSpec = "icon-callout";
325
+ export { ArrowTool };
326
+ /** Tool for measuring distances and placing Markups of them
327
+ * @public
328
+ */
329
+ class DistanceTool extends ArrowTool {
330
+ constructor() {
331
+ super(...arguments);
332
+ this._startPointWorld = new Point3d();
333
+ }
334
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
335
+ setupAndPromptForNextAction() {
336
+ IModelApp.accuSnap.enableSnap(true);
337
+ IModelApp.toolAdmin.toolState.coordLockOvr = CoordinateLockOverrides.None;
338
+ super.setupAndPromptForNextAction();
339
+ }
340
+ getFormattedDistance(distance) {
341
+ const formatterSpec = IModelApp.quantityFormatter.findFormatterSpecByQuantityType(QuantityType.Length);
342
+ if (undefined === formatterSpec)
343
+ return undefined;
344
+ return IModelApp.quantityFormatter.formatQuantity(distance, formatterSpec);
345
+ }
346
+ createMarkup(svgMarkup, ev, isDynamics) {
347
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
348
+ return;
349
+ const start = this._points[0];
350
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[1];
351
+ const vec = start.vectorTo(end);
352
+ if (!vec.normalizeInPlace())
353
+ return;
354
+ const formatterSpec = IModelApp.quantityFormatter.findFormatterSpecByQuantityType(QuantityType.Length);
355
+ if (undefined === formatterSpec)
356
+ return;
357
+ const distanceLine = svgMarkup.line(start.x, start.y, end.x, end.y);
358
+ this.setCurrentStyle(distanceLine, false);
359
+ const marker = this.getOrCreateArrowMarker(distanceLine.css("stroke"));
360
+ distanceLine.marker("start", marker);
361
+ distanceLine.marker("end", marker);
362
+ const loc = start.interpolate(0.5, end);
363
+ const distance = IModelApp.quantityFormatter.formatQuantity(this._startPointWorld.distance(ev.point), formatterSpec);
364
+ const text = svgMarkup.plain(distance).addClass(MarkupApp.textClass).attr("text-anchor", "middle").translate(loc.x, loc.y);
365
+ this.setCurrentTextStyle(text);
366
+ const textWithBg = this.createBoxedText(svgMarkup, text);
367
+ if (!isDynamics) {
368
+ const markup = this.markup;
369
+ const undo = markup.undo;
370
+ undo.performOperation(this.keyin, () => {
371
+ undo.onAdded(distanceLine);
372
+ undo.onAdded(textWithBg);
373
+ });
374
+ markup.selected.restart(textWithBg); // Select text+box so that user can freely position relative to distance line...
375
+ }
376
+ }
377
+ async onDataButtonDown(ev) {
378
+ if (undefined === await IModelApp.quantityFormatter.getFormatterSpecByQuantityType(QuantityType.Length)) {
379
+ await this.onReinitialize();
380
+ return EventHandled.No;
381
+ }
382
+ if (0 === this._points.length)
383
+ this._startPointWorld.setFrom(ev.point);
384
+ return super.onDataButtonDown(ev);
385
+ }
386
+ }
387
+ DistanceTool.toolId = "Markup.Distance";
388
+ DistanceTool.iconSpec = "icon-distance";
389
+ export { DistanceTool };
390
+ /** Tool for placing Markup freehand sketches
391
+ * @public
392
+ */
393
+ class SketchTool extends RedlineTool {
394
+ constructor() {
395
+ super(...arguments);
396
+ this._minDistSquared = 100;
397
+ }
398
+ showPrompt() { this.provideToolAssistance(CoreTools.tools + (0 === this._points.length ? "ElementSet.Prompts.StartPoint" : "ElementSet.Prompts.EndPoint")); }
399
+ createMarkup(svgMarkup, ev, isDynamics) {
400
+ if (this._points.length < (isDynamics ? this._nRequiredPoints - 1 : this._nRequiredPoints))
401
+ return;
402
+ const pts = [];
403
+ const evPt = MarkupApp.convertVpToVb(ev.viewPoint);
404
+ this._points.forEach((pt) => {
405
+ pts.push(pt.x);
406
+ pts.push(pt.y);
407
+ });
408
+ if (isDynamics && !evPt.isAlmostEqualXY(this._points[this._points.length - 1])) {
409
+ pts.push(evPt.x);
410
+ pts.push(evPt.y);
411
+ }
412
+ const isClosed = (this._points.length > 2 && (this._points[0].distanceSquaredXY(isDynamics ? evPt : this._points[this._points.length - 1]) < this._minDistSquared * 2));
413
+ const element = isClosed ? svgMarkup.polygon(pts) : svgMarkup.polyline(pts);
414
+ this.setCurrentStyle(element, isClosed);
415
+ if (!isDynamics)
416
+ this.onAdded(element);
417
+ }
418
+ async onMouseMotion(ev) {
419
+ const evPt = MarkupApp.convertVpToVb(ev.viewPoint);
420
+ if (undefined !== ev.viewport && this._points.length > 0 && evPt.distanceSquaredXY(this._points[this._points.length - 1]) > this._minDistSquared)
421
+ this._points.push(evPt);
422
+ return super.onMouseMotion(ev);
423
+ }
424
+ }
425
+ SketchTool.toolId = "Markup.Sketch";
426
+ SketchTool.iconSpec = "icon-draw";
427
+ export { SketchTool };
428
+ /** Tool for placing SVG symbols on a Markup
429
+ * @public
430
+ */
431
+ class SymbolTool extends RedlineTool {
432
+ constructor(_symbolData, _applyCurrentStyle) {
433
+ super();
434
+ this._symbolData = _symbolData;
435
+ this._applyCurrentStyle = _applyCurrentStyle;
436
+ }
437
+ async onInstall() {
438
+ if (undefined === this._symbolData)
439
+ return false;
440
+ return super.onInstall();
441
+ }
442
+ showPrompt() {
443
+ this.provideToolAssistance(0 === this._points.length ? (`${MarkupTool.toolKey}Symbol.Prompts.FirstPoint`) : `${CoreTools.tools}ElementSet.Prompts.OppositeCorner`, true);
444
+ }
445
+ createMarkup(svgMarkup, ev, isDynamics) {
446
+ if (undefined === this._symbolData)
447
+ return;
448
+ if (this._points.length < this._minPoints)
449
+ return;
450
+ const start = this._points[0];
451
+ const end = isDynamics ? MarkupApp.convertVpToVb(ev.viewPoint) : this._points[this._points.length - 1];
452
+ const vec = start.vectorTo(end);
453
+ const width = Math.abs(vec.x);
454
+ const height = Math.abs(vec.y);
455
+ if ((width < 10 || height < 10) && (isDynamics || this._points.length !== this._minPoints))
456
+ return;
457
+ if (undefined === this._symbol) {
458
+ const symbol = svgMarkup.group().svg(this._symbolData); // creating group instead of using symbol because of inability to flash/hilite multi-color symbol instance...
459
+ if (0 === symbol.children().length) {
460
+ symbol.remove();
461
+ this._symbolData = undefined;
462
+ }
463
+ try {
464
+ symbol.flatten(symbol);
465
+ }
466
+ catch { }
467
+ this._symbol = symbol;
468
+ }
469
+ else if (!isDynamics) {
470
+ svgMarkup.add(this._symbol);
471
+ }
472
+ const offset = Point3d.create(vec.x < 0 ? end.x : start.x, vec.y < 0 ? end.y : start.y); // define location by corner points...
473
+ if (!isDynamics && this._points.length === this._minPoints)
474
+ this._symbol.size(ev.viewport.viewRect.width * 0.1).center(offset.x, offset.y);
475
+ else if (!ev.isShiftKey)
476
+ this._symbol.size(width).move(offset.x, offset.y);
477
+ else
478
+ this._symbol.size(width, height).move(offset.x, offset.y);
479
+ if (this._applyCurrentStyle) {
480
+ const active = MarkupApp.props.active; // Apply color and transparency only; using active stroke-width, etc. for pre-defined symbols is likely undesirable...
481
+ this._symbol.forElementsOfGroup((child) => {
482
+ const css = window.getComputedStyle(child.node);
483
+ const toValue = (val, newVal) => (!val || val === "none") ? "none" : newVal;
484
+ child.css({ "fill": toValue(css.fill, active.element.fill), "stroke": toValue(css.stroke, active.element.stroke), "fill-opacity": active.element["fill-opacity"], "stroke-opacity": active.element["stroke-opacity"] });
485
+ });
486
+ }
487
+ if (!isDynamics)
488
+ this.onAdded(this._symbol);
489
+ }
490
+ async onDataButtonUp(ev) {
491
+ if (undefined === ev.viewport || this._points.length !== this._minPoints)
492
+ return EventHandled.No;
493
+ this.createMarkup(this.markup.svgMarkup, ev, false);
494
+ await this.onReinitialize();
495
+ return EventHandled.No;
496
+ }
497
+ async onResetButtonUp(_ev) {
498
+ await this.onReinitialize();
499
+ return EventHandled.No;
500
+ }
501
+ clearDynamicsMarkup(isDynamics) {
502
+ if (!isDynamics)
503
+ super.clearDynamicsMarkup(isDynamics); // For dynamics we don't create a new symbol each frame, we just set the width/height...
504
+ }
505
+ }
506
+ SymbolTool.toolId = "Markup.Symbol";
507
+ SymbolTool.iconSpec = "icon-symbol";
508
+ export { SymbolTool };
499
509
  //# sourceMappingURL=RedlineTool.js.map