@itwin/editor-frontend 3.0.0-dev.135 → 3.0.0-dev.140

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.
@@ -5,10 +5,10 @@
5
5
  /** @packageDocumentation
6
6
  * @module Editing
7
7
  */
8
- import { BooleanMode, ChamferMode, CutDepthMode, CutDirectionMode, EmbossDirectionMode, ProfileClosure, SubEntityType } from "@itwin/editor-common";
8
+ import { BooleanMode, BRepEntityType, ChamferMode, CutDepthMode, CutDirectionMode, EmbossDirectionMode, ProfileClosure, SubEntityType } from "@itwin/editor-common";
9
9
  import { ColorDef, ElementGeometry } from "@itwin/core-common";
10
- import { Angle, Geometry, LineString3d, Point3d, Vector3d } from "@itwin/core-geometry";
11
- import { AccuDrawHintBuilder, EventHandled, GraphicType } from "@itwin/core-frontend";
10
+ import { Angle, Geometry, LineString3d, Matrix3d, Point3d, Vector3d } from "@itwin/core-geometry";
11
+ import { AccuDraw, AccuDrawHintBuilder, EventHandled, GraphicType, TentativeOrAccuSnap } from "@itwin/core-frontend";
12
12
  import { computeChordToleranceFromPoint } from "./CreateElementTool";
13
13
  import { ElementGeometryCacheTool, isSameSubEntity, LocateSubEntityTool, SubEntityData } from "./ElementGeometryTool";
14
14
  /** @alpha Base class for tools that perform boolean operations on a set of elements. */
@@ -675,6 +675,7 @@ export class ImprintSolidElementsTool extends LocateSubEntityTool {
675
675
  this.points.push(ev.point.clone());
676
676
  if (!ev.isControlKey)
677
677
  break;
678
+ this.setupAndPromptForNextAction();
678
679
  return EventHandled.No;
679
680
  }
680
681
  default:
@@ -715,7 +716,7 @@ export class ImprintSolidElementsTool extends LocateSubEntityTool {
715
716
  }
716
717
  ImprintSolidElementsTool.toolId = "ImprintSolids";
717
718
  ImprintSolidElementsTool.iconSpec = "icon-move"; // TODO: Need better icon...
