@itwin/editor-frontend 3.0.0-dev.130 → 3.0.0-dev.131
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.
- package/lib/cjs/EditTool.d.ts.map +1 -1
- package/lib/cjs/EditTool.js +3 -0
- package/lib/cjs/EditTool.js.map +1 -1
- package/lib/cjs/ElementGeometryTool.d.ts +1 -0
- package/lib/cjs/ElementGeometryTool.d.ts.map +1 -1
- package/lib/cjs/ElementGeometryTool.js +11 -1
- package/lib/cjs/ElementGeometryTool.js.map +1 -1
- package/lib/cjs/SolidModelingTools.d.ts +50 -0
- package/lib/cjs/SolidModelingTools.d.ts.map +1 -1
- package/lib/cjs/SolidModelingTools.js +306 -4
- package/lib/cjs/SolidModelingTools.js.map +1 -1
- package/lib/esm/EditTool.d.ts.map +1 -1
- package/lib/esm/EditTool.js +4 -1
- package/lib/esm/EditTool.js.map +1 -1
- package/lib/esm/ElementGeometryTool.d.ts +1 -0
- package/lib/esm/ElementGeometryTool.d.ts.map +1 -1
- package/lib/esm/ElementGeometryTool.js +12 -2
- package/lib/esm/ElementGeometryTool.js.map +1 -1
- package/lib/esm/SolidModelingTools.d.ts +50 -0
- package/lib/esm/SolidModelingTools.d.ts.map +1 -1
- package/lib/esm/SolidModelingTools.js +303 -4
- package/lib/esm/SolidModelingTools.js.map +1 -1
- package/lib/public/locales/en/Editor.json +12 -0
- package/package.json +12 -12
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { BooleanMode, ChamferMode, CutDepthMode, CutDirectionMode, EmbossDirectionMode, ProfileClosure, SubEntityType } from "@itwin/editor-common";
|
|
9
9
|
import { ColorDef, ElementGeometry } from "@itwin/core-common";
|
|
10
|
-
import { Geometry, LineString3d, Point3d, Vector3d } from "@itwin/core-geometry";
|
|
10
|
+
import { Angle, Geometry, LineString3d, Point3d, Vector3d } from "@itwin/core-geometry";
|
|
11
11
|
import { AccuDrawHintBuilder, EventHandled, GraphicType } from "@itwin/core-frontend";
|
|
12
12
|
import { computeChordToleranceFromPoint } from "./CreateElementTool";
|
|
13
13
|
import { ElementGeometryCacheTool, isSameSubEntity, LocateSubEntityTool, SubEntityData } from "./ElementGeometryTool";
|
|
@@ -353,6 +353,13 @@ export class FaceLocationData {
|
|
|
353
353
|
}
|
|
354
354
|
/** @alpha Identify faces of solids and surfaces to offset. */
|
|
355
355
|
export class OffsetFacesTool extends LocateSubEntityTool {
|
|
356
|
+
constructor() {
|
|
357
|
+
super(...arguments);
|
|
358
|
+
// TODO: Tool settings for offset distance...
|
|
359
|
+
this.useOffset = false;
|
|
360
|
+
this.offset = 0.1;
|
|
361
|
+
this.addSmoothFaces = false;
|
|
362
|
+
}
|
|
356
363
|
get wantDynamics() { return true; }
|
|
357
364
|
get wantAccuSnap() { return this.isDynamicsStarted; }
|
|
358
365
|
get geometryCacheFilter() {
|
|
@@ -376,6 +383,15 @@ export class OffsetFacesTool extends LocateSubEntityTool {
|
|
|
376
383
|
builder.addPointString([faceData.point]);
|
|
377
384
|
context.addDecorationFromBuilder(builder);
|
|
378
385
|
}
|
|
386
|
+
async getSmoothFaces(id, face) {
|
|
387
|
+
try {
|
|
388
|
+
// NOTE: For offset, include all smoothly connected faces, not just adjacent...
|
|
389
|
+
return await ElementGeometryCacheTool.callCommand("getConnectedSubEntities", id, face, SubEntityType.Face, { smoothFaces: true });
|
|
390
|
+
}
|
|
391
|
+
catch (err) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
379
395
|
async applyAgendaOperation(ev, isAccept) {
|
|
380
396
|
var _a;
|
|
381
397
|
if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities)
|
|
@@ -387,6 +403,8 @@ export class OffsetFacesTool extends LocateSubEntityTool {
|
|
|
387
403
|
if (undefined === projPt)
|
|
388
404
|
return undefined;
|
|
389
405
|
const offsetDir = Vector3d.createStartEnd(faceData.point, projPt);
|
|
406
|
+
if (this.useOffset && undefined === offsetDir.scaleToLength(this.offset, offsetDir))
|
|
407
|
+
return undefined;
|
|
390
408
|
let offset = offsetDir.magnitude();
|
|
391
409
|
if (offset < Geometry.smallMetricDistance)
|
|
392
410
|
return undefined;
|
|
@@ -394,14 +412,28 @@ export class OffsetFacesTool extends LocateSubEntityTool {
|
|
|
394
412
|
offset = -offset;
|
|
395
413
|
try {
|
|
396
414
|
this._startedCmd = await this.startCommand();
|
|
397
|
-
const
|
|
415
|
+
const id = this.agenda.elements[0];
|
|
416
|
+
const faces = this.getAcceptedSubEntities();
|
|
417
|
+
if (this.addSmoothFaces) {
|
|
418
|
+
const allSmoothFaces = [];
|
|
419
|
+
for (const face of faces) {
|
|
420
|
+
const smoothFaces = await this.getSmoothFaces(id, face);
|
|
421
|
+
if (undefined !== smoothFaces)
|
|
422
|
+
allSmoothFaces.push(...smoothFaces);
|
|
423
|
+
}
|
|
424
|
+
for (const smooth of allSmoothFaces) {
|
|
425
|
+
if (undefined === faces.find((selected) => isSameSubEntity(selected, smooth)))
|
|
426
|
+
faces.unshift(smooth); // Preserve last selected entry as reference face...
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const params = { faces, distances: offset };
|
|
398
430
|
const opts = {
|
|
399
431
|
wantGraphic: isAccept ? undefined : true,
|
|
400
432
|
chordTolerance: computeChordToleranceFromPoint(ev.viewport, ev.point),
|
|
401
|
-
requestId: `${this.toolId}:${
|
|
433
|
+
requestId: `${this.toolId}:${id}`,
|
|
402
434
|
writeChanges: isAccept ? true : undefined,
|
|
403
435
|
};
|
|
404
|
-
return await ElementGeometryCacheTool.callCommand("offsetFaces",
|
|
436
|
+
return await ElementGeometryCacheTool.callCommand("offsetFaces", id, params, opts);
|
|
405
437
|
}
|
|
406
438
|
catch (err) {
|
|
407
439
|
return undefined;
|
|
@@ -468,6 +500,72 @@ export class HollowFacesTool extends LocateSubEntityTool {
|
|
|
468
500
|
}
|
|
469
501
|
HollowFacesTool.toolId = "HollowFaces";
|
|
470
502
|
HollowFacesTool.iconSpec = "icon-move"; // TODO: Need better icon...
|
|
503
|
+
/** @alpha Identify faces or edges for removal by growing surrounding faces. */
|
|
504
|
+
export class DeleteSubEntitiesTool extends LocateSubEntityTool {
|
|
505
|
+
wantSubEntityType(type) {
|
|
506
|
+
switch (type) {
|
|
507
|
+
case SubEntityType.Face:
|
|
508
|
+
case SubEntityType.Edge:
|
|
509
|
+
// Choose all faces or all edges...
|
|
510
|
+
return (0 === this._acceptedSubEntities.length || this._acceptedSubEntities[0].props.type === type);
|
|
511
|
+
default:
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
getMaximumSubEntityHits(type) {
|
|
516
|
+
if (!this.wantSubEntityType(type))
|
|
517
|
+
return 0;
|
|
518
|
+
// Only return single closest edge, avoids having to test for redundant edges on reset...
|
|
519
|
+
return (SubEntityType.Edge === type ? 1 : 25);
|
|
520
|
+
}
|
|
521
|
+
get geometryCacheFilter() {
|
|
522
|
+
return { parts: true, curves: false, surfaces: true, solids: true, other: false };
|
|
523
|
+
}
|
|
524
|
+
async doPickSubEntities(id, ev) {
|
|
525
|
+
const hits = await super.doPickSubEntities(id, ev);
|
|
526
|
+
if (undefined === hits)
|
|
527
|
+
return hits;
|
|
528
|
+
// Don't allow reset to select a back edge...
|
|
529
|
+
if (SubEntityType.Face === hits[0].subEntity.type)
|
|
530
|
+
return hits.filter((hit) => { return SubEntityType.Face === hit.subEntity.type; });
|
|
531
|
+
try {
|
|
532
|
+
const accept = await ElementGeometryCacheTool.callCommand("isRedundantEdge", id, hits[0].subEntity);
|
|
533
|
+
if (accept)
|
|
534
|
+
return hits;
|
|
535
|
+
if (hits.length > 1 && SubEntityType.Face === hits[1].subEntity.type)
|
|
536
|
+
return hits.slice(1); // Accept face of rejected edge...
|
|
537
|
+
return undefined;
|
|
538
|
+
}
|
|
539
|
+
catch (err) {
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
async applyAgendaOperation(ev, isAccept) {
|
|
544
|
+
if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities)
|
|
545
|
+
return undefined;
|
|
546
|
+
try {
|
|
547
|
+
this._startedCmd = await this.startCommand();
|
|
548
|
+
const params = { subEntities: this.getAcceptedSubEntities() };
|
|
549
|
+
const opts = {
|
|
550
|
+
wantGraphic: isAccept ? undefined : true,
|
|
551
|
+
chordTolerance: computeChordToleranceFromPoint(ev.viewport, ev.point),
|
|
552
|
+
requestId: `${this.toolId}:${this.agenda.elements[0]}`,
|
|
553
|
+
writeChanges: isAccept ? true : undefined,
|
|
554
|
+
};
|
|
555
|
+
return await ElementGeometryCacheTool.callCommand("deleteSubEntities", this.agenda.elements[0], params, opts);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async onRestartTool() {
|
|
562
|
+
const tool = new DeleteSubEntitiesTool();
|
|
563
|
+
if (!await tool.run())
|
|
564
|
+
return this.exitTool();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
DeleteSubEntitiesTool.toolId = "DeleteSubEntities";
|
|
568
|
+
DeleteSubEntitiesTool.iconSpec = "icon-move"; // TODO: Need better icon...
|
|
471
569
|
/** @alpha */
|
|
472
570
|
export var ImprintSolidMethod;
|
|
473
571
|
(function (ImprintSolidMethod) {
|
|
@@ -764,4 +862,205 @@ export class ChamferEdgesTool extends BlendEdgesTool {
|
|
|
764
862
|
}
|
|
765
863
|
ChamferEdgesTool.toolId = "ChamferEdges";
|
|
766
864
|
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;
|
|
873
|
+
}
|
|
874
|
+
get wantDynamics() { return true; }
|
|
875
|
+
get wantAccuSnap() { return this.isDynamicsStarted; }
|
|
876
|
+
// TODO: Sweep open path to surface, sweep surface to solid...
|
|
877
|
+
get geometryCacheFilter() {
|
|
878
|
+
return { parts: true, curves: false, surfaces: true, solids: true, other: false };
|
|
879
|
+
}
|
|
880
|
+
async createSubEntityData(id, hit) {
|
|
881
|
+
const data = await super.createSubEntityData(id, hit);
|
|
882
|
+
if (undefined !== hit.point && undefined !== hit.normal)
|
|
883
|
+
data.toolData = FaceLocationData.create(hit.point, hit.normal);
|
|
884
|
+
return data;
|
|
885
|
+
}
|
|
886
|
+
drawAcceptedSubEntities(context) {
|
|
887
|
+
var _a;
|
|
888
|
+
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)
|
|
892
|
+
return;
|
|
893
|
+
const builder = context.createGraphic({ type: GraphicType.WorldOverlay });
|
|
894
|
+
builder.setSymbology(context.viewport.getContrastToBackgroundColor(), ColorDef.black, 10);
|
|
895
|
+
builder.addPointString([faceData.point]);
|
|
896
|
+
context.addDecorationFromBuilder(builder);
|
|
897
|
+
}
|
|
898
|
+
async getSmoothFaces(id, face) {
|
|
899
|
+
try {
|
|
900
|
+
// NOTE: For sweep/translation, it makes sense to limit smooth to immediately adjacent...
|
|
901
|
+
return await ElementGeometryCacheTool.callCommand("getConnectedSubEntities", id, face, SubEntityType.Face, { smoothFaces: true, adjacentFaces: true });
|
|
902
|
+
}
|
|
903
|
+
catch (err) {
|
|
904
|
+
return undefined;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
async applyAgendaOperation(ev, isAccept) {
|
|
908
|
+
var _a;
|
|
909
|
+
if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities)
|
|
910
|
+
return undefined;
|
|
911
|
+
const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
|
|
912
|
+
if (undefined === faceData)
|
|
913
|
+
return undefined;
|
|
914
|
+
const path = Vector3d.createStartEnd(faceData.point, ev.point);
|
|
915
|
+
if (this.useDistance && undefined === path.scaleToLength(this.distance, path))
|
|
916
|
+
return undefined;
|
|
917
|
+
if (path.magnitude() < Geometry.smallMetricDistance)
|
|
918
|
+
return undefined;
|
|
919
|
+
try {
|
|
920
|
+
this._startedCmd = await this.startCommand();
|
|
921
|
+
const id = this.agenda.elements[0];
|
|
922
|
+
const opts = {
|
|
923
|
+
wantGraphic: isAccept ? undefined : true,
|
|
924
|
+
chordTolerance: computeChordToleranceFromPoint(ev.viewport, ev.point),
|
|
925
|
+
requestId: `${this.toolId}:${id}`,
|
|
926
|
+
writeChanges: isAccept ? true : undefined,
|
|
927
|
+
};
|
|
928
|
+
const faces = this.getAcceptedSubEntities();
|
|
929
|
+
if (this.addSmoothFaces) {
|
|
930
|
+
const allSmoothFaces = [];
|
|
931
|
+
for (const face of faces) {
|
|
932
|
+
const smoothFaces = await this.getSmoothFaces(id, face);
|
|
933
|
+
if (undefined !== smoothFaces)
|
|
934
|
+
allSmoothFaces.push(...smoothFaces);
|
|
935
|
+
}
|
|
936
|
+
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...
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
const params = { faces, path };
|
|
942
|
+
return await ElementGeometryCacheTool.callCommand("sweepFaces", id, params, opts);
|
|
943
|
+
}
|
|
944
|
+
catch (err) {
|
|
945
|
+
return undefined;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
setupAccuDraw() {
|
|
949
|
+
var _a;
|
|
950
|
+
if (!this.haveAcceptedSubEntities)
|
|
951
|
+
return;
|
|
952
|
+
const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
|
|
953
|
+
if (undefined === faceData)
|
|
954
|
+
return;
|
|
955
|
+
const hints = new AccuDrawHintBuilder();
|
|
956
|
+
hints.setOriginFixed = true;
|
|
957
|
+
hints.setLockY = true;
|
|
958
|
+
hints.setLockZ = true;
|
|
959
|
+
hints.setModeRectangular();
|
|
960
|
+
hints.setOrigin(faceData.point);
|
|
961
|
+
hints.setXAxis2(faceData.normal);
|
|
962
|
+
hints.sendHints(false);
|
|
963
|
+
}
|
|
964
|
+
async onRestartTool() {
|
|
965
|
+
const tool = new SweepFacesTool();
|
|
966
|
+
if (!await tool.run())
|
|
967
|
+
return this.exitTool();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
SweepFacesTool.toolId = "SweepFaces";
|
|
971
|
+
SweepFacesTool.iconSpec = "icon-move"; // TODO: Need better icon...
|
|
972
|
+
/** @alpha Identify faces of solids and surfaces to revolve. */
|
|
973
|
+
export class SpinFacesTool extends LocateSubEntityTool {
|
|
974
|
+
constructor() {
|
|
975
|
+
super(...arguments);
|
|
976
|
+
this.points = [];
|
|
977
|
+
// TODO: Tool settings for sweep angle...
|
|
978
|
+
this.sweep = Angle.piOver2Radians;
|
|
979
|
+
}
|
|
980
|
+
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
|
+
}
|
|
995
|
+
async applyAgendaOperation(ev, isAccept) {
|
|
996
|
+
if (undefined === ev.viewport || this.agenda.isEmpty || !this.haveAcceptedSubEntities || this.points.length < (isAccept ? 2 : 1))
|
|
997
|
+
return undefined;
|
|
998
|
+
const direction = Vector3d.createStartEnd(this.points[0], isAccept ? this.points[1] : ev.point);
|
|
999
|
+
if (direction.magnitude() < Geometry.smallMetricDistance)
|
|
1000
|
+
return undefined;
|
|
1001
|
+
const origin = this.points[0];
|
|
1002
|
+
const angle = Angle.createRadians(this.sweep);
|
|
1003
|
+
try {
|
|
1004
|
+
this._startedCmd = await this.startCommand();
|
|
1005
|
+
const id = this.agenda.elements[0];
|
|
1006
|
+
const opts = {
|
|
1007
|
+
wantGraphic: isAccept ? undefined : true,
|
|
1008
|
+
chordTolerance: computeChordToleranceFromPoint(ev.viewport, ev.point),
|
|
1009
|
+
requestId: `${this.toolId}:${id}`,
|
|
1010
|
+
writeChanges: isAccept ? true : undefined,
|
|
1011
|
+
};
|
|
1012
|
+
const faces = this.getAcceptedSubEntities();
|
|
1013
|
+
const params = { faces, origin, direction, angle };
|
|
1014
|
+
let result = await ElementGeometryCacheTool.callCommand("spinFaces", id, params, opts);
|
|
1015
|
+
// Spun face can be used to create a pocket...retry with negative sweep...
|
|
1016
|
+
if (undefined === result) {
|
|
1017
|
+
angle.setRadians(-angle.radians);
|
|
1018
|
+
result = await ElementGeometryCacheTool.callCommand("spinFaces", id, params, opts);
|
|
1019
|
+
}
|
|
1020
|
+
return result;
|
|
1021
|
+
}
|
|
1022
|
+
catch (err) {
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
onDynamicFrame(ev, context) {
|
|
1027
|
+
if (0 === this.points.length)
|
|
1028
|
+
return;
|
|
1029
|
+
const pts = this.points.slice();
|
|
1030
|
+
pts.push(ev.point.clone());
|
|
1031
|
+
const builder = context.createGraphic({ type: GraphicType.WorldOverlay });
|
|
1032
|
+
builder.setSymbology(context.viewport.getContrastToBackgroundColor(), ColorDef.black, 3);
|
|
1033
|
+
builder.addLineString(pts);
|
|
1034
|
+
context.addGraphic(builder.finish());
|
|
1035
|
+
super.onDynamicFrame(ev, context);
|
|
1036
|
+
}
|
|
1037
|
+
async gatherInput(ev) {
|
|
1038
|
+
if (this.haveAcceptedSubEntities)
|
|
1039
|
+
this.points.push(ev.point.clone());
|
|
1040
|
+
return super.gatherInput(ev);
|
|
1041
|
+
}
|
|
1042
|
+
get wantAdditionalInput() {
|
|
1043
|
+
return super.wantAdditionalInput || this.points.length < 2;
|
|
1044
|
+
}
|
|
1045
|
+
setupAccuDraw() {
|
|
1046
|
+
var _a;
|
|
1047
|
+
if (!this.haveAcceptedSubEntities || 0 !== this.points.length)
|
|
1048
|
+
return;
|
|
1049
|
+
const faceData = (_a = this.getAcceptedSubEntityData()) === null || _a === void 0 ? void 0 : _a.toolData;
|
|
1050
|
+
if (undefined === faceData)
|
|
1051
|
+
return;
|
|
1052
|
+
const hints = new AccuDrawHintBuilder();
|
|
1053
|
+
hints.setModeRectangular();
|
|
1054
|
+
hints.setOrigin(faceData.point);
|
|
1055
|
+
hints.setNormal(faceData.normal);
|
|
1056
|
+
hints.sendHints(false);
|
|
1057
|
+
}
|
|
1058
|
+
async onRestartTool() {
|
|
1059
|
+
const tool = new SpinFacesTool();
|
|
1060
|
+
if (!await tool.run())
|
|
1061
|
+
return this.exitTool();
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
SpinFacesTool.toolId = "SpinFaces";
|
|
1065
|
+
SpinFacesTool.iconSpec = "icon-move"; // TODO: Need better icon...
|
|
767
1066
|
//# sourceMappingURL=SolidModelingTools.js.map
|