@itwin/editor-frontend 3.0.0-dev.98 → 3.0.0

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 (61) hide show
  1. package/CHANGELOG.md +108 -1
  2. package/LICENSE.md +1 -1
  3. package/README.md +9 -9
  4. package/lib/cjs/EditTool.d.ts.map +1 -1
  5. package/lib/cjs/EditTool.js +18 -7
  6. package/lib/cjs/EditTool.js.map +1 -1
  7. package/lib/cjs/ElementGeometryTool.d.ts +64 -80
  8. package/lib/cjs/ElementGeometryTool.d.ts.map +1 -1
  9. package/lib/cjs/ElementGeometryTool.js +333 -295
  10. package/lib/cjs/ElementGeometryTool.js.map +1 -1
  11. package/lib/cjs/ProjectLocation/ProjectGeolocation.d.ts +10 -17
  12. package/lib/cjs/ProjectLocation/ProjectGeolocation.d.ts.map +1 -1
  13. package/lib/cjs/ProjectLocation/ProjectGeolocation.js +54 -94
  14. package/lib/cjs/ProjectLocation/ProjectGeolocation.js.map +1 -1
  15. package/lib/cjs/SketchTools.d.ts +21 -9
  16. package/lib/cjs/SketchTools.d.ts.map +1 -1
  17. package/lib/cjs/SketchTools.js +197 -204
  18. package/lib/cjs/SketchTools.js.map +1 -1
  19. package/lib/cjs/SolidModelingTools.d.ts +381 -0
  20. package/lib/cjs/SolidModelingTools.d.ts.map +1 -0
  21. package/lib/cjs/SolidModelingTools.js +1460 -0
  22. package/lib/cjs/SolidModelingTools.js.map +1 -0
  23. package/lib/cjs/TransformElementsTool.d.ts.map +1 -1
  24. package/lib/cjs/TransformElementsTool.js +8 -28
  25. package/lib/cjs/TransformElementsTool.js.map +1 -1
  26. package/lib/cjs/UndoRedoTool.js +1 -1
  27. package/lib/cjs/UndoRedoTool.js.map +1 -1
  28. package/lib/cjs/editor-frontend.d.ts +1 -0
  29. package/lib/cjs/editor-frontend.d.ts.map +1 -1
  30. package/lib/cjs/editor-frontend.js +1 -0
  31. package/lib/cjs/editor-frontend.js.map +1 -1
  32. package/lib/esm/EditTool.d.ts.map +1 -1
  33. package/lib/esm/EditTool.js +14 -3
  34. package/lib/esm/EditTool.js.map +1 -1
  35. package/lib/esm/ElementGeometryTool.d.ts +64 -80
  36. package/lib/esm/ElementGeometryTool.d.ts.map +1 -1
  37. package/lib/esm/ElementGeometryTool.js +334 -290
  38. package/lib/esm/ElementGeometryTool.js.map +1 -1
  39. package/lib/esm/ProjectLocation/ProjectGeolocation.d.ts +10 -17
  40. package/lib/esm/ProjectLocation/ProjectGeolocation.d.ts.map +1 -1
  41. package/lib/esm/ProjectLocation/ProjectGeolocation.js +54 -94
  42. package/lib/esm/ProjectLocation/ProjectGeolocation.js.map +1 -1
  43. package/lib/esm/SketchTools.d.ts +21 -9
  44. package/lib/esm/SketchTools.d.ts.map +1 -1
  45. package/lib/esm/SketchTools.js +198 -205
  46. package/lib/esm/SketchTools.js.map +1 -1
  47. package/lib/esm/SolidModelingTools.d.ts +381 -0
  48. package/lib/esm/SolidModelingTools.d.ts.map +1 -0
  49. package/lib/esm/SolidModelingTools.js +1435 -0
  50. package/lib/esm/SolidModelingTools.js.map +1 -0
  51. package/lib/esm/TransformElementsTool.d.ts.map +1 -1
  52. package/lib/esm/TransformElementsTool.js +8 -28
  53. package/lib/esm/TransformElementsTool.js.map +1 -1
  54. package/lib/esm/UndoRedoTool.js +1 -1
  55. package/lib/esm/UndoRedoTool.js.map +1 -1
  56. package/lib/esm/editor-frontend.d.ts +1 -0
  57. package/lib/esm/editor-frontend.d.ts.map +1 -1
  58. package/lib/esm/editor-frontend.js +1 -0
  59. package/lib/esm/editor-frontend.js.map +1 -1
  60. package/lib/public/locales/en/Editor.json +141 -3
  61. package/package.json +17 -25
@@ -3,7 +3,7 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import { BentleyError } from "@itwin/core-bentley";
6
- import { Angle, AngleSweep, Arc3d, BSplineCurve3d, BSplineCurveOps, CurveFactory, FrameBuilder, Geometry, IModelJson, LineString3d, Loop, Matrix3d, Path, Plane3dByOriginAndUnitNormal, Point3d, PointString3d, Ray3d, RegionOps, Transform, Vector3d, YawPitchRollAngles } from "@itwin/core-geometry";
6
+ import { Angle, AngleSweep, Arc3d, BSplineCurve3d, CurveFactory, FrameBuilder, Geometry, IModelJson, InterpolationCurve3d, InterpolationCurve3dOptions, LineString3d, Loop, Matrix3d, Path, Plane3dByOriginAndUnitNormal, Point3d, PointString3d, Ray3d, RegionOps, Transform, Vector3d, YawPitchRollAngles } from "@itwin/core-geometry";
7
7
  import { Code, ColorDef, ElementGeometry, isPlacement3dProps, LinePixels } from "@itwin/core-common";