718
- /** @alpha Identify edges of solids and surfaces to apply blend to. */
719
+ /** @alpha Base class for tools to identify edges of solids and surfaces and apply blends. */
719
720
  export class BlendEdgesTool extends LocateSubEntityTool {
720
721
  constructor() {
721
722
  super(...arguments);
@@ -862,39 +863,81 @@ export class ChamferEdgesTool extends BlendEdgesTool {
862
863
  }
863
864
  ChamferEdgesTool.toolId = "ChamferEdges";
864
865
  ChamferEdgesTool.iconSpec = "icon-move"; // TODO: Need better icon...
865
- /** @alpha Identify faces of solids and surfaces to translate. */
866
- export class SweepFacesTool extends LocateSubEntityTool {
867
- constructor() {
868
- super(...arguments);
869
- // TODO: Tool settings for sweep distance...
870
- this.useDistance = false;
871
- this.distance = 0.1;
872
- this.addSmoothFaces = false;
866
+ /** @alpha */
867
+ export class ProfileLocationData {
868
+ constructor(point, orientation) {
869
+ this.point = point;
870
+ this.orientation = orientation;
871
+ }
872
+ }
873
+ /** @alpha Base class for picking profiles (open paths and regions) or faces of solids. */
874
+ export class LocateFaceOrProfileTool extends LocateSubEntityTool {
875
+ get wantGeometrySummary() { return true; }
876
+ wantSubEntityType(type) {
877
+ switch (type) {
878
+ case SubEntityType.Face:
879
+ case SubEntityType.Edge:
880
+ // Choose all faces or all edges...
881
+ return (0 === this._acceptedSubEntities.length || this._acceptedSubEntities[0].props.type === type);
882
+ default:
883
+ return false;
884
+ }
873
885
  }
874
- get wantDynamics() { return true; }
875
- get wantAccuSnap() { return this.isDynamicsStarted; }
876
- // TODO: Sweep open path to surface, sweep surface to solid...
877
886
  get geometryCacheFilter() {
878
- return { parts: true, curves: false, surfaces: true, solids: true, other: false };
887
+ return { parts: true, curves: true, surfaces: true, solids: true, other: false };
888
+ }
889
+ async doPickSubEntities(id, ev) {
890
+ const hits = await super.doPickSubEntities(id, ev);
891
+ if (undefined === hits)
892
+ return hits;
893
+ // Only want edges from wire bodies...
894
+ const accept = (BRepEntityType.Wire === this.getBRepEntityTypeForSubEntity(id, hits[0].subEntity) ? SubEntityType.Edge : SubEntityType.Face);
895
+ return hits.filter((hit) => { return accept === hit.subEntity.type; });
879
896
  }
880
897
  async createSubEntityData(id, hit) {
881
898
  const data = await super.createSubEntityData(id, hit);
882
- if (undefined !== hit.point && undefined !== hit.normal)
883
- data.toolData = FaceLocationData.create(hit.point, hit.normal);
899
+ // Prefer orientation from snap to take entire path curve as well as placement z into account...
900
+ const snap = TentativeOrAccuSnap.getCurrentSnap(false);
901
+ const point = (id === (snap === null || snap === void 0 ? void 0 : snap.sourceId) && snap.isHot ? snap.snapPoint : Point3d.fromJSON(hit.point));
902
+ const matrix = (id === (snap === null || snap === void 0 ? void 0 : snap.sourceId) ? AccuDraw.getSnapRotation(snap, undefined) : undefined);
903
+ const invMatrix = matrix === null || matrix === void 0 ? void 0 : matrix.inverse(); // getSnapRotation returns row matrix...
904
+ if (undefined !== invMatrix)
905
+ data.toolData = new ProfileLocationData(point, invMatrix);
906
+ else
907
+ data.toolData = new ProfileLocationData(point, undefined !== hit.normal ? Vector3d.fromJSON(hit.normal) : Vector3d.unitZ());
884
908
  return data;
885
909
  }
910
+ drawSubEntity(context, subEntity, accepted) {
911
+ const id = this.getCurrentElement();
912
+ if (undefined !== id && BRepEntityType.Solid !== this.getBRepEntityTypeForSubEntity(id, subEntity.props))
913
+ return; // Operation will be applied to wire or sheet body, don't display sub-entity...
914
+ super.drawSubEntity(context, subEntity, accepted);
915
+ }
886
916
  drawAcceptedSubEntities(context) {
887
917
  var _a;
888
918
  super.drawAcceptedSubEntities(context);
889
- // Show pick point on last identified face, offset direction/distance will be computed from this location...
890
- const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
891
- if (undefined === faceData)
919
+ // Show pick point on last identified face...
920
+ const profileData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
921
+ if (undefined === profileData)
892
922
  return;
893
923
  const builder = context.createGraphic({ type: GraphicType.WorldOverlay });
894
924
  builder.setSymbology(context.viewport.getContrastToBackgroundColor(), ColorDef.black, 10);
895
- builder.addPointString([faceData.point]);
925
+ builder.addPointString([profileData.point]);
896
926
  context.addDecorationFromBuilder(builder);
897
927
  }
928
+ }
929
+ /** @alpha Identify faces of solids and surfaces to translate. */
930
+ export class SweepFacesTool extends LocateFaceOrProfileTool {
931
+ constructor() {
932
+ super(...arguments);
933
+ // TODO: Tool settings for sweep distance...
934
+ this.useDistance = false;
935
+ this.distance = 0.1;
936
+ this.addSmoothFaces = false;
937
+ }
938
+ get wantDynamics() { return true; }
939
+ get wantAccuSnap() { return true; }
940
+ get wantSubEntitySnap() { return true; }
898
941
  async getSmoothFaces(id, face) {
899
942
  try {
900
943
  // NOTE: For sweep/translation, it makes sense to limit smooth to immediately adjacent...
@@ -908,10 +951,10 @@ export class SweepFacesTool extends LocateSubEntityTool {
908
951
  var _a;
909
952
  if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities)
910
953
  return undefined;
911
- const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
912
- if (undefined === faceData)
954
+ const profileData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
955
+ if (undefined === profileData)
913
956
  return undefined;
914
- const path = Vector3d.createStartEnd(faceData.point, ev.point);
957
+ const path = Vector3d.createStartEnd(profileData.point, ev.point);
915
958
  if (this.useDistance && undefined === path.scaleToLength(this.distance, path))
916
959
  return undefined;
917
960
  if (path.magnitude() < Geometry.smallMetricDistance)
@@ -925,20 +968,24 @@ export class SweepFacesTool extends LocateSubEntityTool {
925
968
  requestId: `${this.toolId}:${id}`,
926
969
  writeChanges: isAccept ? true : undefined,
927
970
  };
928
- const faces = this.getAcceptedSubEntities();
971
+ const subEntities = this.getAcceptedSubEntities();
972
+ const params = { path };
973
+ if (SubEntityType.Edge === subEntities[0].type || BRepEntityType.Solid !== this.getBRepEntityTypeForSubEntity(id, subEntities[0])) {
974
+ return await ElementGeometryCacheTool.callCommand("sweepFaces", id, params, opts);
975
+ }
929
976
  if (this.addSmoothFaces) {
930
977
  const allSmoothFaces = [];
931
- for (const face of faces) {
978
+ for (const face of subEntities) {
932
979
  const smoothFaces = await this.getSmoothFaces(id, face);
933
980
  if (undefined !== smoothFaces)
934
981
  allSmoothFaces.push(...smoothFaces);
935
982
  }
936
983
  for (const smooth of allSmoothFaces) {
937
- if (undefined === faces.find((selected) => isSameSubEntity(selected, smooth)))
938
- faces.unshift(smooth); // Preserve last selected entry as reference face...
984
+ if (undefined === subEntities.find((selected) => isSameSubEntity(selected, smooth)))
985
+ subEntities.unshift(smooth); // Preserve last selected entry as reference face...
939
986
  }
940
987
  }
941
- const params = { faces, path };
988
+ params.faces = subEntities;
942
989
  return await ElementGeometryCacheTool.callCommand("sweepFaces", id, params, opts);
943
990
  }
944
991
  catch (err) {
@@ -949,16 +996,16 @@ export class SweepFacesTool extends LocateSubEntityTool {
949
996
  var _a;
950
997
  if (!this.haveAcceptedSubEntities)
951
998
  return;
952
- const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
953
- if (undefined === faceData)
999
+ const profileData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
1000
+ if (undefined === profileData)
954
1001
  return;
955
1002
  const hints = new AccuDrawHintBuilder();
956
1003
  hints.setOriginFixed = true;
957
1004
  hints.setLockY = true;
958
1005
  hints.setLockZ = true;
959
1006
  hints.setModeRectangular();
960
- hints.setOrigin(faceData.point);
961
- hints.setXAxis2(faceData.normal);
1007
+ hints.setOrigin(profileData.point);
1008
+ hints.setXAxis2(profileData.orientation instanceof Matrix3d ? profileData.orientation.getColumn(2) : profileData.orientation);
962
1009
  hints.sendHints(false);
963
1010
  }
964
1011
  async onRestartTool() {
@@ -970,28 +1017,16 @@ export class SweepFacesTool extends LocateSubEntityTool {
970
1017
  SweepFacesTool.toolId = "SweepFaces";
971
1018
  SweepFacesTool.iconSpec = "icon-move"; // TODO: Need better icon...
972
1019
  /** @alpha Identify faces of solids and surfaces to revolve. */
973
- export class SpinFacesTool extends LocateSubEntityTool {
1020
+ export class SpinFacesTool extends LocateFaceOrProfileTool {
974
1021
  constructor() {
975
1022
  super(...arguments);
976
- this.points = [];
977
1023
  // TODO: Tool settings for sweep angle...
978
1024
  this.sweep = Angle.piOver2Radians;
1025
+ this.points = [];
979
1026
  }
980
1027
  get wantDynamics() { return true; }
981
- get wantAccuSnap() { return this.isDynamicsStarted || (this.haveAcceptedSubEntities && !this.isControlDown); }
982
- // TODO: Spin open path to surface, spin surface to solid...
983
- get geometryCacheFilter() {
984
- return { parts: true, curves: false, surfaces: true, solids: true, other: false };
985
- }
986
- getSubEntityFilter() {
987
- return { nonPlanarFaces: true };
988
- }
989
- async createSubEntityData(id, hit) {
990
- const data = await super.createSubEntityData(id, hit);
991
- if (undefined !== hit.point && undefined !== hit.normal)
992
- data.toolData = FaceLocationData.create(hit.point, hit.normal);
993
- return data;
994
- }
1028
+ get wantAccuSnap() { return true; }
1029
+ get wantSubEntitySnap() { return true; }
995
1030
  async applyAgendaOperation(ev, isAccept) {
996
1031
  if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities || this.points.length < (isAccept ? 2 : 1))
997
1032
  return undefined;
@@ -1009,8 +1044,12 @@ export class SpinFacesTool extends LocateSubEntityTool {
1009
1044
  requestId: `${this.toolId}:${id}`,
1010
1045
  writeChanges: isAccept ? true : undefined,
1011
1046
  };
1012
- const faces = this.getAcceptedSubEntities();
1013
- const params = { faces, origin, direction, angle };
1047
+ const subEntities = this.getAcceptedSubEntities();
1048
+ const params = { origin, direction, angle };
1049
+ if (SubEntityType.Edge === subEntities[0].type || BRepEntityType.Solid !== this.getBRepEntityTypeForSubEntity(id, subEntities[0])) {
1050
+ return await ElementGeometryCacheTool.callCommand("spinFaces", id, params, opts);
1051
+ }
1052
+ params.faces = subEntities;
1014
1053
  let result = await ElementGeometryCacheTool.callCommand("spinFaces", id, params, opts);
1015
1054
  // Spun face can be used to create a pocket...retry with negative sweep...
1016
1055
  if (undefined === result) {
@@ -1035,7 +1074,7 @@ export class SpinFacesTool extends LocateSubEntityTool {
1035
1074
  super.onDynamicFrame(ev, context);
1036
1075
  }
1037
1076
  async gatherInput(ev) {
1038
- if (this.haveAcceptedSubEntities)
1077
+ if (!this.wantAdditionalSubEntities)
1039
1078
  this.points.push(ev.point.clone());
1040
1079
  return super.gatherInput(ev);
1041
1080
  }
@@ -1046,13 +1085,16 @@ export class SpinFacesTool extends LocateSubEntityTool {
1046
1085
  var _a;
1047
1086
  if (!this.haveAcceptedSubEntities || 0 !== this.points.length)
1048
1087
  return;
1049
- const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
1050
- if (undefined === faceData)
1088
+ const profileData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
1089
+ if (undefined === profileData)
1051
1090
  return;
1052
1091
  const hints = new AccuDrawHintBuilder();
1053
1092
  hints.setModeRectangular();
1054
- hints.setOrigin(faceData.point);
1055
- hints.setNormal(faceData.normal);
1093
+ hints.setOrigin(profileData.point);
1094
+ if (profileData.orientation instanceof Matrix3d)
1095
+ hints.setMatrix(profileData.orientation);
1096
+ else
1097
+ hints.setNormal(profileData.orientation);
1056
1098
  hints.sendHints(false);
1057
1099
  }
1058
1100
  async onRestartTool() {