@itwin/measure-tools-react
Version:
Frontend framework and tools for measurements
217 lines • 11.8 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { AxisOrder, Matrix3d, Vector3d } from "@itwin/core-geometry";
import { AccuDrawHintBuilder, BeButtonEvent, EventHandled, IModelApp, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
import { FeatureTracking, MeasureToolsFeatures } from "../api/FeatureTracking.js";
import { MeasurementToolBase } from "../api/MeasurementTool.js";
import { MeasurementViewTarget } from "../api/MeasurementViewTarget.js";
import { MeasureAreaToolModel } from "../toolmodels/MeasureAreaToolModel.js";
import { MeasureTools } from "../MeasureTools.js";
import { SheetMeasurementsHelper } from "../api/SheetMeasurementHelper.js";
import { ViewHelper } from "../api/ViewHelper.js";
export class MeasureAreaTool extends MeasurementToolBase {
get allowedDrawingTypes() {
return [SheetMeasurementsHelper.DrawingType.CrossSection, SheetMeasurementsHelper.DrawingType.Plan];
}
static get flyover() {
return MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.flyover");
}
static get description() {
return MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.description");
}
static get keyin() {
return MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.keyin");
}
get feature() {
return MeasureToolsFeatures.Tools_MeasureArea;
}
constructor(enableSheetMeasurements = false, allowedViewportCallback = (() => true)) {
super(allowedViewportCallback);
this._enableSheetMeasurements = enableSheetMeasurements;
}
async onRestartTool() {
const tool = new MeasureAreaTool(this._enableSheetMeasurements, this._allowedViewportCallback);
if (await tool.run())
return;
return this.exitTool();
}
async onPostInstall() {
await super.onPostInstall();
}
async onReinitialize() {
await super.onReinitialize();
AccuDrawHintBuilder.deactivate();
}
async onUndoPreviousStep() {
if (this.toolModel.popMeasurementPoint()) {
const ev = new BeButtonEvent();
this.getCurrentButtonEvent(ev);
// Update the dynamic polygon
await this.onMouseMotion(ev);
this._sendHintsToAccuDraw(ev);
this.updateToolAssistance();
return true;
}
return super.onUndoPreviousStep();
}
async onDataButtonDown(ev) {
if (!ev.viewport)
return EventHandled.No;
const viewType = MeasurementViewTarget.classifyViewport(ev.viewport);
// We only want the sheetMeasurement logic to trigger when the first point is set but it needs to be after the measurement is created
// this boolean helps us ensure that this is the case
let isFirstPoint = false;
if (MeasureAreaToolModel.State.SetMeasurementViewport ===
this.toolModel.currentState) {
this.toolModel.setMeasurementViewport(viewType);
isFirstPoint = true;
}
this.toolModel.addPoint(viewType, ev.point, false);
await this.sheetMeasurementsDataButtonDown(ev, isFirstPoint);
if (undefined === this.toolModel.dynamicMeasurement) {
await this.onReinitialize();
}
else {
this._sendHintsToAccuDraw(ev);
this.updateToolAssistance();
}
ev.viewport.invalidateDecorations();
return EventHandled.Yes;
}
async sheetMeasurementsDataButtonDown(ev, isFirstPoint) {
if (!ev.viewport)
return;
if (this._enableSheetMeasurements) {
if (this.toolModel.drawingMetadata?.drawingId === undefined && ev.viewport.view.id !== undefined && isFirstPoint) {
const drawingInfo = await SheetMeasurementsHelper.getDrawingId(this.iModel, ev.viewport.view.id, ev.point);
this.toolModel.sheetViewId = ev.viewport.view.id;
if (drawingInfo?.drawingId !== undefined && drawingInfo.origin !== undefined && drawingInfo.worldScale !== undefined) {
const data = { origin: drawingInfo.origin, drawingId: drawingInfo.drawingId, worldScale: drawingInfo.worldScale, extents: drawingInfo.extents };
this.toolModel.drawingMetadata = data;
}
}
}
}
isValidLocation(ev, isButtonEvent) {
if (!super.isValidLocation(ev, isButtonEvent))
return false;
if (!this._enableSheetMeasurements || !ev.viewport?.view.isSheetView())
return true;
if (!SheetMeasurementsHelper.checkIfAllowedDrawingType(ev.viewport, ev.point, this.allowedDrawingTypes))
return false;
if (this.toolModel.drawingMetadata?.drawingId === undefined || this.toolModel.drawingMetadata?.origin === undefined || this.toolModel.drawingMetadata?.extents === undefined)
return true;
return SheetMeasurementsHelper.checkIfInDrawing(ev.point, this.toolModel.drawingMetadata?.origin, this.toolModel.drawingMetadata?.extents);
}
_sendHintsToAccuDraw(ev) {
const dynamicMeasurement = this.toolModel.dynamicMeasurement;
if (undefined === ev.viewport || undefined === dynamicMeasurement)
return;
const points = dynamicMeasurement.polygonPoints;
if (0 === points.length)
return;
const hints = new AccuDrawHintBuilder();
hints.setOrigin(points[points.length - 1]);
if (1 === points.length) {
hints.setModeRectangular();
}
else {
// Adjust to keep orientation defined by the last 2 points and the surface normal (if any).
const snapDetail = IModelApp.accuSnap.getCurrSnapDetail();
if (undefined !== snapDetail && undefined !== snapDetail.normal) {
const normal = Vector3d.createZero();
const xVector = Vector3d.createStartEnd(points[points.length - 2], points[points.length - 1]);
const zVector = ev.viewport.view.getZVector();
if (zVector.dotProduct(snapDetail.normal) < 0.0)
normal.setFrom(snapDetail.normal);
else
snapDetail.normal.negate(normal);
const mat = Matrix3d.createRigidFromColumns(xVector, normal, AxisOrder.XZY);
if (undefined !== mat)
hints.setMatrix(mat);
}
}
hints.sendHints(false);
IModelApp.toolAdmin.setCursor(IModelApp.viewManager.crossHairCursor);
}
async onResetButtonDown(ev) {
// Attempt to close polygon
if (this.toolModel.tryCommitMeasurement()) {
if (this._enableSheetMeasurements && ev.viewport !== undefined && ViewHelper.isSheetView(ev.viewport))
FeatureTracking.notifyFeature(MeasureToolsFeatures.Tools_MeasureDistance_createdInSheet);
await this.onReinitialize();
return EventHandled.Yes;
}
return super.onResetButtonDown(ev);
}
async onMouseMotion(ev) {
if (!ev.viewport)
return;
const viewType = MeasurementViewTarget.classifyViewport(ev.viewport);
if (this.toolModel.addPoint(viewType, ev.point, true))
ev.viewport.invalidateDecorations();
}
createToolModel() {
return new MeasureAreaToolModel();
}
decorate(context) {
super.decorate(context);
if (this._enableSheetMeasurements && this.toolModel.drawingMetadata?.origin !== undefined && this.toolModel.drawingMetadata?.extents !== undefined) {
context.addDecorationFromBuilder(SheetMeasurementsHelper.getDrawingContourGraphic(context, this.toolModel.drawingMetadata?.origin, this.toolModel.drawingMetadata?.extents));
}
}
updateToolAssistance() {
const hasPoints = undefined !== this.toolModel.dynamicMeasurement;
const hasEnoughPoints = hasPoints && this.toolModel.hasEnoughPoints;
let promptMainInstruction;
if (hasEnoughPoints)
promptMainInstruction = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.mainInstructionClose");
else
promptMainInstruction = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.mainInstruction");
const promptClickTap = MeasureTools.localization.getLocalizedString("MeasureTools:tools.GenericPrompts.acceptPoint");
let promptRightClick;
if (hasEnoughPoints)
promptRightClick = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.rightClickCloseShape");
else if (hasPoints)
promptRightClick = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.rightClickClearShape");
else
promptRightClick = MeasureTools.localization.getLocalizedString("MeasureTools:tools.GenericPrompts.restart");
const mainInstruction = ToolAssistance.createInstruction(this.iconSpec, promptMainInstruction);
const mouseInstructions = [];
const touchInstructions = [];
if (!ToolAssistance.createTouchCursorInstructions(touchInstructions)) {
touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, promptClickTap, false, ToolAssistanceInputMethod.Touch));
if (hasEnoughPoints) {
const tmp = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.oneTouchTapClose");
touchInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, tmp, false, ToolAssistanceInputMethod.Touch));
}
}
mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, promptClickTap, false, ToolAssistanceInputMethod.Mouse));
if (hasEnoughPoints) {
const tmp = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.leftClickClose");
mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.LeftClick, tmp, false, ToolAssistanceInputMethod.Mouse));
}
mouseInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.RightClick, promptRightClick, false, ToolAssistanceInputMethod.Mouse));
if (undefined !== this.toolModel.dynamicMeasurement) {
const undoPointText = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureArea.undoLastPoint");
mouseInstructions.push(this.createMouseUndoInstruction(undoPointText));
}
else {
if (this.toolModel.canUndo)
mouseInstructions.push(this.createMouseUndoInstruction());
if (this.toolModel.canRedo)
mouseInstructions.push(this.createMouseRedoInstruction());
}
const sections = [
ToolAssistance.createSection(mouseInstructions, ToolAssistance.inputsLabel),
ToolAssistance.createSection(touchInstructions, ToolAssistance.inputsLabel),
];
const instructions = ToolAssistance.createInstructions(mainInstruction, sections);
IModelApp.notifications.setToolAssistance(instructions);
}
}
MeasureAreaTool.toolId = "MeasureTools.MeasureArea";
MeasureAreaTool.iconSpec = "icon-measure-2d";
//# sourceMappingURL=MeasureAreaTool.js.map