8
8
  import { AccuDrawHintBuilder, AngleDescription, BeButton, BeModifierKeys, CoreTools, EventHandled, GraphicType, IModelApp, LengthDescription, NotifyMessageDetails, OutputMessagePriority, TentativeOrAccuSnap, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod } from "@itwin/core-frontend";
9
9
  import { editorBuiltInCmdIds } from "@itwin/editor-common";
@@ -233,6 +233,7 @@ export class CreateOrContinuePathTool extends CreateElementTool {
233
233
  switch (curve.curvePrimitiveType) {
234
234
  case "arc":
235
235
  case "bsplineCurve":
236
+ case "interpolationCurve":
236
237
  break;
237
238
  default:
238
239
  return;
@@ -267,6 +268,12 @@ export class CreateOrContinuePathTool extends CreateElementTool {
267
268
  builder.addPointString(poles);
268
269
  break;
269
270
  }
271
+ case "interpolationCurve": {
272
+ const fitCurve = curve;
273
+ builder.setSymbology(context.viewport.getContrastToBackgroundColor(), ColorDef.black, 5);
274
+ builder.addPointString(fitCurve.options.fitPoints); // deep copy shoulnd't be necessary...
275
+ break;
276
+ }
270
277
  default:
271
278
  break;
272
279
  }
@@ -554,11 +561,17 @@ export class CreateOrContinuePathTool extends CreateElementTool {
554
561
  await this.updateCurveAndContinuationData(ev, false, phase);
555
562
  return true;
556
563
  }
564
+ async cancelPoint(_ev) { return true; }
557
565
  async onDataButtonDown(ev) {
558
566
  if (!await this.acceptPoint(ev))
559
567
  return EventHandled.Yes;
560
568
  return super.onDataButtonDown(ev);
561
569
  }
570
+ async onResetButtonUp(ev) {
571
+ if (!await this.cancelPoint(ev))
572
+ return EventHandled.Yes;
573
+ return super.onResetButtonUp(ev);
574
+ }
562
575
  async onUndoPreviousStep() {
563
576
  if (0 === this.accepted.length)
564
577
  return false;
@@ -617,13 +630,13 @@ export class CreateLineStringTool extends CreateOrContinuePathTool {
617
630
  const pts = (isDynamics ? [...this.accepted, ev.point] : this.accepted);
618
631
  return LineString3d.create(pts);
619
632
  }
620
- async onResetButtonUp(ev) {
633
+ async cancelPoint(ev) {
621
634
  // NOTE: Starting another tool will not create element...require reset or closure...
622
635
  if (this.isComplete(ev)) {
623
636
  await this.updateCurveAndContinuationData(ev, false, CreateCurvePhase.DefineEnd);
624
637
  await this.createElement();
625
638
  }
626
- return super.onResetButtonUp(ev);
639
+ return true;
627
640
  }
628
641
  async onRestartTool() {
629
642
  const tool = new CreateLineStringTool();
@@ -914,70 +927,28 @@ export class CreateArcTool extends CreateOrContinuePathTool {
914
927
  return;
915
928
  }
916
929
  const syncData = [];
917
- if (!this.useRadius) {
918
- this.radiusProperty.displayValue = this.radiusProperty.description.format(this.radius);
919
- this.radiusProperty.isDisabled = !this.useRadius;
930
+ if (!this.useRadius)
920
931
  syncData.push(this.radiusProperty.syncItem);
921
- }
922
- if (!this.useSweep) {
923
- this.sweepProperty.displayValue = this.sweepProperty.description.format(this.sweep);
924
- this.sweepProperty.isDisabled = !this.useSweep;
932
+ if (!this.useSweep)
925
933
  syncData.push(this.sweepProperty.syncItem);
926
- }
927
934
  if (0 !== syncData.length)
928
935
  this.syncToolSettingsProperties(syncData);
929
936
  }
930
- syncRadiusState() {
931
- this.radiusProperty.displayValue = this.radiusProperty.description.format(this.radius);
932
- this.radiusProperty.isDisabled = !this.useRadius;
933
- this.syncToolSettingsProperties([this.radiusProperty.syncItem]);
934
- }
935
- syncSweepState() {
936
- this.sweepProperty.displayValue = this.sweepProperty.description.format(this.sweep);
937
- this.sweepProperty.isDisabled = !this.useSweep;
938
- this.syncToolSettingsProperties([this.sweepProperty.syncItem]);
937
+ getToolSettingPropertyLocked(property) {
938
+ if (property === this.useRadiusProperty)
939
+ return this.radiusProperty;
940
+ else if (property === this.useSweepProperty)
941
+ return this.sweepProperty;
942
+ return undefined;
939
943
  }
940
944
  async applyToolSettingPropertyChange(updatedValue) {
941
- if (this.methodProperty.name === updatedValue.propertyName) {
942
- this.methodProperty.value = updatedValue.value.value;
943
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.methodProperty.item);
945
+ if (!this.changeToolSettingPropertyValue(updatedValue))
946
+ return false;
947
+ if (this.methodProperty.name === updatedValue.propertyName)
944
948
  await this.onReinitialize();
945
- return true;
946
- }
947
- else if (updatedValue.propertyName === this.useRadiusProperty.name) {
948
- this.useRadius = updatedValue.value.value;
949
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.useRadiusProperty.item);
950
- this.syncRadiusState();
951
- return true;
952
- }
953
- else if (updatedValue.propertyName === this.useSweepProperty.name) {
954
- this.useSweep = updatedValue.value.value;
955
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.useSweepProperty.item);
956
- this.syncSweepState();
957
- return true;
958
- }
959
- else if (updatedValue.propertyName === this.radiusProperty.name) {
960
- if (!updatedValue.value.value) {
961
- this.syncRadiusState(); // force UI to redisplay last valid value
962
- return false;
963
- }
964
- this.radius = updatedValue.value.value;
965
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.radiusProperty.item);
966
- // If radius is changed when creating arc by start/center after center has been defined, back up a step to defined a new center point...
967
- if (ArcMethod.StartCenter === this.method && this.useRadius && 2 === this.accepted.length)
968
- await this.onUndoPreviousStep();
969
- return true;
970
- }
971
- else if (updatedValue.propertyName === this.sweepProperty.name) {
972
- if (!updatedValue.value.value) {
973
- this.syncSweepState(); // force UI to redisplay last valid value
974
- return false;
975
- }
976
- this.sweep = updatedValue.value.value;
977
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.sweepProperty.item);
978
- return true;
979
- }
980
- return false;
949
+ else if (updatedValue.propertyName === this.radiusProperty.name && ArcMethod.StartCenter === this.method && this.useRadius && 2 === this.accepted.length)
950
+ await this.onUndoPreviousStep(); // If radius is changed when creating arc by start/center after center has been defined, back up a step to defined a new center point...
951
+ return true;
981
952
  }
