@itwin/core-frontend
Version:
iTwin.js frontend components
917 lines • 96.8 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Tools
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ViewClipDecorationProvider = exports.ClipEventType = exports.ViewClipDecoration = exports.ViewClipControlArrow = exports.ViewClipPlanesModifyTool = exports.ViewClipShapeModifyTool = exports.ViewClipModifyTool = exports.ViewClipByElementTool = exports.ViewClipByRangeTool = exports.ViewClipByShapeTool = exports.ViewClipByPlaneTool = exports.ViewClipClearTool = exports.ViewClipTool = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const AccuDraw_1 = require("../AccuDraw");
const CoordSystem_1 = require("../CoordSystem");
const ElementLocateManager_1 = require("../ElementLocateManager");
const IModelApp_1 = require("../IModelApp");
const EditManipulator_1 = require("./EditManipulator");
const PrimitiveTool_1 = require("./PrimitiveTool");
const Tool_1 = require("./Tool");
const ToolAssistance_1 = require("./ToolAssistance");
const GraphicType_1 = require("../common/render/GraphicType");
/** A tool to define a clip volume for a view
* @public @extensions
*/
class ViewClipTool extends PrimitiveTool_1.PrimitiveTool {
_clipEventHandler;
constructor(_clipEventHandler) {
super();
this._clipEventHandler = _clipEventHandler;
}
/** @internal */
static _orientationName = "enumAsOrientation";
/** @internal */
static enumAsOrientationMessage(str) { return Tool_1.CoreTools.translate(`Settings.Orientation.${str}`); }
/** @internal */
static _getEnumAsOrientationDescription = () => {
return {
name: ViewClipTool._orientationName,
displayLabel: Tool_1.CoreTools.translate("Settings.Orientation.Label"),
typename: "enum",
enum: {
choices: [
{ label: ViewClipTool.enumAsOrientationMessage("Top"), value: AccuDraw_1.ContextRotationId.Top },
{ label: ViewClipTool.enumAsOrientationMessage("Front"), value: AccuDraw_1.ContextRotationId.Front },
{ label: ViewClipTool.enumAsOrientationMessage("Left"), value: AccuDraw_1.ContextRotationId.Left },
{ label: ViewClipTool.enumAsOrientationMessage("Bottom"), value: AccuDraw_1.ContextRotationId.Bottom },
{ label: ViewClipTool.enumAsOrientationMessage("Back"), value: AccuDraw_1.ContextRotationId.Back },
{ label: ViewClipTool.enumAsOrientationMessage("Right"), value: AccuDraw_1.ContextRotationId.Right },
{ label: ViewClipTool.enumAsOrientationMessage("View"), value: AccuDraw_1.ContextRotationId.View },
{ label: ViewClipTool.enumAsOrientationMessage("Face"), value: AccuDraw_1.ContextRotationId.Face },
],
},
};
};
/** @internal */
requireWriteableTarget() { return false; }
/** @internal */
isCompatibleViewport(vp, isSelectedViewChange) { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && vp.view.allow3dManipulations()); }
/** @internal */
async onPostInstall() {
await super.onPostInstall();
this.setupAndPromptForNextAction();
}
/** @internal */
async onUnsuspend() { this.showPrompt(); }
/** @internal */
async onRestartTool() { return this.exitTool(); }
/** @internal */
showPrompt() { }
/** @internal */
setupAndPromptForNextAction() { this.showPrompt(); }
/** @internal */
async onResetButtonUp(_ev) {
await this.onReinitialize();
return Tool_1.EventHandled.No;
}
/** @internal */
static getPlaneInwardNormal(orientation, viewport) {
const matrix = AccuDraw_1.AccuDrawHintBuilder.getContextRotation(orientation, viewport);
if (undefined === matrix)
return undefined;
return matrix.getColumn(2).negate();
}
static enableClipVolume(viewport) {
if (viewport.viewFlags.clipVolume)
return false;
viewport.viewFlags = viewport.viewFlags.with("clipVolume", true);
return true;
}
static setViewClip(viewport, clip) {
viewport.view.setViewClip(clip);
viewport.setupFromView();
return true;
}
static doClipToConvexClipPlaneSet(viewport, planes) {
const prim = core_geometry_1.ClipPrimitive.createCapture(planes);
const clip = core_geometry_1.ClipVector.createEmpty();
clip.appendReference(prim);
return this.setViewClip(viewport, clip);
}
static doClipToPlane(viewport, origin, normal, clearExistingPlanes) {
const plane = core_geometry_1.Plane3dByOriginAndUnitNormal.create(origin, normal);
if (undefined === plane)
return false;
let planeSet;
if (!clearExistingPlanes) {
const existingClip = viewport.view.getViewClip();
if (undefined !== existingClip && 1 === existingClip.clips.length) {
const existingPrim = existingClip.clips[0];
if (!(existingPrim instanceof core_geometry_1.ClipShape)) {
const existingPlaneSets = existingPrim.fetchClipPlanesRef();
if (undefined !== existingPlaneSets && 1 === existingPlaneSets.convexSets.length)
planeSet = existingPlaneSets.convexSets[0];
}
}
}
if (undefined === planeSet)
planeSet = core_geometry_1.ConvexClipPlaneSet.createEmpty();
planeSet.addPlaneToConvexSet(core_geometry_1.ClipPlane.createPlane(plane));
return this.doClipToConvexClipPlaneSet(viewport, planeSet);
}
static doClipToShape(viewport, xyPoints, transform, zLow, zHigh) {
const clip = core_geometry_1.ClipVector.createEmpty();
clip.appendShape(xyPoints, zLow, zHigh, transform);
return this.setViewClip(viewport, clip);
}
static doClipToRange(viewport, range, transform) {
if (range.isNull || range.isAlmostZeroX || range.isAlmostZeroY)
return false;
const clip = core_geometry_1.ClipVector.createEmpty();
const block = core_geometry_1.ClipShape.createBlock(range, range.isAlmostZeroZ ? core_geometry_1.ClipMaskXYZRangePlanes.XAndY : core_geometry_1.ClipMaskXYZRangePlanes.All, false, false, transform);
clip.appendReference(block);
return this.setViewClip(viewport, clip);
}
static doClipClear(viewport) {
if (!ViewClipTool.hasClip(viewport))
return false;
return this.setViewClip(viewport);
}
/** @internal */
static getClipRayTransformed(origin, direction, transform) {
const facePt = origin.clone();
const faceDir = direction.clone();
if (undefined !== transform) {
transform.multiplyPoint3d(facePt, facePt);
transform.multiplyVector(faceDir, faceDir);
faceDir.normalizeInPlace();
}
return core_geometry_1.Ray3d.createCapture(facePt, faceDir);
}
/** @internal */
static getOffsetValueTransformed(offset, transform) {
if (undefined === transform)
return offset;
const lengthVec = core_geometry_1.Vector3d.create(offset);
transform.multiplyVector(lengthVec, lengthVec);
const localOffset = lengthVec.magnitude();
return (offset < 0 ? -localOffset : localOffset);
}
/** @internal */
static addClipPlanesLoops(builder, loops, outline) {
for (const geom of loops) {
if (!(geom instanceof core_geometry_1.Loop))
continue;
if (outline)
builder.addPath(core_geometry_1.Path.createArray(geom.children));
else
builder.addLoop(geom);
}
}
/** @internal */
static addClipShape(builder, shape, extents) {
const shapePtsLo = ViewClipTool.getClipShapePoints(shape, extents.low);
const shapePtsHi = ViewClipTool.getClipShapePoints(shape, extents.high);
for (let i = 0; i < shapePtsLo.length; i++)
builder.addLineString([shapePtsLo[i].clone(), shapePtsHi[i].clone()]);
builder.addLineString(shapePtsLo);
builder.addLineString(shapePtsHi);
}
/** @internal */
static drawClip(context, clip, viewExtents, options) {
const clipShape = ViewClipTool.isSingleClipShape(clip);
const clipPlanes = (undefined === clipShape ? ViewClipTool.isSingleConvexClipPlaneSet(clip) : undefined);
if (undefined === clipShape && undefined === clipPlanes)
return;
const viewRange = (viewExtents ? viewExtents : context.viewport.computeViewRange());
const clipPlanesLoops = (undefined !== clipPlanes ? core_geometry_1.ClipUtilities.loopsOfConvexClipPlaneIntersectionWithRange(clipPlanes, viewRange) : undefined);
if (undefined === clipShape && (undefined === clipPlanesLoops || 0 === clipPlanesLoops.length))
return;
const color = (options && options.color ? options.color : EditManipulator_1.EditManipulator.HandleUtils.adjustForBackgroundColor(core_common_1.ColorDef.white, context.viewport));
const builderVis = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration, clipShape ? clipShape.transformFromClip : undefined, (options ? options.id : undefined));
const builderHid = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldOverlay, clipShape ? clipShape.transformFromClip : undefined);
builderVis.setSymbology(color, core_common_1.ColorDef.black, (options && options.visibleWidth ? options.visibleWidth : 3));
builderHid.setSymbology(color, core_common_1.ColorDef.black, (options && options.hiddenWidth ? options.hiddenWidth : 1), (options && options.hiddenStyle ? options.hiddenStyle : core_common_1.LinePixels.Code2));
if (undefined !== clipPlanesLoops) {
ViewClipTool.addClipPlanesLoops(builderVis, clipPlanesLoops, true);
ViewClipTool.addClipPlanesLoops(builderHid, clipPlanesLoops, true);
if (options && options.fillClipPlanes) {
const fill = (options.fill ? options.fill : EditManipulator_1.EditManipulator.HandleUtils.adjustForBackgroundColor(core_common_1.ColorDef.from(0, 255, 255, 225), context.viewport));
const builderFill = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration);
builderFill.setSymbology(fill, fill, 0);
ViewClipTool.addClipPlanesLoops(builderFill, (options.hasPrimaryPlane ? [clipPlanesLoops[0]] : clipPlanesLoops), false);
context.addDecorationFromBuilder(builderFill);
}
}
else if (undefined !== clipShape) {
const clipExtents = ViewClipTool.getClipShapeExtents(clipShape, viewRange);
ViewClipTool.addClipShape(builderVis, clipShape, clipExtents);
ViewClipTool.addClipShape(builderHid, clipShape, clipExtents);
}
context.addDecorationFromBuilder(builderVis);
context.addDecorationFromBuilder(builderHid);
}
static isHilited(vp, id) {
return (undefined !== id ? vp.iModel.hilited.elements.has(core_bentley_1.Id64.getLowerUint32(id), core_bentley_1.Id64.getUpperUint32(id)) : false);
}
static isFlashed(vp, id) {
return (undefined !== id ? vp.lastFlashedElementId === id : false);
}
static drawClipShape(context, shape, extents, color, weight, id) {
const builder = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration, shape.transformFromClip, id); // Use WorldDecoration not WorldOverlay to make sure handles have priority...
builder.setSymbology(color, core_common_1.ColorDef.black, weight);
ViewClipTool.addClipShape(builder, shape, extents);
context.addDecorationFromBuilder(builder);
// NOTE: We want to display hidden edges when clip decoration isn't hilited (not selected or drawn in dynamics).
// This isn't required and is messy looking when the clip is being drawn hilited.
// If the clip decoration is being flashed, draw using the hilite color to match the pickable world decoration display.
if (!this.isHilited(context.viewport, id)) {
const builderHid = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldOverlay, shape.transformFromClip);
builderHid.setSymbology(this.isFlashed(context.viewport, id) ? context.viewport.hilite.color : color, core_common_1.ColorDef.black, 1, core_common_1.LinePixels.Code2);
ViewClipTool.addClipShape(builderHid, shape, extents);
context.addDecorationFromBuilder(builderHid);
}
}
/** @internal */
static getClipShapePoints(shape, z) {
const points = [];
for (const pt of shape.polygon)
points.push(core_geometry_1.Point3d.create(pt.x, pt.y, z));
return points;
}
/** @internal */
static getClipShapeExtents(shape, viewRange) {
let zLow = shape.zLow;
let zHigh = shape.zHigh;
if (undefined === zLow || undefined === zHigh) {
const zVec = core_geometry_1.Vector3d.unitZ();
const origin = shape.polygon[0];
const corners = viewRange.corners();
if (undefined !== shape.transformToClip)
shape.transformToClip.multiplyPoint3dArrayInPlace(corners);
for (const corner of corners) {
const delta = core_geometry_1.Vector3d.createStartEnd(origin, corner);
const projection = delta.dotProduct(zVec);
if (undefined === shape.zLow && (undefined === zLow || projection < zLow))
zLow = projection;
if (undefined === shape.zHigh && (undefined === zHigh || projection > zHigh))
zHigh = projection;
}
}
if (undefined === zLow || undefined === zHigh)
return core_geometry_1.Range1d.createNull();
return core_geometry_1.Range1d.createXX(zLow, zHigh);
}
/** @internal */
static isSingleClipShape(clip) {
if (1 !== clip.clips.length)
return undefined;
const prim = clip.clips[0];
if (!(prim instanceof core_geometry_1.ClipShape))
return undefined;
if (!prim.isValidPolygon)
return undefined;
return prim;
}
static drawClipPlanesLoops(context, loops, color, weight, dashed, fill, id) {
if (loops.length < 1)
return;
const builderEdge = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration, undefined, id); // Use WorldDecoration not WorldOverlay to make sure handles have priority...
builderEdge.setSymbology(color, core_common_1.ColorDef.black, weight, dashed ? core_common_1.LinePixels.Code2 : undefined);
ViewClipTool.addClipPlanesLoops(builderEdge, loops, true);
context.addDecorationFromBuilder(builderEdge);
// NOTE: We want to display hidden edges when clip decoration isn't hilited (not selected or drawn in dynamics).
// This isn't required and is messy looking when the clip is being drawn hilited.
// If the clip decoration is being flashed, draw using the hilite color to match the pickable world decoration display.
if (!this.isHilited(context.viewport, id)) {
const builderEdgeHid = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldOverlay);
builderEdgeHid.setSymbology(this.isFlashed(context.viewport, id) ? context.viewport.hilite.color : color, core_common_1.ColorDef.black, 1, core_common_1.LinePixels.Code2);
ViewClipTool.addClipPlanesLoops(builderEdgeHid, loops, true);
context.addDecorationFromBuilder(builderEdgeHid);
}
if (undefined === fill)
return;
const builderFace = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration, undefined);
builderFace.setSymbology(fill, fill, 0);
ViewClipTool.addClipPlanesLoops(builderFace, loops, false);
context.addDecorationFromBuilder(builderFace);
}
/** @internal */
static isSingleConvexClipPlaneSet(clip) {
if (1 !== clip.clips.length)
return undefined;
const prim = clip.clips[0];
if (prim instanceof core_geometry_1.ClipShape)
return undefined;
const planeSets = prim.fetchClipPlanesRef();
return (undefined !== planeSets && 1 === planeSets.convexSets.length) ? planeSets.convexSets[0] : undefined;
}
/** @internal */
static isSingleClipPlane(clip) {
const clipPlanes = ViewClipTool.isSingleConvexClipPlaneSet(clip);
if (undefined === clipPlanes || 1 !== clipPlanes.planes.length)
return undefined;
return clipPlanes.planes[0];
}
static areClipsEqual(clipA, clipB) {
if (clipA === clipB)
return true;
if (clipA.clips.length !== clipB.clips.length)
return false;
for (let iPrim = 0; iPrim < clipA.clips.length; iPrim++) {
const primA = clipA.clips[iPrim];
const primB = clipB.clips[iPrim];
const planesA = primA.fetchClipPlanesRef();
const planesB = primB.fetchClipPlanesRef();
if (undefined !== planesA && undefined !== planesB) {
if (planesA.convexSets.length !== planesB.convexSets.length)
return false;
for (let iPlane = 0; iPlane < planesA.convexSets.length; iPlane++) {
const planeSetA = planesA.convexSets[iPlane];
const planeSetB = planesB.convexSets[iPlane];
if (planeSetA.planes.length !== planeSetB.planes.length)
return false;
for (let iClipPlane = 0; iClipPlane < planeSetA.planes.length; iClipPlane++) {
const planeA = planeSetA.planes[iClipPlane];
const planeB = planeSetB.planes[iClipPlane];
if (!planeA.isAlmostEqual(planeB))
return false;
}
}
}
else if (undefined === planesA && undefined === planesB) {
continue;
}
else {
return false;
}
}
return true;
}
static hasClip(viewport) {
return (undefined !== viewport.view.getViewClip());
}
}
exports.ViewClipTool = ViewClipTool;
/** A tool to remove a clip volume for a view
* @public @extensions
*/
class ViewClipClearTool extends ViewClipTool {
static toolId = "ViewClip.Clear";
static iconSpec = "icon-section-tool";
/** @internal */
isCompatibleViewport(vp, isSelectedViewChange) { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && ViewClipTool.hasClip(vp)); }
/** @internal */
showPrompt() {
const mainInstruction = ToolAssistance_1.ToolAssistance.createInstruction(this.iconSpec, Tool_1.CoreTools.translate("ViewClip.Clear.Prompts.FirstPoint"));
IModelApp_1.IModelApp.notifications.setToolAssistance(ToolAssistance_1.ToolAssistance.createInstructions(mainInstruction));
}
async doClipClear(viewport) {
if (!ViewClipTool.doClipClear(viewport))
return false;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onClearClip(viewport);
await this.onReinitialize();
return true;
}
/** @internal */
async onPostInstall() {
await super.onPostInstall();
if (undefined !== this.targetView)
await this.doClipClear(this.targetView);
}
/** @internal */
async onDataButtonDown(_ev) {
if (undefined === this.targetView)
return Tool_1.EventHandled.No;
return await this.doClipClear(this.targetView) ? Tool_1.EventHandled.Yes : Tool_1.EventHandled.No;
}
}
exports.ViewClipClearTool = ViewClipClearTool;
/** A tool to define a clip volume for a view by specifying a plane
* @public
*/
class ViewClipByPlaneTool extends ViewClipTool {
_clearExistingPlanes;
static toolId = "ViewClip.ByPlane";
static iconSpec = "icon-section-plane";
/** @internal */
_orientationValue = { value: AccuDraw_1.ContextRotationId.Face };
constructor(clipEventHandler, _clearExistingPlanes = false) {
super(clipEventHandler);
this._clearExistingPlanes = _clearExistingPlanes;
}
/** @internal */
get orientation() { return this._orientationValue.value; }
set orientation(option) { this._orientationValue.value = option; }
/** @internal */
supplyToolSettingsProperties() {
const initialValue = IModelApp_1.IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, ViewClipTool._orientationName);
initialValue && (this._orientationValue = initialValue);
const toolSettings = new Array();
const settingsItem = { value: this._orientationValue, property: ViewClipTool._getEnumAsOrientationDescription(), editorPosition: { rowPriority: 0, columnIndex: 2 } };
toolSettings.push(settingsItem);
return toolSettings;
}
/** @internal */
async applyToolSettingPropertyChange(updatedValue) {
if (updatedValue.propertyName === ViewClipTool._orientationName) {
this._orientationValue = updatedValue.value;
if (this._orientationValue) {
IModelApp_1.IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: ViewClipTool._orientationName, value: this._orientationValue });
return true;
}
}
return false;
}
/** @internal */
showPrompt() {
const mainInstruction = ToolAssistance_1.ToolAssistance.createInstruction(this.iconSpec, Tool_1.CoreTools.translate("ViewClip.ByPlane.Prompts.FirstPoint"));
const mouseInstructions = [];
const touchInstructions = [];
if (!ToolAssistance_1.ToolAssistance.createTouchCursorInstructions(touchInstructions))
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.OneTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.LeftClick, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.TwoTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.RightClick, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
const sections = [];
sections.push(ToolAssistance_1.ToolAssistance.createSection(mouseInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
sections.push(ToolAssistance_1.ToolAssistance.createSection(touchInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
const instructions = ToolAssistance_1.ToolAssistance.createInstructions(mainInstruction, sections);
IModelApp_1.IModelApp.notifications.setToolAssistance(instructions);
}
/** @internal */
setupAndPromptForNextAction() {
IModelApp_1.IModelApp.accuSnap.enableSnap(true);
super.setupAndPromptForNextAction();
}
/** @internal */
async onDataButtonDown(ev) {
if (undefined === this.targetView)
return Tool_1.EventHandled.No;
const normal = ViewClipTool.getPlaneInwardNormal(this.orientation, this.targetView);
if (undefined === normal)
return Tool_1.EventHandled.No;
ViewClipTool.enableClipVolume(this.targetView);
if (!ViewClipTool.doClipToPlane(this.targetView, ev.point, normal, this._clearExistingPlanes))
return Tool_1.EventHandled.No;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onNewClipPlane(this.targetView);
await this.onReinitialize();
return Tool_1.EventHandled.Yes;
}
}
exports.ViewClipByPlaneTool = ViewClipByPlaneTool;
/** A tool to define a clip volume for a view by specifying a shape
* @public
*/
class ViewClipByShapeTool extends ViewClipTool {
static toolId = "ViewClip.ByShape";
static iconSpec = "icon-section-shape";
/** @internal */
_orientationValue = { value: AccuDraw_1.ContextRotationId.Top };
/** @internal */
_points = [];
/** @internal */
_matrix;
/** @internal */
_zLow;
/** @internal */
_zHigh;
/** @internal */
get orientation() { return this._orientationValue.value; }
set orientation(option) { this._orientationValue.value = option; }
/** @internal */
supplyToolSettingsProperties() {
const initialValue = IModelApp_1.IModelApp.toolAdmin.toolSettingsState.getInitialToolSettingValue(this.toolId, ViewClipTool._orientationName);
initialValue && (this._orientationValue = initialValue);
const toolSettings = new Array();
toolSettings.push({ value: this._orientationValue, property: ViewClipTool._getEnumAsOrientationDescription(), editorPosition: { rowPriority: 0, columnIndex: 2 } });
return toolSettings;
}
/** @internal */
async applyToolSettingPropertyChange(updatedValue) {
if (updatedValue.propertyName === ViewClipTool._orientationName) {
this._orientationValue = updatedValue.value;
if (!this._orientationValue)
return false;
this._points.length = 0;
this._matrix = undefined;
AccuDraw_1.AccuDrawHintBuilder.deactivate();
this.setupAndPromptForNextAction();
IModelApp_1.IModelApp.toolAdmin.toolSettingsState.saveToolSettingProperty(this.toolId, { propertyName: ViewClipTool._orientationName, value: this._orientationValue });
return true;
}
return false;
}
/** @internal */
showPrompt() {
let mainMsg = "ViewClip.ByShape.Prompts.";
switch (this._points.length) {
case 0:
mainMsg += "FirstPoint";
break;
case 1:
mainMsg += "SecondPoint";
break;
case 2:
mainMsg += "ThirdPoint";
break;
default:
mainMsg += "NextPoint";
break;
}
const mainInstruction = ToolAssistance_1.ToolAssistance.createInstruction(this.iconSpec, Tool_1.CoreTools.translate(mainMsg));
const mouseInstructions = [];
const touchInstructions = [];
if (!ToolAssistance_1.ToolAssistance.createTouchCursorInstructions(touchInstructions))
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.OneTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.LeftClick, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.TwoTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.RightClick, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
if (this._points.length > 1)
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createModifierKeyInstruction(ToolAssistance_1.ToolAssistance.ctrlKey, ToolAssistance_1.ToolAssistanceImage.LeftClick, Tool_1.CoreTools.translate("ElementSet.Inputs.AdditionalPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
if (0 !== this._points.length)
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createKeyboardInstruction(ToolAssistance_1.ToolAssistance.createKeyboardInfo([ToolAssistance_1.ToolAssistance.ctrlKey, "Z"]), Tool_1.CoreTools.translate("ElementSet.Inputs.UndoLastPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
const sections = [];
sections.push(ToolAssistance_1.ToolAssistance.createSection(mouseInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
sections.push(ToolAssistance_1.ToolAssistance.createSection(touchInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
const instructions = ToolAssistance_1.ToolAssistance.createInstructions(mainInstruction, sections);
IModelApp_1.IModelApp.notifications.setToolAssistance(instructions);
}
/** @internal */
setupAndPromptForNextAction() {
IModelApp_1.IModelApp.accuSnap.enableSnap(true);
super.setupAndPromptForNextAction();
if (0 === this._points.length)
return;
if (undefined === this._matrix)
return;
const hints = new AccuDraw_1.AccuDrawHintBuilder();
hints.setOrigin(this._points[this._points.length - 1]);
if (1 === this._points.length) {
hints.setMatrix(this._matrix);
hints.setModeRectangular();
}
else if (this._points.length > 1 && !(this._points[this._points.length - 1].isAlmostEqual(this._points[this._points.length - 2]))) {
const xVec = core_geometry_1.Vector3d.createStartEnd(this._points[this._points.length - 2], this._points[this._points.length - 1]);
const zVec = this._matrix.getColumn(2);
const matrix = core_geometry_1.Matrix3d.createRigidFromColumns(xVec, zVec, core_geometry_1.AxisOrder.XZY);
if (undefined !== matrix)
hints.setMatrix(matrix); // Rotate AccuDraw x axis to last segment preserving current up vector...
}
hints.setLockZ = true;
hints.sendHints();
}
getClipPoints(ev) {
const points = [];
if (undefined === this.targetView || this._points.length < 1)
return points;
for (const pt of this._points)
points.push(pt.clone());
if (undefined === this._matrix)
return points;
const vp = ev.viewport;
if (undefined === vp)
return points;
const normal = this._matrix.getColumn(2);
let currentPt = AccuDraw_1.AccuDrawHintBuilder.projectPointToPlaneInView(ev.point, points[0], normal, vp, true);
if (undefined === currentPt)
currentPt = ev.point.clone();
if (2 === points.length && !ev.isControlKey) {
const xDir = core_geometry_1.Vector3d.createStartEnd(points[0], points[1]);
const xLen = xDir.magnitude();
xDir.normalizeInPlace();
const yDir = xDir.crossProduct(normal);
yDir.normalizeInPlace();
const cornerPt = AccuDraw_1.AccuDrawHintBuilder.projectPointToLineInView(currentPt, points[1], yDir, vp, true);
if (undefined !== cornerPt) {
points.push(cornerPt);
cornerPt.plusScaled(xDir, -xLen, currentPt);
}
}
points.push(currentPt);
if (points.length > 2)
points.push(points[0].clone());
return points;
}
/** @internal */
isValidLocation(ev, isButtonEvent) {
return (this._points.length > 0 ? true : super.isValidLocation(ev, isButtonEvent));
}
/** @internal */
decorate(context) {
if (context.viewport !== this.targetView)
return;
const ev = new Tool_1.BeButtonEvent();
IModelApp_1.IModelApp.toolAdmin.fillEventFromCursorLocation(ev);
if (undefined === ev.viewport)
return;
const points = this.getClipPoints(ev);
if (points.length < 2)
return;
const builderAccVis = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration);
const builderAccHid = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldOverlay);
const colorAccVis = EditManipulator_1.EditManipulator.HandleUtils.adjustForBackgroundColor(core_common_1.ColorDef.white, context.viewport);
const colorAccHid = colorAccVis.withAlpha(100);
const fillAccVis = context.viewport.hilite.color.withAlpha(25);
builderAccVis.setSymbology(colorAccVis, fillAccVis, 3);
builderAccHid.setSymbology(colorAccHid, fillAccVis, 1, core_common_1.LinePixels.Code2);
if (points.length > 2)
builderAccHid.addShape(points);
builderAccVis.addLineString(points);
builderAccHid.addLineString(points);
context.addDecorationFromBuilder(builderAccVis);
context.addDecorationFromBuilder(builderAccHid);
}
/** @internal */
async onMouseMotion(ev) {
if (this._points.length > 0 && undefined !== ev.viewport)
ev.viewport.invalidateDecorations();
}
/** @internal */
async onDataButtonDown(ev) {
if (undefined === this.targetView)
return Tool_1.EventHandled.No;
if (this._points.length > 1 && !ev.isControlKey) {
const points = this.getClipPoints(ev);
if (points.length < 3)
return Tool_1.EventHandled.No;
const transform = core_geometry_1.Transform.createOriginAndMatrix(points[0], this._matrix);
transform.multiplyInversePoint3dArrayInPlace(points);
ViewClipTool.enableClipVolume(this.targetView);
if (!ViewClipTool.doClipToShape(this.targetView, points, transform, this._zLow, this._zHigh))
return Tool_1.EventHandled.No;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onNewClip(this.targetView);
await this.onReinitialize();
return Tool_1.EventHandled.Yes;
}
if (undefined === this._matrix && undefined === (this._matrix = AccuDraw_1.AccuDrawHintBuilder.getContextRotation(this.orientation, this.targetView)))
return Tool_1.EventHandled.No;
const currPt = ev.point.clone();
if (this._points.length > 0) {
const vp = ev.viewport;
const planePt = (vp ? AccuDraw_1.AccuDrawHintBuilder.projectPointToPlaneInView(currPt, this._points[0], this._matrix.getColumn(2), vp, true) : undefined);
if (undefined !== planePt)
currPt.setFrom(planePt);
}
this._points.push(currPt);
this.setupAndPromptForNextAction();
return Tool_1.EventHandled.No;
}
/** @internal */
async onUndoPreviousStep() {
if (0 === this._points.length)
return false;
this._points.pop();
this.setupAndPromptForNextAction();
return true;
}
}
exports.ViewClipByShapeTool = ViewClipByShapeTool;
/** A tool to define a clip volume for a view by specifying range corners
* @public
*/
class ViewClipByRangeTool extends ViewClipTool {
static toolId = "ViewClip.ByRange";
static iconSpec = "icon-section-range";
/** @internal */
_corner;
/** @internal */
showPrompt() {
const mainInstruction = ToolAssistance_1.ToolAssistance.createInstruction(this.iconSpec, Tool_1.CoreTools.translate(undefined === this._corner ? "ViewClip.ByRange.Prompts.FirstPoint" : "ViewClip.ByRange.Prompts.NextPoint"));
const mouseInstructions = [];
const touchInstructions = [];
if (!ToolAssistance_1.ToolAssistance.createTouchCursorInstructions(touchInstructions))
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.OneTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.LeftClick, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.TwoTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.RightClick, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
if (undefined !== this._corner)
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createKeyboardInstruction(ToolAssistance_1.ToolAssistance.createKeyboardInfo([ToolAssistance_1.ToolAssistance.ctrlKey, "Z"]), Tool_1.CoreTools.translate("ElementSet.Inputs.UndoLastPoint"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
const sections = [];
sections.push(ToolAssistance_1.ToolAssistance.createSection(mouseInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
sections.push(ToolAssistance_1.ToolAssistance.createSection(touchInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
const instructions = ToolAssistance_1.ToolAssistance.createInstructions(mainInstruction, sections);
IModelApp_1.IModelApp.notifications.setToolAssistance(instructions);
}
/** @internal */
setupAndPromptForNextAction() {
IModelApp_1.IModelApp.accuSnap.enableSnap(true);
super.setupAndPromptForNextAction();
}
getClipRange(range, transform, ev) {
if (undefined === this.targetView || undefined === this._corner)
return false;
// Creating clip aligned with ACS when ACS context lock is enabled...
const matrix = AccuDraw_1.AccuDrawHintBuilder.getContextRotation(AccuDraw_1.ContextRotationId.Top, this.targetView);
core_geometry_1.Transform.createOriginAndMatrix(this._corner, matrix, transform);
const pt1 = transform.multiplyInversePoint3d(this._corner);
const pt2 = transform.multiplyInversePoint3d(ev.point);
if (undefined === pt1 || undefined === pt2)
return false;
range.setFrom(core_geometry_1.Range3d.create(pt1, pt2));
return true;
}
/** @internal */
decorate(context) {
if (context.viewport !== this.targetView || undefined === this._corner)
return;
const ev = new Tool_1.BeButtonEvent();
IModelApp_1.IModelApp.toolAdmin.fillEventFromCursorLocation(ev);
if (undefined === ev.viewport)
return;
const range = core_geometry_1.Range3d.create();
const transform = core_geometry_1.Transform.createIdentity();
if (!this.getClipRange(range, transform, ev))
return;
const builderAccVis = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration, transform);
const builderAccHid = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldOverlay, transform);
const colorAccVis = EditManipulator_1.EditManipulator.HandleUtils.adjustForBackgroundColor(core_common_1.ColorDef.white, context.viewport);
const colorAccHid = colorAccVis.withAlpha(100);
builderAccVis.setSymbology(colorAccVis, core_common_1.ColorDef.black, 3);
builderAccHid.setSymbology(colorAccHid, core_common_1.ColorDef.black, 1, core_common_1.LinePixels.Code2);
builderAccVis.addRangeBox(range);
builderAccHid.addRangeBox(range);
context.addDecorationFromBuilder(builderAccVis);
context.addDecorationFromBuilder(builderAccHid);
}
/** @internal */
async onMouseMotion(ev) {
if (undefined !== this._corner && undefined !== ev.viewport)
ev.viewport.invalidateDecorations();
}
/** @internal */
async onDataButtonDown(ev) {
if (undefined === this.targetView)
return Tool_1.EventHandled.No;
if (undefined !== this._corner) {
const range = core_geometry_1.Range3d.create();
const transform = core_geometry_1.Transform.createIdentity();
if (!this.getClipRange(range, transform, ev))
return Tool_1.EventHandled.No;
ViewClipTool.enableClipVolume(this.targetView);
if (!ViewClipTool.doClipToRange(this.targetView, range, transform))
return Tool_1.EventHandled.No;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onNewClip(this.targetView);
await this.onReinitialize();
return Tool_1.EventHandled.Yes;
}
this._corner = ev.point.clone();
this.setupAndPromptForNextAction();
return Tool_1.EventHandled.No;
}
/** @internal */
async onUndoPreviousStep() {
if (undefined === this._corner)
return false;
this._corner = undefined;
this.setupAndPromptForNextAction();
return true;
}
}
exports.ViewClipByRangeTool = ViewClipByRangeTool;
/** A tool to define a clip volume for a view using the element aligned box or axis aligned box.
* @public
*/
class ViewClipByElementTool extends ViewClipTool {
_alwaysUseRange;
static toolId = "ViewClip.ByElement";
static iconSpec = "icon-section-element";
constructor(clipEventHandler, _alwaysUseRange = false) {
super(clipEventHandler);
this._alwaysUseRange = _alwaysUseRange;
}
/** @internal */
showPrompt() {
const mainInstruction = ToolAssistance_1.ToolAssistance.createInstruction(this.iconSpec, Tool_1.CoreTools.translate("ViewClip.ByElement.Prompts.FirstPoint"));
const mouseInstructions = [];
const touchInstructions = [];
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.OneTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.LeftClick, Tool_1.CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
touchInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.TwoTouchTap, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Touch));
mouseInstructions.push(ToolAssistance_1.ToolAssistance.createInstruction(ToolAssistance_1.ToolAssistanceImage.RightClick, Tool_1.CoreTools.translate("ElementSet.Inputs.Exit"), false, ToolAssistance_1.ToolAssistanceInputMethod.Mouse));
const sections = [];
sections.push(ToolAssistance_1.ToolAssistance.createSection(mouseInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
sections.push(ToolAssistance_1.ToolAssistance.createSection(touchInstructions, ToolAssistance_1.ToolAssistance.inputsLabel));
const instructions = ToolAssistance_1.ToolAssistance.createInstructions(mainInstruction, sections);
IModelApp_1.IModelApp.notifications.setToolAssistance(instructions);
}
/** @internal */
async onPostInstall() {
await super.onPostInstall();
if (undefined !== this.targetView && this.targetView.iModel.selectionSet.isActive) {
let useSelection = true;
this.targetView.iModel.selectionSet.elements.forEach((val) => {
if (core_bentley_1.Id64.isInvalid(val) || core_bentley_1.Id64.isTransient(val))
useSelection = false;
});
if (useSelection) {
await this.doClipToSelectedElements(this.targetView);
return;
}
}
this.initLocateElements(true, false, "default", Tool_1.CoordinateLockOverrides.All);
}
/** @internal */
async doClipToSelectedElements(viewport) {
if (await this.doClipToElements(viewport, viewport.iModel.selectionSet.elements, this._alwaysUseRange))
return true;
await this.exitTool();
return false;
}
async doClipToElements(viewport, ids, alwaysUseRange = false, modelId) {
try {
const placements = await viewport.iModel.elements.getPlacements(ids, { type: viewport.view.is3d() ? "3d" : "2d" });
if (0 === placements.length)
return false;
const displayTransform = modelId && 1 === placements.length ? viewport.view.computeDisplayTransform({ modelId, elementId: placements[0].elementId }) : undefined;
const range = new core_geometry_1.Range3d();
const transform = core_geometry_1.Transform.createIdentity();
if (!alwaysUseRange && 1 === placements.length) {
const placement = placements[0];
range.setFrom(placement instanceof core_common_1.Placement2d ? core_geometry_1.Range3d.createRange2d(placement.bbox, 0) : placement.bbox);
transform.setFrom(placement.transform); // Use ElementAlignedBox for single selection...
displayTransform?.multiplyTransformTransform(transform, transform);
}
else {
for (const placement of placements)
range.extendRange(placement.calculateRange());
if (displayTransform)
transform.setFrom(displayTransform);
}
if (range.isNull)
return false;
range.scaleAboutCenterInPlace(1.001); // pad range slightly...
if (range.isAlmostZeroX || range.isAlmostZeroY) {
if (range.isAlmostZeroZ)
return false;
// Invalid XY range for clip, see if XZ or YZ can be used instead...
const canUseXZ = !range.isAlmostZeroX;
const canUseYZ = !canUseXZ && !range.isAlmostZeroY;
if (!canUseXZ && !canUseYZ)
return false;
const zDir = canUseXZ ? core_geometry_1.Vector3d.unitY() : core_geometry_1.Vector3d.unitX();
const indices = core_geometry_1.Range3d.faceCornerIndices(canUseXZ ? 3 : 1);
const corners = range.corners();
const points = [];
for (const index of indices)
points.push(corners[index]);
transform.multiplyPoint3dArrayInPlace(points);
transform.multiplyVector(zDir, zDir);
transform.setFrom(core_geometry_1.Transform.createOriginAndMatrix(points[0], core_geometry_1.Matrix3d.createRigidHeadsUp(zDir)));
transform.multiplyInversePoint3dArrayInPlace(points);
ViewClipTool.enableClipVolume(viewport);
if (!ViewClipTool.doClipToShape(viewport, points, transform))
return false;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onNewClip(viewport);
await this.onReinitialize();
return true;
}
ViewClipTool.enableClipVolume(viewport);
if (!ViewClipTool.doClipToRange(viewport, range, transform))
return false;
if (undefined !== this._clipEventHandler)
this._clipEventHandler.onNewClip(viewport);
await this.onReinitialize();
return true;
}
catch {
return false;
}
}
/** @internal */
async onDataButtonDown(ev) {
if (undefined === this.targetView)
return Tool_1.EventHandled.No;
const hit = await IModelApp_1.IModelApp.locateManager.doLocate(new ElementLocateManager_1.LocateResponse(), true, ev.point, ev.viewport, ev.inputSource);
if (undefined === hit || !hit.isElementHit)
return Tool_1.EventHandled.No;
return await this.doClipToElements(this.targetView, hit.sourceId, this._alwaysUseRange, hit.modelId) ? Tool_1.EventHandled.Yes : Tool_1.EventHandled.No;
}
}
exports.ViewClipByElementTool = ViewClipByElementTool;
/** @internal Interactive tool base class to modify a view's clip */
class ViewClipModifyTool extends EditManipulator_1.EditManipulator.HandleTool {
_anchorIndex;
_ids;
_controls;
_clipView;
_clip;
_viewRange;
_restoreClip = true;
_currentDistance = 0.0;
_clipStyle;
constructor(manipulator, clip, vp, hitId, ids, controls) {
super(manipulator);
this._anchorIndex = ids.indexOf(hitId);
this._ids = ids;
this._controls = controls;
this._clipView = vp;
this._clip = clip;
this._viewRange = vp.computeViewRange();
// Don't request section-cut graphics while the user is modifying the clip. We'll rest