UNPKG

@itwin/core-frontend

Version:
917 lines • 96.8 kB
"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