982
953
  supplyToolSettingsProperties() {
983
954
  const toolSettings = new Array();
@@ -1002,23 +973,9 @@ export class CreateArcTool extends CreateOrContinuePathTool {
1002
973
  if (!await super.onInstall())
1003
974
  return false;
1004
975
  // Setup initial values here instead of supplyToolSettingsProperties to support keyin args w/o appui-react...
1005
- const methodValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.methodProperty.name);
1006
- if (undefined !== methodValue)
1007
- this.methodProperty.dialogItemValue = methodValue;
1008
- const radiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.radiusProperty.name);
1009
- if (undefined !== radiusValue)
1010
- this.radiusProperty.dialogItemValue = radiusValue;
1011
- const useRadiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.useRadiusProperty.name);
1012
- if (undefined !== useRadiusValue)
1013
- this.useRadiusProperty.dialogItemValue = useRadiusValue;
976
+ this.initializeToolSettingPropertyValues([this.methodProperty, this.radiusProperty, this.useRadiusProperty, this.sweepProperty, this.useSweepProperty]);
1014
977
  if (!this.radius)
1015
978
  this.useRadius = false;
1016
- const useSweepValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.useSweepProperty.name);
1017
- if (undefined !== useSweepValue)
1018
- this.useSweepProperty.dialogItemValue = useSweepValue;
1019
- const sweepValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.sweepProperty.name);
1020
- if (undefined !== sweepValue)
1021
- this.sweepProperty.dialogItemValue = sweepValue;
1022
979
  if (!this.sweep)
1023
980
  this.useSweep = false;
1024
981
  return true;
@@ -1070,16 +1027,16 @@ export class CreateArcTool extends CreateOrContinuePathTool {
1070
1027
  }
1071
1028
  // Update current session values so keyin args are picked up for tool settings/restart...
1072
1029
  if (undefined !== arcMethod)
1073
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.methodProperty.name, value: { value: arcMethod } });
1030
+ this.saveToolSettingPropertyValue(this.methodProperty, { value: arcMethod });
1074
1031
  if (undefined !== arcRadius) {
1075
1032
  if (0.0 !== arcRadius)
1076
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.radiusProperty.name, value: { value: arcRadius } });
1077
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.useRadiusProperty.name, value: { value: 0.0 !== arcRadius } });
1033
+ this.saveToolSettingPropertyValue(this.radiusProperty, { value: arcRadius });
1034
+ this.saveToolSettingPropertyValue(this.useRadiusProperty, { value: 0.0 !== arcRadius });
1078
1035
  }
1079
1036
  if (undefined !== arcSweep) {
1080
1037
  if (0.0 !== arcSweep)
1081
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.sweepProperty.name, value: { value: arcSweep } });
1082
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.useSweepProperty.name, value: { value: 0.0 !== arcSweep } });
1038
+ this.saveToolSettingPropertyValue(this.sweepProperty, { value: arcSweep });
1039
+ this.saveToolSettingPropertyValue(this.useSweepProperty, { value: 0.0 !== arcSweep });
1083
1040
  }
1084
1041
  return this.run();
1085
1042
  }
@@ -1189,56 +1146,30 @@ export class CreateCircleTool extends CreateOrContinuePathTool {
1189
1146
  vector90.scaleToLength(radius, vector90);
1190
1147
  return Arc3d.create(center, vector0, vector90);
1191
1148
  }
1192
- async onResetButtonUp(ev) {
1149
+ async cancelPoint(_ev) {
1193
1150
  if (CircleMethod.Center === this.method && this.useRadius) {
1194
1151
  // Exit instead of restarting to avoid having circle "stuck" on cursor...
1195
1152
  await this.exitTool();
1196
- return EventHandled.Yes;
1153
+ return false;
1197
1154
  }
1198
- return super.onResetButtonUp(ev);
1155
+ return true;
1199
1156
  }
1200
1157
  syncToolSettingsRadius() {
1201
1158
  if (this.useRadius)
1202
1159
  return;
1203
- const syncData = [];
1204
- if (!this.useRadius) {
1205
- this.radiusProperty.displayValue = this.radiusProperty.description.format(this.radius);
1206
- this.radiusProperty.isDisabled = !this.useRadius;
1207
- syncData.push(this.radiusProperty.syncItem);
1208
- }
1209
- if (0 !== syncData.length)
1210
- this.syncToolSettingsProperties(syncData);
1211
- }
1212
- syncRadiusState() {
1213
- this.radiusProperty.displayValue = this.radiusProperty.description.format(this.radius);
1214
- this.radiusProperty.isDisabled = !this.useRadius;
1215
1160
  this.syncToolSettingsProperties([this.radiusProperty.syncItem]);
1216
1161
  }
1162
+ getToolSettingPropertyLocked(property) {
1163
+ return (property === this.useRadiusProperty ? this.radiusProperty : undefined);
1164
+ }
1217
1165
  async applyToolSettingPropertyChange(updatedValue) {
1218
- if (this.methodProperty.name === updatedValue.propertyName) {
1219
- this.methodProperty.value = updatedValue.value.value;
1220
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.methodProperty.item);
1166
+ if (!this.changeToolSettingPropertyValue(updatedValue))
1167
+ return false;
1168
+ if (this.methodProperty.name === updatedValue.propertyName)
1221
1169
  await this.onReinitialize();
1222
- return true;
1223
- }
1224
- else if (updatedValue.propertyName === this.useRadiusProperty.name) {
1225
- this.useRadius = updatedValue.value.value;
1226
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.useRadiusProperty.item);
1227
- this.syncRadiusState();
1228
- if (CircleMethod.Center === this.method && this.useRadius && 0 === this.accepted.length)
1229
- await this.onReinitialize();
1230
- return true;
1231
- }
1232
- else if (updatedValue.propertyName === this.radiusProperty.name) {
1233
- if (!updatedValue.value.value) {
1234
- this.syncRadiusState(); // force UI to redisplay last valid value
1235
- return false;
1236
- }
1237
- this.radius = updatedValue.value.value;
1238
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.radiusProperty.item);
1239
- return true;
1240
- }
1241
- return false;
1170
+ else if (updatedValue.propertyName === this.useRadiusProperty.name && CircleMethod.Center === this.method && this.useRadius && 0 === this.accepted.length)
1171
+ await this.onReinitialize();
1172
+ return true;
1242
1173
  }
1243
1174
  supplyToolSettingsProperties() {
1244
1175
  const toolSettings = new Array();
@@ -1278,15 +1209,7 @@ export class CreateCircleTool extends CreateOrContinuePathTool {
1278
1209
  if (!await super.onInstall())
1279
1210
  return false;
1280
1211
  // Setup initial values here instead of supplyToolSettingsProperties to support keyin args w/o appui-react...
1281
- const methodValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.methodProperty.name);
1282
- if (undefined !== methodValue)
1283
- this.methodProperty.dialogItemValue = methodValue;
1284
- const radiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.radiusProperty.name);
1285
- if (undefined !== radiusValue)
1286
- this.radiusProperty.dialogItemValue = radiusValue;
1287
- const useRadiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.useRadiusProperty.name);
1288
- if (undefined !== useRadiusValue)
1289
- this.useRadiusProperty.dialogItemValue = useRadiusValue;
1212
+ this.initializeToolSettingPropertyValues([this.methodProperty, this.radiusProperty, this.useRadiusProperty]);
1290
1213
  if (!this.radius)
1291
1214
  this.useRadius = false;
1292
1215
  return true;
@@ -1324,11 +1247,11 @@ export class CreateCircleTool extends CreateOrContinuePathTool {
1324
1247
  }
1325
1248
  // Update current session values so keyin args are picked up for tool settings/restart...
1326
1249
  if (undefined !== circleMethod)
1327
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.methodProperty.name, value: { value: circleMethod } });
1250
+ this.saveToolSettingPropertyValue(this.methodProperty, { value: circleMethod });
1328
1251
  if (undefined !== circleRadius) {
1329
1252
  if (0.0 !== circleRadius)
1330
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.radiusProperty.name, value: { value: circleRadius } });
1331
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.useRadiusProperty.name, value: { value: 0.0 !== circleRadius } });
1253
+ this.saveToolSettingPropertyValue(this.radiusProperty, { value: circleRadius });
1254
+ this.saveToolSettingPropertyValue(this.useRadiusProperty, { value: 0.0 !== circleRadius });
1332
1255
  }
1333
1256
  return this.run();
1334
1257
  }
@@ -1481,27 +1404,11 @@ export class CreateRectangleTool extends CreateOrContinuePathTool {
1481
1404
  return;
1482
1405
  return { format: "flatbuffer", data: builder.entries };
1483
1406
  }
1484
- syncRadiusState() {
1485
- this.radiusProperty.displayValue = this.radiusProperty.description.format(this.radius);
1486
- this.radiusProperty.isDisabled = !this.useRadius;
1487
- this.syncToolSettingsProperties([this.radiusProperty.syncItem]);
1407
+ getToolSettingPropertyLocked(property) {
1408
+ return (property === this.useRadiusProperty ? this.radiusProperty : undefined);
1488
1409
  }
1489
1410
  async applyToolSettingPropertyChange(updatedValue) {
1490
- if (updatedValue.propertyName === this.useRadiusProperty.name) {
1491
- this.useRadius = updatedValue.value.value;
1492
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.useRadiusProperty.item);
1493
- this.syncRadiusState();
1494
- }
1495
- else if (updatedValue.propertyName === this.radiusProperty.name) {
1496
- if (!updatedValue.value.value) {
1497
- this.syncRadiusState(); // force UI to redisplay last valid value
1498
- return false;
1499
- }
1500
- this.radius = updatedValue.value.value;
1501
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.radiusProperty.item);
1502
- return true;
1503
- }
1504
- return false;
1411
+ return this.changeToolSettingPropertyValue(updatedValue);
1505
1412
  }
1506
1413
  supplyToolSettingsProperties() {
1507
1414
  const toolSettings = new Array();
@@ -1520,12 +1427,7 @@ export class CreateRectangleTool extends CreateOrContinuePathTool {
1520
1427
  if (!await super.onInstall())
1521
1428
  return false;
1522
1429
  // Setup initial values here instead of supplyToolSettingsProperties to support keyin args w/o appui-react...
1523
- const radiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.radiusProperty.name);
1524
- if (undefined !== radiusValue)
1525
- this.radiusProperty.dialogItemValue = radiusValue;
1526
- const useRadiusValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.useRadiusProperty.name);
1527
- if (undefined !== useRadiusValue)
1528
- this.useRadiusProperty.dialogItemValue = useRadiusValue;
1430
+ this.initializeToolSettingPropertyValues([this.radiusProperty, this.useRadiusProperty]);
1529
1431
  if (!this.radius)
1530
1432
  this.useRadius = false;
1531
1433
  return true;
@@ -1549,8 +1451,8 @@ export class CreateRectangleTool extends CreateOrContinuePathTool {
1549
1451
  // Update current session values so keyin args are picked up for tool settings/restart...
1550
1452
  if (undefined !== cornerRadius) {
1551
1453
  if (0.0 !== cornerRadius)
1552
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.radiusProperty.name, value: { value: cornerRadius } });
1553
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.useRadiusProperty.name, value: { value: 0.0 !== cornerRadius } });
1454
+ this.saveToolSettingPropertyValue(this.radiusProperty, { value: cornerRadius });
1455
+ this.saveToolSettingPropertyValue(this.useRadiusProperty, { value: 0.0 !== cornerRadius });
1554
1456
  }
1555
1457
  return this.run();
1556
1458
  }
@@ -1565,13 +1467,24 @@ export var BCurveMethod;
1565
1467
  })(BCurveMethod || (BCurveMethod = {}));
1566
1468
  /** @alpha Creates a bspline curve by poles or through points. Uses model and category from ToolAdmin.ActiveSettings. */
1567
1469
  export class CreateBCurveTool extends CreateOrContinuePathTool {
1470
+ constructor() {
1471
+ super(...arguments);
1472
+ this._isPhysicallyClosedOrComplete = false;
1473
+ this._tangentPhase = CreateCurvePhase.DefineOther;
1474
+ }
1475
+ static get minArgs() { return 0; }
1476
+ static get maxArgs() { return 3; } // method, order, tangents...
1568
1477
  get wantPickableDynamics() { return true; } // Allow snapping to control polygon or through points...
1569
1478
  get showCurveConstructions() { return true; } // Display control polygon or through points...
1570
1479
  provideToolAssistance(_mainInstrText, _additionalInstr) {
1571
1480
  const nPts = this.accepted.length;
1572
- const mainMsg = CoreTools.translate(0 === nPts ? "ElementSet.Prompts.StartPoint" : (1 === nPts ? "ElementSet.Prompts.EndPoint" : "ElementSet.Inputs.AdditionalPoint"));
1481
+ let mainMsg;
1482
+ if (CreateCurvePhase.DefineOther === this._tangentPhase)
1483
+ mainMsg = CoreTools.translate(0 === nPts ? "ElementSet.Prompts.StartPoint" : "ElementSet.Inputs.AdditionalPoint");
1484
+ else
1485
+ mainMsg = EditTools.translate(CreateCurvePhase.DefineStart === this._tangentPhase ? "CreateBCurve.Prompts.StartTangent" : "CreateBCurve.Prompts.EndTangent");
1573
1486
  const leftMsg = CoreTools.translate("ElementSet.Inputs.AcceptPoint");
1574
- const rightMsg = CoreTools.translate(nPts > 1 ? "ElementSet.Inputs.Complete" : "ElementSet.Inputs.Cancel");
1487
+ const rightMsg = CoreTools.translate(CreateCurvePhase.DefineOther === this._tangentPhase && nPts >= this.requiredPointCount ? "ElementSet.Inputs.Complete" : "ElementSet.Inputs.Cancel");
1575
1488
  const mouseInstructions = [];
1576
1489
  const touchInstructions = [];
1577
1490
  if (!ToolAssistance.createTouchCursorInstructions(touchInstructions))
@@ -1603,43 +1516,97 @@ export class CreateBCurveTool extends CreateOrContinuePathTool {
1603
1516
  set order(value) { this.orderProperty.value = value; }
1604
1517
  get minOrder() { return 2; }
1605
1518
  get maxOrder() { return 16; }
1519
+ get tangentsProperty() {
1520
+ if (!this._tangentsProperty)
1521
+ this._tangentsProperty = new DialogProperty(PropertyDescriptionHelper.buildToggleDescription("bcurveTangents", EditTools.translate("CreateBCurve.Label.Tangents")), false);
1522
+ return this._tangentsProperty;
1523
+ }
1524
+ get tangents() { return this.tangentsProperty.value; }
1525
+ set tangents(value) { this.tangentsProperty.value = value; }
1606
1526
  get wantClosure() {
1607
1527
  // A bcurve can support physical closure when creating a new path...
1608
1528
  return this.allowClosure;
1609
1529
  }
1530
+ get requiredPointCount() {
1531
+ if (BCurveMethod.ThroughPoints === this.method)
1532
+ return 3; // Interpolation curve is always order 4 with 3 point minimum...
1533
+ return this.order;
1534
+ }
1535
+ get createCurvePhase() {
1536
+ if (CreateCurvePhase.DefineOther !== this._tangentPhase)
1537
+ return CreateCurvePhase.DefineOther;
1538
+ return super.createCurvePhase;
1539
+ }
1610
1540
  isComplete(ev) {
1611
1541
  // Accept on reset with sufficient points...
1612
1542
  if (BeButton.Reset === ev.button)
1613
- return (this.accepted.length >= this.order);
1543
+ return (this.accepted.length >= this.requiredPointCount);
1614
1544
  // Allow data to complete on physical closure...
1615
- return this.isClosed;
1545
+ return this.isClosed || this._isPhysicallyClosedOrComplete;
1546
+ }
1547
+ showConstructionGraphics(ev, context) {
1548
+ if (CreateCurvePhase.DefineOther !== this._tangentPhase && this.current) {
1549
+ const fitCurve = this.current;
1550
+ const builder = context.createGraphic({ type: GraphicType.WorldOverlay });
1551
+ const color = context.viewport.getContrastToBackgroundColor();
1552
+ builder.setSymbology(color, ColorDef.black, 1, LinePixels.Code2);
1553
+ builder.addLineString([ev.point, fitCurve.options.fitPoints[CreateCurvePhase.DefineStart === this._tangentPhase ? 0 : fitCurve.options.fitPoints.length - 1]]);
1554
+ builder.setSymbology(color, ColorDef.black, 8);
1555
+ builder.addPointString([ev.point]);
1556
+ context.addGraphic(builder.finish());
1557
+ }
1558
+ return super.showConstructionGraphics(ev, context);
1616
1559
  }
1617
1560
  createNewCurvePrimitive(ev, isDynamics) {
1561
+ if (CreateCurvePhase.DefineOther !== this._tangentPhase && this.current) {
1562
+ const fitCurve = this.current;
1563
+ if (CreateCurvePhase.DefineStart === this._tangentPhase) {
1564
+ const tangentS = Vector3d.createStartEnd(ev.point, fitCurve.options.fitPoints[0]);
1565
+ if (tangentS.magnitude() > Geometry.smallMetricDistance)
1566
+ fitCurve.options.startTangent = tangentS;
1567
+ }
1568
+ else {
1569
+ const tangentE = Vector3d.createStartEnd(ev.point, fitCurve.options.fitPoints[fitCurve.options.fitPoints.length - 1]);
1570
+ if (tangentE.magnitude() > Geometry.smallMetricDistance)
1571
+ fitCurve.options.endTangent = tangentE;
1572
+ }
1573
+ return fitCurve;
1574
+ }
1618
1575
  // Don't include current point if it's the same as the last accepted point, want dynamics to show an accurate preview of what reset will accept...
1619
1576
  const includeCurrPt = (isDynamics && (0 === this.accepted.length || !ev.point.isAlmostEqual(this.accepted[this.accepted.length - 1])));
1620
1577
  const pts = (includeCurrPt ? [...this.accepted, ev.point] : this.accepted);
1621
- const numRequired = this.order;
1578
+ const numRequired = this.requiredPointCount;
1622
1579
  if (pts.length < numRequired) {
1623
1580
  // Create point/linestring construction geometry to support join...
1624
1581
  this.isConstruction = true;
1625
1582
  return LineString3d.create(1 === pts.length ? [pts[0], pts[0]] : pts);
1626
1583
  }
1627
- // TODO: Support physical closure by creating closed/rational bcurve...
1628
- if (BCurveMethod.ControlPoints === this.method)
1584
+ // Create periodic-looking curve on physical closure with sufficient points even when not creating a loop/surface...
1585
+ this._isPhysicallyClosedOrComplete = (undefined === this.continuationData && pts[0].isAlmostEqual(pts[pts.length - 1]));
1586
+ if (BCurveMethod.ControlPoints === this.method) {
1587
+ if (this._isPhysicallyClosedOrComplete && this.order > 2) {
1588
+ const tmpPts = pts.slice(undefined, -1); // Don't include closure point...
1589
+ return BSplineCurve3d.createPeriodicUniformKnots(tmpPts, this.order);
1590
+ }
1629
1591
  return BSplineCurve3d.createUniformKnots(pts, this.order);
1630
- // TODO: InterpolationCurve3d, set end tangents using continuation curve...self-closure, etc.
1631
- return BSplineCurveOps.createThroughPoints(pts, this.order);
1632
- }
1633
- addConstructionGraphics(curve, showCurve, context) {
1634
- // TODO: Need proper Interpolation curve class to extract/show through points...
1635
- if (BCurveMethod.ThroughPoints === this.method && !showCurve && 0 !== this.accepted.length) {
1636
- const builder = context.createGraphic({ type: GraphicType.WorldOverlay });
1637
- builder.setSymbology(context.viewport.getContrastToBackgroundColor(), ColorDef.black, 5);
1638
- builder.addPointString(this.accepted);
1639
- context.addGraphic(builder.finish());
1640
- return;
1641
1592
  }
1642
- return super.addConstructionGraphics(curve, showCurve, context);
1593
+ const interpProps = { fitPoints: pts, closed: this._isPhysicallyClosedOrComplete, isChordLenKnots: 1, isColinearTangents: 1 };
1594
+ // Create interpolation curve tangent to continuation curve...
1595
+ if (undefined !== this.continuationData && this.tangents) {
1596
+ const tangentS = this.continuationData.path.children[0].fractionToPointAndUnitTangent(0.0);
1597
+ const tangentE = this.continuationData.path.children[this.continuationData.path.children.length - 1].fractionToPointAndUnitTangent(1.0);
1598
+ if (pts[0].isAlmostEqual(tangentS.origin))
1599
+ interpProps.startTangent = tangentS.direction.scale(-1);
1600
+ else if (pts[0].isAlmostEqual(tangentE.origin))
1601
+ interpProps.startTangent = tangentE.direction;
1602
+ if (pts[pts.length - 1].isAlmostEqual(tangentS.origin))
1603
+ interpProps.endTangent = tangentS.direction.scale(-1);
1604
+ else if (pts[pts.length - 1].isAlmostEqual(tangentE.origin))
1605
+ interpProps.endTangent = tangentE.direction;
1606
+ this._isPhysicallyClosedOrComplete = (undefined !== interpProps.startTangent && undefined !== interpProps.endTangent);
1607
+ }
1608
+ const interpOpts = InterpolationCurve3dOptions.create(interpProps);
1609
+ return InterpolationCurve3d.createCapture(interpOpts);
1643
1610
  }
1644
1611
  getSnapGeometry() {
1645
1612
  // Only snap to through points...
@@ -1647,40 +1614,63 @@ export class CreateBCurveTool extends CreateOrContinuePathTool {
1647
1614
  return (this.accepted.length > 1 ? PointString3d.create(this.accepted) : undefined);
1648
1615
  return super.getSnapGeometry();
1649
1616
  }
1650
- async onResetButtonUp(ev) {
1617
+ async acceptPoint(ev) {
1618
+ switch (this._tangentPhase) {
1619
+ case CreateCurvePhase.DefineOther:
1620
+ return super.acceptPoint(ev);
1621
+ case CreateCurvePhase.DefineStart:
1622
+ this._tangentPhase = CreateCurvePhase.DefineEnd;
1623
+ break;
1624
+ case CreateCurvePhase.DefineEnd:
1625
+ this._isPhysicallyClosedOrComplete = true;
1626
+ break;
1627
+ }
1628
+ await this.updateCurveAndContinuationData(ev, false, CreateCurvePhase.DefineOther);
1629
+ return true;
1630
+ }
1631
+ async cancelPoint(ev) {
1651
1632
  // NOTE: Starting another tool will not create element...require reset or closure...
1652
1633
  if (this.isComplete(ev)) {
1634
+ if (BCurveMethod.ThroughPoints === this.method && this.tangents && this.current) {
1635
+ const fitCurve = this.current;
1636
+ switch (this._tangentPhase) {
1637
+ case CreateCurvePhase.DefineOther:
1638
+ await this.updateCurveAndContinuationData(ev, false, CreateCurvePhase.DefineEnd);
1639
+ this._tangentPhase = (undefined === fitCurve.options.startTangent ? CreateCurvePhase.DefineStart : CreateCurvePhase.DefineEnd);
1640
+ IModelApp.toolAdmin.updateDynamics();
1641
+ this.setupAndPromptForNextAction();
1642
+ return false;
1643
+ case CreateCurvePhase.DefineStart:
1644
+ fitCurve.options.startTangent = undefined; // Not accepted, compute default start tangent...
1645
+ this._tangentPhase = CreateCurvePhase.DefineEnd;
1646
+ IModelApp.toolAdmin.updateDynamics();
1647
+ this.setupAndPromptForNextAction();
1648
+ return false;
1649
+ case CreateCurvePhase.DefineEnd:
1650
+ fitCurve.options.endTangent = undefined; // Not accepted, compute default end tangent...
1651
+ await this.createElement();
1652
+ return true;
1653
+ }
1654
+ }
1653
1655
  await this.updateCurveAndContinuationData(ev, false, CreateCurvePhase.DefineEnd);
1654
1656
  await this.createElement();
1655
1657
  }
1656
- return super.onResetButtonUp(ev);
1657
- }
1658
- syncOrderState() {
1659
- this.orderProperty.displayValue = this.orderProperty.description.format(this.order);
1660
- this.syncToolSettingsProperties([this.orderProperty.syncItem]);
1658
+ return true;
1661
1659
  }
1662
1660
  async applyToolSettingPropertyChange(updatedValue) {
1663
- if (this.methodProperty.name === updatedValue.propertyName) {
1664
- this.methodProperty.value = updatedValue.value.value;
1665
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.methodProperty.item);
1661
+ if (!this.changeToolSettingPropertyValue(updatedValue))
1662
+ return false;
1663
+ if (this.methodProperty.name === updatedValue.propertyName)
1666
1664
  await this.onReinitialize();
1667
- return true;
1668
- }
1669
- else if (updatedValue.propertyName === this.orderProperty.name) {
1670
- if (!updatedValue.value.value) {
1671
- this.syncOrderState(); // force UI to redisplay last valid value
1672
- return false;
1673
- }
1674
- this.order = updatedValue.value.value;
1675
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, this.orderProperty.item);
1676
- return true;
1677
- }
1678
- return false;
1665
+ return true;
1679
1666
  }
1680
1667
  supplyToolSettingsProperties() {
1681
1668
  const toolSettings = new Array();
1682
1669
  toolSettings.push(this.methodProperty.toDialogItem({ rowPriority: 1, columnIndex: 0 }));
1683
- toolSettings.push(this.orderProperty.toDialogItem({ rowPriority: 2, columnIndex: 1 }));
1670
+ if (BCurveMethod.ThroughPoints === this.method)
1671
+ toolSettings.push(this.tangentsProperty.toDialogItem({ rowPriority: 2, columnIndex: 0 }));
1672
+ else
1673
+ toolSettings.push(this.orderProperty.toDialogItem({ rowPriority: 2, columnIndex: 1 }));
1684
1674
  return toolSettings;
1685
1675
  }
1686
1676
  async onRestartTool() {
@@ -1692,21 +1682,18 @@ export class CreateBCurveTool extends CreateOrContinuePathTool {
1692
1682
  if (!await super.onInstall())
1693
1683
  return false;
1694
1684
  // Setup initial values here instead of supplyToolSettingsProperties to support keyin args w/o appui-react...
1695
- const methodValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.methodProperty.name);
1696
- if (undefined !== methodValue)
1697
- this.methodProperty.dialogItemValue = methodValue;
1698
- const orderValue = IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, this.orderProperty.name);
1699
- if (undefined !== orderValue)
1700
- this.orderProperty.dialogItemValue = orderValue;
1685
+ this.initializeToolSettingPropertyValues([this.methodProperty, this.orderProperty, this.tangentsProperty]);
1701
1686
  return true;
1702
1687
  }
1703
1688
  /** The keyin takes the following arguments, all of which are optional:
1704
1689
  * - `method=0|1` How bcurve will be defined. 0 for control points, 1 for through points.
1705
1690
  * - `order=number` bcurve order from 2 to 16.
1691
+ * - 'tangents=0|1 Whether to specify start/end tangents for through points construction.
1706
1692
  */
1707
1693
  async parseAndRun(...inputArgs) {
1708
1694
  let bcurveMethod;
1709
1695
  let bcurveOrder;
1696
+ let bcurveTangents;
1710
1697
  for (const arg of inputArgs) {
1711
1698
  const parts = arg.split("=");
1712
1699
  if (2 !== parts.length)
@@ -1730,12 +1717,18 @@ export class CreateBCurveTool extends CreateOrContinuePathTool {
1730
1717
  bcurveOrder = order;
1731
1718
  }
1732
1719
  }
1720
+ else if (parts[0].toLowerCase().startsWith("ta")) {
1721
+ const tangents = Number.parseInt(parts[1], 10);
1722
+ bcurveTangents = (0 !== tangents);
1723
+ }
1733
1724
  }
1734
1725
  // Update current session values so keyin args are picked up for tool settings/restart...
1735
1726
  if (undefined !== bcurveMethod)
1736
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.methodProperty.name, value: { value: bcurveMethod } });
1727
+ this.saveToolSettingPropertyValue(this.methodProperty, { value: bcurveMethod });
1737
1728
  if (undefined !== bcurveOrder)
1738
- IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: this.orderProperty.name, value: { value: bcurveOrder } });
1729
+ this.saveToolSettingPropertyValue(this.orderProperty, { value: bcurveOrder });
1730
+ if (undefined !== bcurveTangents)
1731
+ this.saveToolSettingPropertyValue(this.tangentsProperty, { value: bcurveTangents });
1739
1732
  return this.run();
1740
1733
  }
1741
1734
  }