From 938b17fc72a24ab0482c8dbb77b002b9238235c3 Mon Sep 17 00:00:00 2001 From: lindsay Date: Wed, 11 Oct 2023 22:42:28 +0200 Subject: [PATCH 1/3] Make measurements controllers pluggable --- .../angle_createWithMouse_nosnapping.html | 82 ++ .../angle_createWithMouse_snapping.html | 49 +- .../distance_createWithMouse_snapping.html | 31 +- ...istance_createWithMouse_snapping_Lyon.html | 45 +- ...istance_createWithMouse_snapping_lens.html | 255 ---- ...distance_createWithMouse_snapping_vbo.html | 276 ---- examples/measurement/index.html | 25 +- src/extras/PointerLens/PointerLens.js | 39 +- .../AngleMeasurementsControl.js | 1261 +---------------- .../AngleMeasurementsMouseControl.js | 393 +++++ .../AngleMeasurementsPlugin.js | 50 +- src/plugins/AngleMeasurementsPlugin/index.js | 4 +- .../DistanceMeasurement.js | 2 +- .../DistanceMeasurementsControl.js | 984 +------------ .../DistanceMeasurementsMouseControl.js | 354 +++++ .../DistanceMeasurementsPlugin.js | 59 +- .../DistanceMeasurementsPlugin/index.js | 4 +- .../scene/CameraControl/CameraControl.js | 24 +- .../lib/controllers/PickController.js | 4 +- src/viewer/scene/scene/Scene.js | 8 +- src/viewer/scene/webgl/Renderer.js | 14 +- 21 files changed, 1129 insertions(+), 2834 deletions(-) create mode 100644 examples/measurement/angle_createWithMouse_nosnapping.html delete mode 100644 examples/measurement/distance_createWithMouse_snapping_lens.html delete mode 100644 examples/measurement/distance_createWithMouse_snapping_vbo.html create mode 100644 src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js create mode 100644 src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js diff --git a/examples/measurement/angle_createWithMouse_nosnapping.html b/examples/measurement/angle_createWithMouse_nosnapping.html new file mode 100644 index 0000000000..f6851e92fb --- /dev/null +++ b/examples/measurement/angle_createWithMouse_nosnapping.html @@ -0,0 +1,82 @@ + + + + + + + xeokit Example + + + + + + + +
+ +

AngleMeasurementPlugin with AngleMeasurementsMouseControl and PointerLens

+

Click on the model to create angle measurements, with vertex and edge snapping

+

In this example, we're loading a BIM model from the file system, then creating angle measurements wherever the + user clicks on the model with the mouse.

+

Components Used

+ +

Resources

+ +
+ + + \ No newline at end of file diff --git a/examples/measurement/angle_createWithMouse_snapping.html b/examples/measurement/angle_createWithMouse_snapping.html index 689bf6894b..6e42c3249e 100644 --- a/examples/measurement/angle_createWithMouse_snapping.html +++ b/examples/measurement/angle_createWithMouse_snapping.html @@ -14,10 +14,10 @@
-

AngleMeasurementsPlugin

-

Click on the model to create angle measurements, with snap-to-vertex

+

AngleMeasurementPlugin with AngleMeasurementsMouseControl and PointerLens

+

Click on the model to create angle measurements, with vertex and edge snapping

In this example, we're loading a BIM model from the file system, then creating angle measurements wherever the - user clicks on the model.

+ user clicks on the model with the mouse.

Components Used

Resources

@@ -44,15 +48,7 @@

Resources

\ No newline at end of file diff --git a/examples/measurement/distance_createWithMouse_snapping.html b/examples/measurement/distance_createWithMouse_snapping.html index 76f0f05728..930143fd8a 100644 --- a/examples/measurement/distance_createWithMouse_snapping.html +++ b/examples/measurement/distance_createWithMouse_snapping.html @@ -85,10 +85,10 @@
-

DistanceMeasurementPlugin with vertex-snapping

-

Click on the model to create distance measurements

+

DistanceMeasurementPlugin with DistanceMeasurementsMouseControl and PointerLens

+

Click on the model to create distance measurements, with vertex and edge snapping

In this example, we're loading a BIM model from the file system, then creating distance measurements wherever the - user clicks on the model.

+ user clicks on the model with the mouse.

Components Used

  • @@ -99,6 +99,14 @@

    Components Used

    DistanceMeasurementsPlugin
  • +
  • + DistanceMeasurementsMouseControl +
  • +
  • + PointerLens +
  • XKTLoaderPlugin @@ -119,7 +127,7 @@

    Resources

    // Import the modules we need for this example //------------------------------------------------------------------------------------------------------------------ - import {Viewer, XKTLoaderPlugin, ContextMenu, DistanceMeasurementsPlugin, PointerLens} from "../../dist/xeokit-sdk.es.js"; + import {Viewer, XKTLoaderPlugin, ContextMenu, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, PointerLens} from "../../dist/xeokit-sdk.es.js"; //------------------------------------------------------------------------------------------------------------------ // Create a Viewer and arrange the camera @@ -159,14 +167,16 @@

    Resources

    }); //------------------------------------------------------------------------------------------------------------------ - // DistanceMeasurementsPlugin + // DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl and PointerLens //------------------------------------------------------------------------------------------------------------------ - const pointerLens = new PointerLens(viewer); + const distanceMeasurements = new DistanceMeasurementsPlugin(viewer); - const distanceMeasurements = new DistanceMeasurementsPlugin(viewer, { - pointerLens - }); + const distanceMeasurementsMouseControl = new DistanceMeasurementsMouseControl(distanceMeasurements, { + pointerLens : new PointerLens(viewer) + }) + + distanceMeasurementsMouseControl.activate(); //------------------------------------------------------------------------------------------------------------------ // Create a context menu to delete and configure measurements @@ -272,9 +282,6 @@

    Resources

    e.event.preventDefault(); }); - distanceMeasurements.control.activate(); - - window.viewer = viewer; diff --git a/examples/measurement/distance_createWithMouse_snapping_Lyon.html b/examples/measurement/distance_createWithMouse_snapping_Lyon.html index dad1cf18bd..7319b09b9d 100644 --- a/examples/measurement/distance_createWithMouse_snapping_Lyon.html +++ b/examples/measurement/distance_createWithMouse_snapping_Lyon.html @@ -43,20 +43,30 @@
    -

    Logarithmic depth buffer enabled

    -

    XKT model with double-precision geometry, rendered with logarithmic buffer enabled


    +

    DistanceMeasurementPlugin with DistanceMeasurementsMouseControl and Pointerlens

    +

    Click on the model to create distance measurements, with vertex and edge snapping

    +

    In this example, we're loading a model of the City of Lyon from the file system, then creating distance measurements wherever the + user clicks on the model with the mouse.

    Stats

    -
      -
    • -
      Loading JavaScript modules...
      -
    • -
    -

    Components used

    +
    Loading JavaScript modules...
    +

    Components Used

    @@ -88,6 +97,7 @@

    Resources

    buildPlaneGeometry, FastNavPlugin, DistanceMeasurementsPlugin, + DistanceMeasurementsMouseControl, PointerLens } from "../../dist/xeokit-sdk.es.js"; @@ -225,19 +235,20 @@

    Resources

    collidable: false }); - //---------------------------------------------------------------------------------------------------------------------- - // Create distance measurements plugin - //---------------------------------------------------------------------------------------------------------------------- - - const pointerLens = new PointerLens(viewer, { - zoomLevel: 2 - }); + //------------------------------------------------------------------------------------------------------------------ + // DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl and PointerLens + //------------------------------------------------------------------------------------------------------------------ const distanceMeasurements = new DistanceMeasurementsPlugin(viewer, { defaultAxisVisible: false, // <<------------ Hide axis wires - pointerLens }); + const distanceMeasurementsMouseControl = new DistanceMeasurementsMouseControl(distanceMeasurements, { + pointerLens : new PointerLens(viewer) + }) + + distanceMeasurementsMouseControl.activate(); + //---------------------------------------------------------------------------------------------------------------------- // Load nine CityGML models //---------------------------------------------------------------------------------------------------------------------- diff --git a/examples/measurement/distance_createWithMouse_snapping_lens.html b/examples/measurement/distance_createWithMouse_snapping_lens.html deleted file mode 100644 index 6dcac86450..0000000000 --- a/examples/measurement/distance_createWithMouse_snapping_lens.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - xeokit Example - - - - - - - - -
    - -
    -
    -

    Snap-to-vertex

    -

    Hover over model to snap pointer to the nearest vertex

    -

    This page demonstrates a technique developed by Toni Marti, where we use - a lens to help position the pointer as - we snap it to the nearest vertex.

    -

    Components used

    - -

    Resources

    - -
    - - - - diff --git a/examples/measurement/distance_createWithMouse_snapping_vbo.html b/examples/measurement/distance_createWithMouse_snapping_vbo.html deleted file mode 100644 index f2aae8d033..0000000000 --- a/examples/measurement/distance_createWithMouse_snapping_vbo.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - xeokit Example - - - - - - - - - -
    - -

    DistanceMeasurementPlugin with vertex-snapping

    -

    Click on the model to create distance measurements

    -

    In this example, we're loading a BIM model from the file system, then creating distance measurements wherever the - user clicks on the model.

    -

    Components Used

    - -

    Resources

    - -
    - - - \ No newline at end of file diff --git a/examples/measurement/index.html b/examples/measurement/index.html index f702057e3f..7de3774ba5 100644 --- a/examples/measurement/index.html +++ b/examples/measurement/index.html @@ -258,27 +258,28 @@ "Measuring Distances": [ - "# Models with existing distance measurements", + "# Creating distance measurements with mouse", + ["distance_createWithMouse_snapping", "Click with mouse on model to create distance measurements"], + ["distance_createWithMouse_snapping_Lyon", "Click with mouse on model to create distance measurements"], - ["distance_modelWithMeasurements", "BIM model with distance measurements"], - ["distance_modelWithMeasurements_hideAxisWires", "BIM model with distance measurements and axis-aligned wires hidden"], - ["distance_unitsAndScale", "BIM model with configured units (see source code)"], - - "# Creating distance measurements", + ["distance_createWithMouse_nosnapping", "Click with mouse on model to create distance measurements, snapping disabled"], - ["distance_createWithMouse_snapping", "Click on model to create distance measurements; long-click to snap to nearest vertex or edge"], - ["distance_createWithMouse_snapping_Lyon", "Click on model to create distance measurements; long-click to snap to nearest vertex or edge"] + "# Creating distance measurements programmatically", + ["distance_modelWithMeasurements", "Creating distance measurements programmatically"], + ["distance_modelWithMeasurements_hideAxisWires", "BIM model with distance measurements and axis-aligned wires hidden"], + ["distance_unitsAndScale", "BIM model with configured units (see source code)"] ], "Measuring Angles": [ - "# Models with existing angle measurements", + "# Creating angle measurements with mouse", + ["angle_createWithMouse_snapping", "Click with mouse on model to create angle measurements"], - ["angle_modelWithMeasurements", "IFC 2x3 BIM model with angle measurements"], - "# Creating angle measurements", + ["angle_createWithMouse_nosnapping", "Click with mouse on model to create angle measurements, snapping disabled"], - ["angle_createWithMouse_snapping", "Click or tap on the model to create angle measurements, long-click to snap to nearest vertex or edge"] + "# Creating angle measurements programmatically", + ["angle_modelWithMeasurements", "Creating angle measurements programmatically"] ] }; diff --git a/src/extras/PointerLens/PointerLens.js b/src/extras/PointerLens/PointerLens.js index 6cd8277544..999f478c1d 100644 --- a/src/extras/PointerLens/PointerLens.js +++ b/src/extras/PointerLens/PointerLens.js @@ -1,6 +1,43 @@ /** - * A PointerLens shows a magnified view of a {@link Viewer's | Viewer} canvas, centered at the position of the + * A PointerLens shows a magnified view of a {@link Viewer}'s canvas, centered at the position of the * mouse or touch pointer. + * + * This component is used by {@link DistanceMeasurementsControl} and {@link AngleMeasurementsControl} + * to help position the pointer when snap-to-vertex or snap-toedge is enabled. + * + * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/measurements/#distance_modelWithMeasurements)] + * + * ````JavaScript + * + * import {Viewer, XKTLoaderPlugin, AngleMeasurementsPlugin, AngleMeasurementsMouseControl, PointerLens} from "../../dist/xeokit-sdk.es.js"; + * + * const viewer = new Viewer({ + * canvasId: "myCanvas", + * dtxEnabled: true + * }); + * + * viewer.camera.eye = [-3.93, 2.85, 27.01]; + * viewer.camera.look = [4.40, 3.72, 8.89]; + * viewer.camera.up = [-0.01, 0.99, 0.039]; + * + * const xktLoader = new XKTLoaderPlugin(viewer); + * + * const sceneModel = xktLoader.load({ + * id: "myModel", + * src: "../../assets/models/xkt/v10/glTF-Embedded/Duplex_A_20110505.glTFEmbedded.xkt", + * edges: true + * }); + * + * const angleMeasurements = new AngleMeasurementsPlugin(viewer); + * + * const angleMeasurementsMouseControl = new AngleMeasurementsMouseControl(angleMeasurements, { + * pointerLens : new PointerLens(viewer, { + * zoomFactor: 2 + * }) + * }) + * + * angleMeasurementsMouseControl.activate(); + * ```` */ export class PointerLens { diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js index f268ddace2..36ef2cd8fe 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js @@ -1,1270 +1,103 @@ -import {Component} from "../../viewer/scene/Component.js"; -import {math} from "../../viewer/scene/math/math.js"; - -const MOUSE_FINDING_ORIGIN = 0; -const MOUSE_FINDING_CORNER = 1; -const MOUSE_FINDING_TARGET = 2; - -const TOUCH_FINDING_ORIGIN = 0; -const QUICK_TOUCH_FINDING_ORIGIN = 1; -const LONG_TOUCH_FINDING_ORIGIN = 2; -const TOUCH_FINDING_CORNER = 3; -const QUICK_TOUCH_MOUSE_FINDING_CORNER = 4; -const LONG_TOUCH_MOUSE_FINDING_CORNER = 5; -const TOUCH_FINDING_TARGET = 6; -const QUICK_TOUCH_FINDING_TARGET = 7; -const LONG_TOUCH_FINDING_TARGET = 8; -const TOUCH_CANCELING = 9; +import {Component} from "../../viewer"; /** - * Creates {@link AngleMeasurement}s from mouse and touch input. - * - * Belongs to a {@link AngleMeasurementsPlugin}. Located at {@link AngleMeasurementsPlugin#control}. - * - * Once the AngleMeasurementControl is activated, the first click on any {@link Entity} begins constructing a {@link AngleMeasurement}, fixing its origin to that Entity. The next click on any Entity will complete the AngleMeasurement, fixing its target to that second Entity. The AngleMeasurementControl will then wait for the next click on any Entity, to begin constructing another AngleMeasurement, and so on, until deactivated. + * Creates {@link AngleMeasurement}s in an {@link AngleMeasurementsPlugin} from user input. * - * See {@link AngleMeasurementsPlugin} for more info. + * @interface + * @abstract */ -class AngleMeasurementsControl extends Component { +export class AngleMeasurementsControl extends Component { /** - * @private - */ - constructor(plugin, cfg = {}) { - - super(plugin.viewer.scene); - - /** - * The {@link AngleMeasurementsPlugin} that owns this AngleMeasurementsControl. - * @type {AngleMeasurementsPlugin} - */ - this.plugin = plugin; - - this._active = false; - this._mouseState = MOUSE_FINDING_ORIGIN; - this._touchState = TOUCH_FINDING_ORIGIN; - - this._currentAngleMeasurement = null; - - // Add a marker to the canvas - const markerDiv = document.createElement('div'); - const canvas = this.scene.canvas.canvas; - canvas.parentNode.insertBefore(markerDiv, canvas); - - markerDiv.style.background = "black"; - markerDiv.style.border = "2px solid blue"; - markerDiv.style.borderRadius = "10px"; - markerDiv.style.width = "5px"; - markerDiv.style.height = "5px"; - markerDiv.style.margin = "-200px -200px"; - markerDiv.style.zIndex = "100"; - markerDiv.style.position = "absolute"; - markerDiv.style.pointerEvents = "none"; - - this.markerDiv = markerDiv; - - // Event handles from CameraControl - this._onMouseHoverSurface = null; - this._onHoverNothing = null; - this._onMouseHoverOff = null; - this._onPickedNothing = null; - this._onPickedSurface = null; - - // Event handles from Scene.input - this._onInputMouseDown = null; - this._onInputMouseUp = null; - - // Event handles from Canvas element - this._onCanvasTouchStart = null; - this._onCanvasTouchEnd = null; - - this._mobileModeLongPressTimeMs = 500; - - this._snapEdge = cfg.snapEdge !== false; - this._snapVertex = cfg.snapVertex !== false; - } - - /** Gets if this AngleMeasurementsControl is currently active, where it is responding to input. + * Gets if this AngleMeasurementsControl is currently active, where it is responding to input. * - * @returns {Boolean} + * @returns {boolean} True if this AngleMeassurementsControl is active. + * @abstract */ get active() { - return this._active; } /** - * Sets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * Gets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * * This is `true` by default. - * @param snapVertex + * + * @returns {boolean} + * @abstract */ - set snapVertex(snapVertex) { - this._snapVertex = snapVertex; + get snapToVertex() { } /** - * Gets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * Sets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * * This is `true` by default. - * @returns {*} + * + * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this AngleMeasurementsControl. + * @abstract */ - get snapVertex() { - return this._snapVertex; + set snapToVertex(snapToVertex) { } /** - * Sets whether snap-to-edge is enabled for this AngleMeasurementsControl. + * Gets whether snap-to-edge is enabled for this AngleMeasurementsControl. + * * This is `true` by default. - * @param snapEdge + * + * @returns {boolean} + * @abstract */ - set snapEdge(snapEdge) { - this._snapEdge = snapEdge; + get snapToEdge() { } /** - * Gets whether snap-to-edge is enabled for this AngleMeasurementsControl. + * Sets whether snap-to-edge is enabled for this AngleMeasurementsControl. + * * This is `true` by default. - * @returns {*} + * + * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this AngleMeasurementsControl. + * @abstract */ - get snapEdge() { - return this._snapEdge; + set snapToEdge(snapToEdge) { } /** - * Activates this AngleMeasurementsControl, ready to respond to input. + * Activates this AngleMeasurementsMouseControl, ready to respond to input. + * + * @abstract */ activate() { - - if (this._active) { - return; - } - - const plugin = this.plugin; - const scene = this.scene; - const input = scene.input; - const canvas = scene.canvas.canvas; - - const clickTolerance = 20; - - const cameraControl = this.plugin.viewer.cameraControl; - - const pointerLens = this.plugin.pointerLens; - - //---------------------------------------------------------------------------------------------------- - // Mouse input - //---------------------------------------------------------------------------------------------------- - - { - let mouseHovering = false; - let mouseHoverEntity = false; - let lastMouseCanvasX = 0; - let lastMouseCanvasY = 0; - - const mouseWorldPos = math.vec3(); - const mouseHoverCanvasPos = math.vec2(); - - this._currentAngleMeasurement = null; - - this._onMouseHoverSurface = cameraControl.on("hoverSnapOrSurface", event => { - if (event.snappedToVertex || event.snappedToEdge) { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = true; - } - this.markerDiv.style.background = "greenyellow"; - this.markerDiv.style.border = "2px solid green"; - } else { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = false; - } - this.markerDiv.style.background = "pink"; - this.markerDiv.style.border = "2px solid red"; - } - mouseHovering = true; - mouseHoverEntity = event.entity; - mouseWorldPos.set(event.worldPos); - mouseHoverCanvasPos.set(event.canvasPos); - switch (this._mouseState) { - case MOUSE_FINDING_ORIGIN: - this.markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; - this.markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; - break; - case MOUSE_FINDING_CORNER: - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.angleVisible = false; - this._currentAngleMeasurement.corner.worldPos = event.worldPos; - } - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - canvas.style.cursor = "pointer"; - break; - case MOUSE_FINDING_TARGET: - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.angleVisible = true; - this._currentAngleMeasurement.target.worldPos = event.worldPos; - } - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - canvas.style.cursor = "pointer"; - break; - } - }); - - this._onInputMouseDown = input.on("mousedown", (coords) => { - lastMouseCanvasX = coords[0]; - lastMouseCanvasY = coords[1]; - }); - - this._onInputMouseUp = input.on("mouseup", (coords) => { - if (coords[0] > lastMouseCanvasX + clickTolerance || - coords[0] < lastMouseCanvasX - clickTolerance || - coords[1] > lastMouseCanvasY + clickTolerance || - coords[1] < lastMouseCanvasY - clickTolerance) { - return; - } - switch (this._mouseState) { - case MOUSE_FINDING_ORIGIN: - if (mouseHovering) { - this._currentAngleMeasurement = this.plugin.createMeasurement({ - id: math.createUUID(), - origin: { - entity: mouseHoverEntity, - worldPos: mouseWorldPos - }, - corner: { - entity: mouseHoverEntity, - worldPos: mouseWorldPos - }, - target: { - entity: mouseHoverEntity, - worldPos: mouseWorldPos - }, - approximate: true - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.angleVisible = false; - this._mouseState = MOUSE_FINDING_CORNER; - this.fire("measurementStart", this._currentAngleMeasurement); - } - break; - case MOUSE_FINDING_CORNER: - if (mouseHovering) { - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.angleVisible = true; - this._mouseState = MOUSE_FINDING_TARGET; - } else { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - this._mouseState = MOUSE_FINDING_ORIGIN - this.fire("measurementCancel", this._currentAngleMeasurement); - } - } - break; - case MOUSE_FINDING_TARGET: - if (mouseHovering) { - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.angleVisible = true; - this.fire("measurementEnd", this._currentAngleMeasurement); - this._currentAngleMeasurement = null; - this._mouseState = MOUSE_FINDING_ORIGIN; - } else { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - this._mouseState = MOUSE_FINDING_ORIGIN; - this.fire("measurementCancel", this._currentAngleMeasurement); - } - } - break; - } - }); - - this._onMouseHoverOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { - mouseHovering = false; - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos; - pointerLens.cursorPos = event.cursorPos; - pointerLens.snapped = false; - } - this.markerDiv.style.marginLeft = `-100px`; - this.markerDiv.style.marginTop = `-100px`; - if (this._currentAngleMeasurement) { - switch (this._mouseState) { - case MOUSE_FINDING_ORIGIN: - this._currentAngleMeasurement.originVisible = false; - break; - case MOUSE_FINDING_CORNER: - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - break; - case MOUSE_FINDING_TARGET: - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - break; - - } - canvas.style.cursor = "default"; - } - }); - } - - //---------------------------------------------------------------------------------------------------- - // Touch input always assumes mobile devices - //---------------------------------------------------------------------------------------------------- - - { - let longTouchTimeout = null; - - const disableCameraMouseControl = () => { - cameraControl.active = false; - } - - const enableCameraMouseControl = () => { - cameraControl.active = true; - } - - // const scheduleSurfacePickIfNeeded = () => { - // if (!cameraControl._handlers[2]._active) { - // cameraControl._controllers.pickController.schedulePickSurface = true; - // cameraControl._controllers.pickController.update(); - // } - // } - - this._touchState = TOUCH_FINDING_ORIGIN; - - const touchStartCanvasPos = math.vec2(); - const touchMoveCanvasPos = math.vec2(); - const touchEndCanvasPos = math.vec2(); - const pointerWorldPos = math.vec3(); - - canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { - - const currentNumTouches = event.touches.length; - - if (currentNumTouches !== 1) { - return; - } - - const touchX = event.touches[0].clientX; - const touchY = event.touches[0].clientY; - - touchStartCanvasPos.set([touchX, touchY]); - touchMoveCanvasPos.set([touchX, touchY]); - - switch (this._touchState) { - - case TOUCH_FINDING_ORIGIN: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - this._touchState = TOUCH_CANCELING; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_CANCELING") - return; - } - if (currentNumTouches === 1) { // One finger down - longTouchTimeout = setTimeout(() => { - longTouchTimeout = null; - if (currentNumTouches !== 1 || - touchMoveCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchMoveCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchMoveCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchMoveCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - return; // Has moved - } - // Long touch - disableCameraMouseControl(); - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = touchStartCanvasPos; - pointerLens.cursorPos = touchStartCanvasPos; - } - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - const snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - if (!this._currentAngleMeasurement) { - this._currentAngleMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: snapPickResult.snappedWorldPos - }, - corner: { - worldPos: snapPickResult.snappedWorldPos - }, - target: { - worldPos: snapPickResult.snappedWorldPos - } - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - this._currentAngleMeasurement.origin.worldPos = snapPickResult.snappedWorldPos; - } - this.fire("measurementStart", this._currentAngleMeasurement); - this._touchState = LONG_TOUCH_FINDING_ORIGIN; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> LONG_TOUCH_FINDING_ORIGIN") - } else { - const pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - if (!this._currentAngleMeasurement) { - this._currentAngleMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - corner: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - this._currentAngleMeasurement.origin.worldPos = pickResult.worldPos; - } - this.fire("measurementStart", this._currentAngleMeasurement); - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_ORIGIN; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> LONG_TOUCH_FINDING_ORIGIN") - }, this._mobileModeLongPressTimeMs); - this._touchState = QUICK_TOUCH_FINDING_ORIGIN; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> QUICK_TOUCH_FINDING_ORIGIN") - } - break; - - case TOUCH_FINDING_CORNER: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - this._touchState = TOUCH_CANCELING; - // console.log("touchstart: this._touchState= TOUCH_FINDING_CORNER -> TOUCH_CANCELING") - return; - } - if (currentNumTouches === 1) { // One finger down - longTouchTimeout = setTimeout(() => { - longTouchTimeout = null; - if (currentNumTouches !== 1 || - touchMoveCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchMoveCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchMoveCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchMoveCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - return; // Has moved - } - // Long touch - disableCameraMouseControl(); - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = touchStartCanvasPos; - pointerLens.cursorPos = touchStartCanvasPos; - } - - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - const snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - this._currentAngleMeasurement.corner.worldPos = snapPickResult.snappedWorldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - const pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - this._currentAngleMeasurement.corner.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_MOUSE_FINDING_CORNER; - // console.log("touchstart: this._touchState= TOUCH_FINDING_CORNER -> LONG_TOUCH_MOUSE_FINDING_CORNER") - }, this._mobileModeLongPressTimeMs); - this._touchState = QUICK_TOUCH_MOUSE_FINDING_CORNER; - // console.log("touchstart: this._touchState= TOUCH_FINDING_CORNER -> QUICK_TOUCH_MOUSE_FINDING_CORNER") - } - break; - - case TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - return; - } - if (currentNumTouches === 1) { // One finger down - longTouchTimeout = setTimeout(() => { - longTouchTimeout = null; - if (currentNumTouches !== 1 || - touchMoveCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchMoveCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchMoveCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchMoveCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - // Has moved - return; - } - // Long touch - disableCameraMouseControl(); - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = touchStartCanvasPos; - } - - const snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - this._currentAngleMeasurement.target.worldPos = snapPickResult.snappedWorldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.angleVisible = true; - } else { - const pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - this._currentAngleMeasurement.target.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.angleVisible = true; - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_TARGET; - // console.log("touchstart: this._touchState= TOUCH_FINDING_TARGET -> LONG_TOUCH_FINDING_TARGET") - }, this._mobileModeLongPressTimeMs); - this._touchState = QUICK_TOUCH_FINDING_TARGET; - // console.log("touchstart: this._touchState= TOUCH_FINDING_TARGET -> QUICK_TOUCH_FINDING_TARGET") - } - break; - - default: - if (longTouchTimeout !== null) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - enableCameraMouseControl(); - this._touchState = TOUCH_CANCELING; - // console.log("touchstart: this._touchState= default -> TOUCH_CANCELING") - - return; - } - - }, {passive: true}); - - canvas.addEventListener("touchmove", (event) => { - const currentNumTouches = event.touches.length; - if (currentNumTouches !== 1) { - return; - } - const touchX = event.touches[0].clientX; - const touchY = event.touches[0].clientY; - touchMoveCanvasPos.set([touchX, touchY]); - let snapPickResult; - let pickResult; - - switch (this._touchState) { - - case TOUCH_CANCELING: - break; - - case TOUCH_FINDING_ORIGIN: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_FINDING_ORIGIN") - break; - - case QUICK_TOUCH_FINDING_ORIGIN: - this._touchState = QUICK_TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_ORIGIN -> QUICK_TOUCH_FINDING_ORIGIN") - break; - - case LONG_TOUCH_FINDING_ORIGIN: - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - if (!this._currentAngleMeasurement) { - this._currentAngleMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: snapPickResult.snappedWorldPos - }, - corner: { - worldPos: snapPickResult.snappedWorldPos - }, - target: { - worldPos: snapPickResult.snappedWorldPos - } - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - this._currentAngleMeasurement.origin.worldPos = snapPickResult.snappedWorldPos; - } - this.fire("measurementStart", this._currentAngleMeasurement); - } else { - pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - if (!this._currentAngleMeasurement) { - this._currentAngleMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - corner: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - } else { - this._currentAngleMeasurement.origin.worldPos = pickResult.worldPos; - } - this.fire("measurementStart", this._currentAngleMeasurement); - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= LONG_TOUCH_FINDING_ORIGIN -> LONG_TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_CORNER: - this._touchState = TOUCH_FINDING_CORNER; - // console.log("touchmove: this._touchState= TOUCH_FINDING_CORNER -> TOUCH_FINDING_CORNER") - break; - - case QUICK_TOUCH_MOUSE_FINDING_CORNER: - this._touchState = QUICK_TOUCH_MOUSE_FINDING_CORNER; - // console.log("touchmove: this._touchState= QUICK_TOUCH_MOUSE_FINDING_CORNER -> QUICK_TOUCH_MOUSE_FINDING_CORNER") - break; - - case LONG_TOUCH_MOUSE_FINDING_CORNER: - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - this._currentAngleMeasurement.corner.worldPos = snapPickResult.snappedWorldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - } else { - pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - this._currentAngleMeasurement.corner.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_MOUSE_FINDING_CORNER; - // console.log("touchmove: this._touchState= LONG_TOUCH_MOUSE_FINDING_CORNER -> LONG_TOUCH_MOUSE_FINDING_CORNER") - break; - - case TOUCH_FINDING_TARGET: - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchmove: this._touchState= TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - break; - - case QUICK_TOUCH_FINDING_TARGET: - this._touchState = QUICK_TOUCH_FINDING_TARGET; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_TARGET -> QUICK_TOUCH_FINDING_TARGET") - break; - - case LONG_TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - if (pointerLens) { - pointerLens.visible = false; - } - enableCameraMouseControl(); - this._touchState = TOUCH_CANCELING; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_TARGET -> TOUCH_CANCELING") - return; - } - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - this._currentAngleMeasurement.target.worldPos = snapPickResult.snappedWorldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.angleVisible = true; - } else { - pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentAngleMeasurement.target.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.angleVisible = true; - } - } - this._touchState = LONG_TOUCH_FINDING_TARGET; - break; - - default: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= default -> TOUCH_FINDING_ORIGIN") - break; - } - }, {passive: true}); - - - canvas.addEventListener("touchend", this._onCanvasTouchEnd = (event) => { - - const currentNumTouches = event.changedTouches.length; - - if (currentNumTouches !== 1) { - return; - } - - enableCameraMouseControl(); - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - - const touchX = event.changedTouches[0].clientX; - const touchY = event.changedTouches[0].clientY; - - touchEndCanvasPos.set([touchX, touchY]); - - switch (this._touchState) { - - case TOUCH_CANCELING: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= TOUCH_CANCELING -> TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_ORIGIN: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_CORNER: - this._touchState = TOUCH_FINDING_CORNER; - // console.log("touchend: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_FINDING_CORNER") - break; - - case TOUCH_FINDING_TARGET: - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - break; - - case QUICK_TOUCH_FINDING_ORIGIN: - if (currentNumTouches !== 1 || - touchEndCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchEndCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchEndCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchEndCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (pointer moved, destroy measurement) -> TOUCH_FINDING_ORIGIN") - break; - } else { - const pickResult = scene.pick({ - canvasPos: touchEndCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentAngleMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - corner: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentAngleMeasurement.clickable = false; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false - this.fire("measurementStart", this._currentAngleMeasurement); - this._touchState = TOUCH_FINDING_CORNER; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (picked, begin measurement) -> TOUCH_FINDING_CORNER") - break; - } else { - if (this._currentAngleMeasurement) { // Not likely needed, but safe - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (nothing picked) -> TOUCH_FINDING_ORIGIN") - break; - } - } - - case LONG_TOUCH_FINDING_ORIGIN: - if (pointerLens) { - pointerLens.visible = false; - } - if (!this._currentAngleMeasurement) { - if (pointerLens) { - pointerLens.snapped = false; - pointerLens.visible = false; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_ORIGIN (no measurement) -> TOUCH_FINDING_ORIGIN") - break; - } else { - // this.fire("measurementStart", this._currentAngleMeasurement); - // enableCameraMouseControl(); - this._touchState = TOUCH_FINDING_CORNER; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_ORIGIN (picked, begin measurement) -> TOUCH_FINDING_CORNER") - break; - } - - case QUICK_TOUCH_MOUSE_FINDING_CORNER: - if (currentNumTouches !== 1 || - touchEndCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchEndCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchEndCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchEndCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - // if (this._currentAngleMeasurement) { - // this._currentAngleMeasurement.destroy(); - // this._currentAngleMeasurement = null; - // } - this._touchState = TOUCH_FINDING_CORNER; - // console.log("touchend: (moved) this._touchState= QUICK_TOUCH_MOUSE_FINDING_CORNER -> TOUCH_FINDING_CORNER") - break; - } else { - const pickResult = scene.pick({ - canvasPos: touchEndCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentAngleMeasurement.corner.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= QUICK_TOUCH_MOUSE_FINDING_CORNER (picked, begin measurement) -> TOUCH_FINDING_TARGET") - break; - } else { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (nothing picked, destroy measurement) -> TOUCH_FINDING_ORIGIN") - break; - } - } - - case LONG_TOUCH_MOUSE_FINDING_CORNER: { - if (pointerLens) { - pointerLens.visible = false; - } - if (!this._currentAngleMeasurement || !this._currentAngleMeasurement.cornerVisible) { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_ORIGIN (no measurement) -> TOUCH_FINDING_ORIGIN") - } else { - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= LONG_TOUCH_MOUSE_FINDING_CORNER -> TOUCH_FINDING_ORIGIN") - } - break; - } - - case QUICK_TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 || - touchEndCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchEndCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchEndCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchEndCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - // if (this._currentAngleMeasurement) { - // this._currentAngleMeasurement.destroy(); - // this._currentAngleMeasurement = null; - // } - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: (moved) this._touchState= QUICK_TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - break; - } else { - const pickResult = scene.pick({ - canvasPos: touchEndCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentAngleMeasurement.target.worldPos = pickResult.worldPos; - this._currentAngleMeasurement.originVisible = true; - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.cornerWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.angleVisible = true; - this.fire("measurementEnd", this._currentAngleMeasurement); - this._currentAngleMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_TARGET (picked, begin measurement) -> TOUCH_FINDING_ORIGIN") - break; - } else { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (nothing picked, destroy measurement) -> TOUCH_FINDING_ORIGIN") - break; - } - } - - case LONG_TOUCH_FINDING_TARGET: - if (pointerLens) { - pointerLens.visible = false; - } - if (!this._currentAngleMeasurement || !this._currentAngleMeasurement.targetVisible) { - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - this._currentAngleMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_TARGET (no target found) -> TOUCH_FINDING_ORIGIN") - } else { - this._currentAngleMeasurement.clickable = true; - this.fire("measurementEnd", this._currentAngleMeasurement); - this._currentAngleMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_TARGET -> TOUCH_FINDING_ORIGIN") - } - break; - - default: - if (pointerLens) { - pointerLens.visible = false; - pointerLens.snapped = false; - } - this._currentAngleMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= default -> TOUCH_FINDING_ORIGIN") - break; - } - - }, {passive: true}); - } - - this._active = true; } /** * Deactivates this AngleMeasurementsControl, making it unresponsive to input. * - * Destroys any {@link AngleMeasurement} under construction. + * Destroys any {@link AngleMeasurement} under construction by this AngleMeasurementsControl. + * + * @abstract */ deactivate() { - - if (!this._active) { - return; - } - - if (this.plugin.pointerLens) { - this.plugin.pointerLens.visible = false; - } - - this.reset(); - - const input = this.plugin.viewer.scene.input; - const cameraControl = this.plugin.viewer.cameraControl; - const canvas = this.plugin.viewer.scene.canvas.canvas; - - input.off(this._onInputMouseDown); - input.off(this._onInputMouseUp); - - cameraControl.off(this._onMouseHoverSurface); - cameraControl.off(this._onPickedSurface); - cameraControl.off(this._onHoverNothing); - cameraControl.off(this._onPickedNothing); - - canvas.removeEventListener("touchstart", this._onCanvasTouchStart); - canvas.removeEventListener("touchend", this._onCanvasTouchEnd); - - this._currentAngleMeasurement = null; - - this._active = false; } /** * Resets this AngleMeasurementsControl. * - * Destroys any {@link AngleMeasurement} under construction. + * Destroys any {@link AngleMeasurement} under construction by this AngleMeasurementsControl. * * Does nothing if the AngleMeasurementsControl is not active. + * + * @abstract */ reset() { - - if (!this._active) { - return; - } - - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.destroy(); - this._currentAngleMeasurement = null; - } - - this._mouseState = MOUSE_FINDING_ORIGIN; - this._touchState = TOUCH_FINDING_ORIGIN; } /** - * @private + * Destroys this AngleMeasurementsMouseControl. + * + * Destroys any {@link AngleMeasurement} under construction by this AngleMeasurementsControl. + * + * @abstract */ destroy() { - this.deactivate(); - super.destroy(); } - -} - -const getCanvasPosFromEvent = function (event, canvasPos) { - if (!event) { - event = window.event; - canvasPos[0] = event.x; - canvasPos[1] = event.y; - } else { - let element = event.target; - let totalOffsetLeft = 0; - let totalOffsetTop = 0; - while (element.offsetParent) { - totalOffsetLeft += element.offsetLeft; - totalOffsetTop += element.offsetTop; - element = element.offsetParent; - } - canvasPos[0] = event.pageX - totalOffsetLeft; - canvasPos[1] = event.pageY - totalOffsetTop; - } - return canvasPos; -}; - -export {AngleMeasurementsControl}; +} \ No newline at end of file diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js new file mode 100644 index 0000000000..f4c2597a70 --- /dev/null +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js @@ -0,0 +1,393 @@ +import {math} from "../../viewer/scene/math/math.js"; +import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; + +const MOUSE_FINDING_ORIGIN = 0; +const MOUSE_FINDING_CORNER = 1; +const MOUSE_FINDING_TARGET = 2; + +/** + * Creates {@link AngleMeasurement}s in an {@link AngleMeasurementsPlugin} from mouse input. + * + * ## Usage + * + * [[Run example](/examples/measurement/#angle_createWithMouse_snapping)] + * + * ````javascript + * import {Viewer, XKTLoaderPlugin, AngleMeasurementsPlugin, AngleMeasurementsMouseControl, PointerLens} from "xeokit-sdk.es.js"; + * + * const viewer = new Viewer({ + * canvasId: "myCanvas", + * }); + * + * viewer.camera.eye = [-3.93, 2.85, 27.01]; + * viewer.camera.look = [4.40, 3.72, 8.89]; + * viewer.camera.up = [-0.01, 0.99, 0.039]; + * + * const xktLoader = new XKTLoaderPlugin(viewer); + * + * const sceneModel = xktLoader.load({ + * id: "myModel", + * src: "Duplex.xkt" + * }); + * + * const angleMeasurements = new AngleMeasurementsPlugin(viewer); + * + * const angleMeasurementsMouseControl = new AngleMeasurementsMouseControl(angleMeasurements, { + * pointerLens : new PointerLens(viewer) + * }) + * + * angleMeasurementsMouseControl.snapToVertex = true; + * angleMeasurementsMouseControl.snapToEdge = true; + * + * angleMeasurementsMouseControl.activate(); + * ```` + */ +export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { + + /** + * Creates a AngleMeasurementsMouseControl bound to the given AngleMeasurementsPlugin. + */ + constructor(angleMeasurementsPlugin, cfg = {}) { + + super(angleMeasurementsPlugin.viewer.scene); + + this.pointerLens = cfg.pointerLens; + + this._active = false; + this._mouseState = MOUSE_FINDING_ORIGIN; + + this._currentAngleMeasurement = null; + + const markerDiv = document.createElement('div'); + const canvas = this.scene.canvas.canvas; + canvas.parentNode.insertBefore(markerDiv, canvas); + + markerDiv.style.background = "black"; + markerDiv.style.border = "2px solid blue"; + markerDiv.style.borderRadius = "10px"; + markerDiv.style.width = "5px"; + markerDiv.style.height = "5px"; + markerDiv.style.margin = "-200px -200px"; + markerDiv.style.zIndex = "100"; + markerDiv.style.position = "absolute"; + markerDiv.style.pointerEvents = "none"; + + this.markerDiv = markerDiv; + this._onMouseHoverSurface = null; + this._onHoverNothing = null; + this._onPickedNothing = null; + this._onPickedSurface = null; + + this._onInputMouseDown = null; + this._onInputMouseUp = null; + + this._snapToEdge = cfg.snapToEdge !== false; + this._snapToVertex = cfg.snapToVertex !== false; + + this._attachPlugin(angleMeasurementsPlugin, cfg); + } + + _attachPlugin(angleMeasurementsPlugin, cfg = {}) { + + /** + * The {@link AngleMeasurementsPlugin} that owns this AngleMeasurementsMouseControl. + * + * @type {AngleMeasurementsPlugin} + */ + this.angleMeasurementsPlugin = angleMeasurementsPlugin; + + /** + * The {@link AngleMeasurementsPlugin} that owns this AngleMeasurementsMouseControl. + * + * @type {AngleMeasurementsPlugin} + */ + this.plugin = angleMeasurementsPlugin; + } + + /** + * Gets if this AngleMeasurementsMouseControl is currently active, where it is responding to input. + * + * @returns {boolean} + */ + get active() { + return this._active; + } + + /** + * Sets whether snap-to-vertex is enabled for this AngleMeasurementsMouseControl. + * + * This is `true` by default. + * + * @param snapToVertex True if this nap-to-vertex is to be enabled for this AngleMeasurementsMouseControl. + */ + set snapToVertex(snapToVertex) { + this._snapToVertex = snapToVertex; + } + + /** + * Gets whether snap-to-vertex is enabled for this AngleMeasurementsMouseControl. + * + * This is `true` by default. + * + * @returns {boolean} True if snap-to-vertex is enabled for this AngleMeasurementsMouseControl. + */ + get snapToVertex() { + return this._snapToVertex; + } + + /** + * Sets whether snap-to-edge is enabled for this AngleMeasurementsMouseControl. + * + * This is `true` by default. + * + * @param {boolean} snapToEdge True if this snap-to-edge is to be enabled for this AngleMeasurementsMouseControl. + */ + set snapToEdge(snapToEdge) { + this._snapToEdge = snapToEdge; + } + + /** + * Gets whether snap-to-edge is enabled for this AngleMeasurementsMouseControl. + * + * This is `true` by default. + * + * @returns {boolean} True if this snap-to-edge is enabled for this AngleMeasurementsMouseControl. + */ + get snapToEdge() { + return this._snapToEdge; + } + + /** + * Activates this AngleMeasurementsMouseControl, ready to respond to input. + */ + activate() { + if (this._active) { + return; + } + const angleMeasurementsPlugin = this.angleMeasurementsPlugin; + const scene = this.scene; + const input = scene.input; + const canvas = scene.canvas.canvas; + const clickTolerance = 20; + const cameraControl = this.angleMeasurementsPlugin.viewer.cameraControl; + const pointerLens = this.pointerLens; + let mouseHovering = false; + let mouseHoverEntity = false; + let lastMouseCanvasX = 0; + let lastMouseCanvasY = 0; + const mouseWorldPos = math.vec3(); + const mouseHoverCanvasPos = math.vec2(); + this._currentAngleMeasurement = null; + this._onMouseHoverSurface = cameraControl.on("hoverSnapOrSurface", event => { + if (event.snappedToVertex || event.snappedToEdge) { + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos || event.canvasPos; + pointerLens.cursorPos = event.canvasPos; + pointerLens.snapped = true; + } + this.markerDiv.style.background = "greenyellow"; + this.markerDiv.style.border = "2px solid green"; + } else { + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos || event.canvasPos; + pointerLens.cursorPos = event.canvasPos; + pointerLens.snapped = false; + } + this.markerDiv.style.background = "pink"; + this.markerDiv.style.border = "2px solid red"; + } + mouseHovering = true; + mouseHoverEntity = event.entity; + mouseWorldPos.set(event.worldPos); + mouseHoverCanvasPos.set(event.canvasPos); + switch (this._mouseState) { + case MOUSE_FINDING_ORIGIN: + this.markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; + this.markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; + break; + case MOUSE_FINDING_CORNER: + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.originWireVisible = true; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.cornerVisible = true; + this._currentAngleMeasurement.angleVisible = false; + this._currentAngleMeasurement.corner.worldPos = event.worldPos; + } + this.markerDiv.style.marginLeft = `-10000px`; + this.markerDiv.style.marginTop = `-10000px`; + canvas.style.cursor = "pointer"; + break; + case MOUSE_FINDING_TARGET: + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.targetWireVisible = true; + this._currentAngleMeasurement.targetVisible = true; + this._currentAngleMeasurement.angleVisible = true; + this._currentAngleMeasurement.target.worldPos = event.worldPos; + } + this.markerDiv.style.marginLeft = `-10000px`; + this.markerDiv.style.marginTop = `-10000px`; + canvas.style.cursor = "pointer"; + break; + } + }); + this._onInputMouseDown = input.on("mousedown", (coords) => { + lastMouseCanvasX = coords[0]; + lastMouseCanvasY = coords[1]; + }); + this._onInputMouseUp = input.on("mouseup", (coords) => { + if (coords[0] > lastMouseCanvasX + clickTolerance || + coords[0] < lastMouseCanvasX - clickTolerance || + coords[1] > lastMouseCanvasY + clickTolerance || + coords[1] < lastMouseCanvasY - clickTolerance) { + return; + } + switch (this._mouseState) { + case MOUSE_FINDING_ORIGIN: + if (mouseHovering) { + this._currentAngleMeasurement = this.angleMeasurementsPlugin.createMeasurement({ + id: math.createUUID(), + origin: { + entity: mouseHoverEntity, + worldPos: mouseWorldPos + }, + corner: { + entity: mouseHoverEntity, + worldPos: mouseWorldPos + }, + target: { + entity: mouseHoverEntity, + worldPos: mouseWorldPos + }, + approximate: true + }); + this._currentAngleMeasurement.clickable = false; + this._currentAngleMeasurement.originVisible = true; + this._currentAngleMeasurement.originWireVisible = true; + this._currentAngleMeasurement.cornerVisible = false; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.targetVisible = false; + this._currentAngleMeasurement.angleVisible = false; + this._mouseState = MOUSE_FINDING_CORNER; + this.angleMeasurementsPlugin.fire("measurementStart", this._currentAngleMeasurement); + } + break; + case MOUSE_FINDING_CORNER: + if (mouseHovering) { + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.targetVisible = true; + this._currentAngleMeasurement.angleVisible = true; + this._mouseState = MOUSE_FINDING_TARGET; + } else { + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.destroy(); + this._currentAngleMeasurement = null; + this._mouseState = MOUSE_FINDING_ORIGIN + this.angleMeasurementsPlugin.fire("measurementCancel", this._currentAngleMeasurement); + } + } + break; + case MOUSE_FINDING_TARGET: + if (mouseHovering) { + this._currentAngleMeasurement.targetVisible = true; + this._currentAngleMeasurement.angleVisible = true; + this.angleMeasurementsPlugin.fire("measurementEnd", this._currentAngleMeasurement); + this._currentAngleMeasurement = null; + this._mouseState = MOUSE_FINDING_ORIGIN; + } else { + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.destroy(); + this._currentAngleMeasurement = null; + this._mouseState = MOUSE_FINDING_ORIGIN; + this.angleMeasurementsPlugin.fire("measurementCancel", this._currentAngleMeasurement); + } + } + break; + } + }); + this._onMouseHoverOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { + mouseHovering = false; + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos; + pointerLens.cursorPos = event.cursorPos; + pointerLens.snapped = false; + } + this.markerDiv.style.marginLeft = `-100px`; + this.markerDiv.style.marginTop = `-100px`; + if (this._currentAngleMeasurement) { + switch (this._mouseState) { + case MOUSE_FINDING_ORIGIN: + this._currentAngleMeasurement.originVisible = false; + break; + case MOUSE_FINDING_CORNER: + this._currentAngleMeasurement.cornerVisible = false; + this._currentAngleMeasurement.originWireVisible = false; + this._currentAngleMeasurement.targetVisible = false; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.angleVisible = false; + break; + case MOUSE_FINDING_TARGET: + this._currentAngleMeasurement.targetVisible = false; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.angleVisible = false; + break; + } + canvas.style.cursor = "default"; + } + }); + this._active = true; + } + + /** + * Deactivates this AngleMeasurementsMouseControl, making it unresponsive to input. + * + * Destroys any {@link AngleMeasurement} under construction by this AngleMeasurementsMouseControl. + */ + deactivate() { + if (!this._active) { + return; + } + if (this.pointerLens) { + this.pointerLens.visible = false; + } + this.reset(); + const input = this.angleMeasurementsPlugin.viewer.scene.input; + const cameraControl = this.angleMeasurementsPlugin.viewer.cameraControl; + input.off(this._onInputMouseDown); + input.off(this._onInputMouseUp); + cameraControl.off(this._onMouseHoverSurface); + cameraControl.off(this._onPickedSurface); + cameraControl.off(this._onHoverNothing); + cameraControl.off(this._onPickedNothing); + this._currentAngleMeasurement = null; + this._active = false; + } + + /** + * Resets this AngleMeasurementsMouseControl. + * + * Destroys any {@link AngleMeasurement} under construction by this AngleMeasurementsMouseControl. + * + * Does nothing if the AngleMeasurementsMouseControl is not active. + */ + reset() { + if (!this._active) { + return; + } + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.destroy(); + this._currentAngleMeasurement = null; + } + this._mouseState = MOUSE_FINDING_ORIGIN; + } + + /** + * Destroys this DistanceMeasurementsMouseControl. + */ + destroy() { + this.deactivate(); + super.destroy(); + } +} diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsPlugin.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsPlugin.js index 56b95ddc69..e358df840b 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsPlugin.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsPlugin.js @@ -1,6 +1,6 @@ import {Plugin} from "../../viewer/Plugin.js"; import {AngleMeasurement} from "./AngleMeasurement.js"; -import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; +import {AngleMeasurementsMouseControl} from "./AngleMeasurementsMouseControl"; /** * {@link Viewer} plugin for measuring angles. @@ -16,7 +16,7 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * as three positions on the surface(s) of one or more {@link Entity}s. * * As shown on the screen capture above, a AngleMeasurement has two wires that show the line segments, with a label that shows the angle between them. * * Create AngleMeasurements programmatically with {@link AngleMeasurementsPlugin#createMeasurement}. - * * Create AngleMeasurements interactively using the {@link AngleMeasurementsControl}, located at {@link AngleMeasurementsPlugin#control}. + * * Create AngleMeasurements interactively using a {@link AngleMeasurementsControl}. * * Existing AngleMeasurements are registered by ID in {@link AngleMeasurementsPlugin#measurements}. * * Destroy AngleMeasurements using {@link AngleMeasurementsPlugin#destroyMeasurement}. * * Configure global measurement units and scale via {@link Metrics}, located at {@link Scene#metrics} @@ -88,9 +88,9 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * }); * ```` * - * ## Example 2: Creating AngleMeasurements Interactively + * ## Example 2: Creating AngleMeasurements with Mouse Input * - * In our second example, we'll use an {@link XKTLoaderPlugin} to load a model, then we'll use the AngleMeasurementsPlugin's {@link AngleMeasurementsControl} to interactively create {@link AngleMeasurement}s with mouse or touch input. + * In our second example, we'll use an {@link XKTLoaderPlugin} to load a model, then we'll use the AngleMeasurementsPlugin's {@link AngleMeasurementsTouchControl} to interactively create {@link AngleMeasurement}s with mouse or touch input. * * After we've activated the AngleMeasurementsControl, the first click on any {@link Entity} begins constructing a AngleMeasurement, fixing its * origin to that Entity. The next click on any Entity will fix the AngleMeasurement's corner, and the next click after @@ -102,7 +102,7 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/#measurements_angle_createWithMouse)] * * ````JavaScript - * import {Viewer, XKTLoaderPlugin, AngleMeasurementsPlugin} from "xeokit-sdk.es.js"; + * import {Viewer, XKTLoaderPlugin, AngleMeasurementsPlugin, AngleMeasurementsMouseControl, PointerLens} from "xeokit-sdk.es.js"; * * const viewer = new Viewer({ * canvasId: "myCanvas", @@ -115,13 +115,14 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * * const xktLoader = new XKTLoaderPlugin(viewer); * - * const angleMeasurements = new AngleMeasurementsPlugin(viewer); + * cconst angleMeasurementsMouseControl = new AngleMeasurementsMouseControl(angleMeasurements, { + * pointerLens : new PointerLens(viewer) + * }) * - * const model = xktLoader.load({ - * src: "./models/xkt/duplex/duplex.xkt" - * }); + * angleMeasurementsMouseControl.snapToVertex = true; + * angleMeasurementsMouseControl.snapToEdge = true; * - * angleMeasurements.control.activate(); // <------------ Activate the AngleMeasurementsControl + * angleMeasurementsMouseControl.activate(); * ```` * * ## Example 4: Attaching Mouse Handlers @@ -129,7 +130,7 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * In our fourth example, we'll attach even handlers to our plugin, to catch when the user * hovers or right-clicks over our measurements. * - * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/#measurements_angle_modelWithMeasurements)] + * [[Run example](/examples/measurement/#angle_modelWithMeasurements)] * * ````javascript * import {Viewer, XKTLoaderPlugin, AngleMeasurementsPlugin} from "xeokit-sdk.es.js"; @@ -203,7 +204,7 @@ import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; * }); * ```` */ -class AngleMeasurementsPlugin extends Plugin { +export class AngleMeasurementsPlugin extends Plugin { /** * @constructor @@ -214,17 +215,15 @@ class AngleMeasurementsPlugin extends Plugin { * @param {string} [cfg.defaultColor=null] The default color of the dots, wire and label. * @param {boolean} [cfg.defaultLabelsVisible=true] The default value of {@link AngleMeasurement.labelsVisible}. * @param {number} [cfg.zIndex] If set, the wires, dots and labels will have this zIndex (+1 for dots and +2 for labels). - * @param {PointerLens} [cfg.pointerLens] A PointerLens to help the user position the pointer. This can be shared with other plugins. + * @param {PointerCircle} [cfg.pointerLens] A PointerLens to help the user position the pointer. This can be shared with other plugins. */ constructor(viewer, cfg = {}) { super("AngleMeasurements", viewer); - this._pointerLens = cfg.pointerLens; - this._container = cfg.container || document.body; - this._control = new AngleMeasurementsControl(this); + this._defaultControl = null; this._measurements = {}; @@ -277,20 +276,16 @@ class AngleMeasurementsPlugin extends Plugin { } /** - * Gets the PointerLens attached to this AngleMeasurementsPlugin. - * @returns {PointerLens} - */ - get pointerLens() { - return this._pointerLens; - } - - /** - * Gets the {@link AngleMeasurementsControl}, which creates {@link AngleMeasurement}s from user input. + * Gets the default {@link AngleMeasurementsMouseControl}. * - * @type {AngleMeasurementsControl} + * @type {AngleMeasurementsMouseControl} + * @deprecated */ get control() { - return this._control; + if (!this._defaultControl) { + this._defaultControl = new AngleMeasurementsMouseControl(this, {}); + } + return this._defaultControl; } /** @@ -408,4 +403,3 @@ class AngleMeasurementsPlugin extends Plugin { } } -export {AngleMeasurementsPlugin} diff --git a/src/plugins/AngleMeasurementsPlugin/index.js b/src/plugins/AngleMeasurementsPlugin/index.js index d8c52730d4..a9e68b4942 100644 --- a/src/plugins/AngleMeasurementsPlugin/index.js +++ b/src/plugins/AngleMeasurementsPlugin/index.js @@ -1 +1,3 @@ -export * from "./AngleMeasurementsPlugin.js"; \ No newline at end of file +export * from "./AngleMeasurementsPlugin.js"; +export * from "./AngleMeasurementsControl.js"; +export * from "./AngleMeasurementsMouseControl.js"; \ No newline at end of file diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js index 3b6962f654..92047abced 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js @@ -6,7 +6,7 @@ import {math} from "../../viewer/scene/math/math.js"; import {Component} from "../../viewer/scene/Component.js"; -var distVec3 = math.vec3(); +const distVec3 = math.vec3(); const lengthWire = (x1, y1, x2, y2) => { var a = x1 - x2; diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js index 4e9f0efc81..d94b462bd4 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js @@ -1,995 +1,103 @@ -import {Component} from "../../viewer/scene/Component.js"; -import {math} from "../../viewer/scene/math/math.js"; - - -const TOUCH_FINDING_ORIGIN = 0; -const QUICK_TOUCH_FINDING_ORIGIN = 1; -const LONG_TOUCH_FINDING_ORIGIN = 2; -const TOUCH_FINDING_TARGET = 3; -const QUICK_TOUCH_FINDING_TARGET = 4; -const LONG_TOUCH_FINDING_TARGET = 5; -const TOUCH_CANCELING = 6; - - -const MOUSE_FIRST_CLICK_EXPECTED = 0; -const MOUSE_SECOND_CLICK_EXPECTED = 1; +import {Component} from "../../viewer"; /** - * Creates {@link DistanceMeasurement}s from mouse and touch input. + * Creates {@link DistanceMeasurement}s in a {@link DistanceMeasurementsPlugin} from user input. * - * Belongs to a {@link DistanceMeasurementsPlugin}. Located at {@link DistanceMeasurementsPlugin#control}. - * - * Once the DistanceMeasurementControl is activated, the first click on any {@link Entity} begins constructing a {@link DistanceMeasurement}, fixing its origin to that Entity. The next click on any Entity will complete the DistanceMeasurement, fixing its target to that second Entity. The DistanceMeasurementControl will then wait for the next click on any Entity, to begin constructing another DistanceMeasurement, and so on, until deactivated. - * - * See {@link DistanceMeasurementsPlugin} for more info. + * @interface + * @abstract */ -class DistanceMeasurementsControl extends Component { +export class DistanceMeasurementsControl extends Component { /** - * @private - */ - constructor(plugin, cfg = {}) { - - super(plugin.viewer.scene); - - /** - * The {@link DistanceMeasurementsPlugin} that owns this DistanceMeasurementsControl. - * @type {DistanceMeasurementsPlugin} - */ - this.plugin = plugin; - - this._active = false; - - const markerDiv = document.createElement('div'); - const canvas = this.scene.canvas.canvas; - canvas.parentNode.insertBefore(markerDiv, canvas); - - markerDiv.style.background = "black"; - markerDiv.style.border = "2px solid blue"; - markerDiv.style.borderRadius = "10px"; - markerDiv.style.width = "5px"; - markerDiv.style.height = "5px"; - markerDiv.style.margin = "-200px -200px"; - markerDiv.style.zIndex = "100"; - markerDiv.style.position = "absolute"; - markerDiv.style.pointerEvents = "none"; - - this.markerDiv = markerDiv; - - this._currentDistanceMeasurement = null; - - this._currentDistanceMeasurementInitState = { - wireVisible: null, - axisVisible: null, - xAxisVisible: null, - yaxisVisible: null, - zAxisVisible: null, - targetVisible: null, - } - - this._onCameraControlHoverSnapOrSurface = null; - this._onCameraControlHoverSnapOrSurfaceOff = null; - this._onInputMouseDown = null; - this._onInputMouseUp = null; - - // Event handles from Canvas element - this._onCanvasTouchStart = null; - this._onCanvasTouchEnd = null; - - this._mobileModeLongPressTimeMs = 500; - - this._snapEdge = cfg.snapEdge !== false; - this._snapVertex = cfg.snapVertex !== false; - - this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; - this._touchState = TOUCH_FINDING_ORIGIN; - } - - /** Gets if this DistanceMeasurementsControl is currently active, where it is responding to input. + * Gets if this DistanceMeasurementsControl is currently active, where it is responding to input. * - * @returns {Boolean} + * @returns {boolean} True if this DistanceMeasurementsControl is active. + * @abstract */ get active() { - return this._active; } /** - * Sets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * Gets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * * This is `true` by default. - * @param snapVertex + * + * @returns {boolean} Whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * @abstract */ - set snapVertex(snapVertex) { - this._snapVertex = snapVertex; + get snapToVertex() { } /** - * Gets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * Sets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * * This is `true` by default. - * @returns {*} + * + * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this DistanceMeasurementsControl. + * @abstract */ - get snapVertex() { - return this._snapVertex; + set snapToVertex(snapToVertex) { } /** - * Sets whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * Gets whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * * This is `true` by default. - * @param snapEdge + * + * @returns {boolean} Whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * @abstract */ - set snapEdge(snapEdge) { - this._snapEdge = snapEdge; + get snapToEdge() { } /** - * Gets whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * Sets whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * * This is `true` by default. - * @returns {*} + * + * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this DistanceMeasurementsControl. + * @abstract */ - get snapEdge() { - return this._snapEdge; + set snapToEdge(snapToEdge) { } /** * Activates this DistanceMeasurementsControl, ready to respond to input. + * + * @abstract */ activate() { - - if (this._active) { - return; - } - - const plugin = this.plugin; - const scene = this.scene; - const cameraControl = plugin.viewer.cameraControl; - const canvas = scene.canvas.canvas; - const input = scene.input; - - const pointerLens = plugin.pointerLens; - - let mouseHovering = false; - const pointerWorldPos = math.vec3(); - const pointerCanvasPos = math.vec2(); - - let pointerDownCanvasX; - let pointerDownCanvasY; - - const clickTolerance = 20; - - - this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; - - - //---------------------------------------------------------------------------------------------------- - // Mouse input - //---------------------------------------------------------------------------------------------------- - - { - - this._onCameraControlHoverSnapOrSurface = cameraControl.on("hoverSnapOrSurface", event => { - mouseHovering = true; - pointerWorldPos.set(event.worldPos); - pointerCanvasPos.set(event.canvasPos); - if (this._mouseState === MOUSE_FIRST_CLICK_EXPECTED) { - this.markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; - this.markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; - this.markerDiv.style.background = "pink"; - if (event.snappedToVertex || event.snappedToEdge) { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = true; - } - this.markerDiv.style.background = "greenyellow"; - this.markerDiv.style.border = "2px solid green"; - } else { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = false; - } - this.markerDiv.style.background = "pink"; - this.markerDiv.style.border = "2px solid red"; - } - } else { - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - } - canvas.style.cursor = "pointer"; - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.wireVisible = this._currentDistanceMeasurementInitState.wireVisible; - this._currentDistanceMeasurement.axisVisible = this._currentDistanceMeasurementInitState.axisVisible && this.plugin.defaultAxisVisible; - this._currentDistanceMeasurement.xAxisVisible = this._currentDistanceMeasurementInitState.xAxisVisible && this.plugin.defaultXAxisVisible; - this._currentDistanceMeasurement.yAxisVisible = this._currentDistanceMeasurementInitState.yAxisVisible && this.plugin.defaultYAxisVisible; - this._currentDistanceMeasurement.zAxisVisible = this._currentDistanceMeasurementInitState.zAxisVisible && this.plugin.defaultZAxisVisible; - this._currentDistanceMeasurement.targetVisible = this._currentDistanceMeasurementInitState.targetVisible; - this._currentDistanceMeasurement.target.worldPos = pointerWorldPos.slice(); - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - } - }); - - this._onInputMouseDown = input.on("mousedown", (coords) => { - pointerDownCanvasX = coords[0]; - pointerDownCanvasY = coords[1]; - }); - - this._onInputMouseUp = input.on("mouseup", (coords) => { - if (coords[0] > pointerDownCanvasX + clickTolerance || - coords[0] < pointerDownCanvasX - clickTolerance || - coords[1] > pointerDownCanvasY + clickTolerance || - coords[1] < pointerDownCanvasY - clickTolerance) { - return; - } - if (this._currentDistanceMeasurement) { - if (mouseHovering) { - this._currentDistanceMeasurement.clickable = true; - this.fire("measurementEnd", this._currentDistanceMeasurement); - this._currentDistanceMeasurement = null; - } else { - this._currentDistanceMeasurement.destroy(); - this.fire("measurementCancel", this._currentDistanceMeasurement); - this._currentDistanceMeasurement = null; - } - } else { - if (mouseHovering) { - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pointerWorldPos.slice() - }, - target: { - worldPos: pointerWorldPos.slice() - }, - approximate: true - }); - this._currentDistanceMeasurementInitState.axisVisible = this._currentDistanceMeasurement.axisVisible && this.plugin.defaultAxisVisible; - this._currentDistanceMeasurementInitState.xAxisVisible = this._currentDistanceMeasurement.xAxisVisible && this.plugin.defaultXAxisVisible; - this._currentDistanceMeasurementInitState.yAxisVisible = this._currentDistanceMeasurement.yAxisVisible && this.plugin.defaultYAxisVisible; - this._currentDistanceMeasurementInitState.zAxisVisible = this._currentDistanceMeasurement.zAxisVisible && this.plugin.defaultZAxisVisible; - this._currentDistanceMeasurementInitState.wireVisible = this._currentDistanceMeasurement.wireVisible; - this._currentDistanceMeasurementInitState.targetVisible = this._currentDistanceMeasurement.targetVisible; - this._currentDistanceMeasurement.clickable = false; - this.fire("measurementStart", this._currentDistanceMeasurement); - } - } - }); - - this._onCameraControlHoverSnapOrSurfaceOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - } - mouseHovering = false; - this.markerDiv.style.marginLeft = `-100px`; - this.markerDiv.style.marginTop = `-100px`; - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.wireVisible = false; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.axisVisible = false; - } - canvas.style.cursor = "default"; - }); - } - - //---------------------------------------------------------------------------------------------------- - // Touch input always assumes mobile devices - //---------------------------------------------------------------------------------------------------- - - { - let longTouchTimeout = null; - - const disableCameraMouseControl = () => { - this.plugin.viewer.cameraControl.active = false; - } - - const enableCameraMouseControl = () => { - this.plugin.viewer.cameraControl.active = true; - } - - const scheduleSurfacePickIfNeeded = () => { - if (!this.plugin.viewer.cameraControl._handlers[2]._active) { - this.plugin.viewer.cameraControl._controllers.pickController.schedulePickSurface = true; - this.plugin.viewer.cameraControl._controllers.pickController.update(); - } - } - - this._touchState = TOUCH_FINDING_ORIGIN; - - const touchStartCanvasPos = math.vec2(); - const touchMoveCanvasPos = math.vec2(); - const touchEndCanvasPos = math.vec2(); - - canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { - - const currentNumTouches = event.touches.length; - - if (currentNumTouches !== 1) { - return; - } - - const touchX = event.touches[0].clientX; - const touchY = event.touches[0].clientY; - - touchStartCanvasPos.set([touchX, touchY]); - touchMoveCanvasPos.set([touchX, touchY]); - - switch (this._touchState) { - - case TOUCH_FINDING_ORIGIN: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - this._touchState = TOUCH_CANCELING; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_CANCELING") - return; - } - longTouchTimeout = setTimeout(() => { - - if (currentNumTouches !== 1 || - touchMoveCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchMoveCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchMoveCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchMoveCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - return; // Has moved - } - // Long touch - disableCameraMouseControl(); - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = touchStartCanvasPos; - pointerLens.cursorPos = touchStartCanvasPos; - } - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - pointerLens.snapped = false; - } - const snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - if (!this._currentDistanceMeasurement) { - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: snapPickResult.snappedWorldPos - }, - target: { - worldPos: snapPickResult.snappedWorldPos - } - }); - this._currentDistanceMeasurement.labelsVisible = false; - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.wireVisible = false; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.clickable = false; - } else { - this._currentDistanceMeasurement.origin.worldPos = snapPickResult.snappedWorldPos; - } - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - const pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - if (!this._currentDistanceMeasurement) { - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentDistanceMeasurement.labelsVisible = false; - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.wireVisible = false; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.clickable = false; - } else { - this._currentDistanceMeasurement.origin.worldPos = pickResult.worldPos; - } - - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_ORIGIN; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> LONG_TOUCH_FINDING_ORIGIN") - }, this._mobileModeLongPressTimeMs); - this._touchState = QUICK_TOUCH_FINDING_ORIGIN; - // console.log("touchstart: this._touchState= TOUCH_FINDING_ORIGIN -> QUICK_TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - return; - } - if (currentNumTouches === 1) { // One finger down - longTouchTimeout = setTimeout(() => { - longTouchTimeout = null; - if (currentNumTouches !== 1 || - touchMoveCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchMoveCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchMoveCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchMoveCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - // Has moved - return; - } - // Long touch - disableCameraMouseControl(); - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = touchStartCanvasPos; - pointerLens.snapped = false; - } - - const snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - this._currentDistanceMeasurement.target.worldPos = snapPickResult.snappedWorldPos; - this._currentDistanceMeasurement.targetVisible = true; - this._currentDistanceMeasurement.wireVisible = true; - this._currentDistanceMeasurement.labelsVisible = true; - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - const pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - this._currentDistanceMeasurement.target.worldPos = pickResult.worldPos; - this._currentDistanceMeasurement.targetVisible = true; - this._currentDistanceMeasurement.wireVisible = true; - this._currentDistanceMeasurement.labelsVisible = true; - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_TARGET; - // console.log("touchstart: this._touchState= TOUCH_FINDING_TARGET -> LONG_TOUCH_FINDING_TARGET") - }, this._mobileModeLongPressTimeMs); - this._touchState = QUICK_TOUCH_FINDING_TARGET; - // console.log("touchstart: this._touchState= TOUCH_FINDING_TARGET -> QUICK_TOUCH_FINDING_TARGET") - } - break; - - default: - if (longTouchTimeout !== null) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - enableCameraMouseControl(); - this._touchState = TOUCH_CANCELING; - // console.log("touchstart: this._touchState= default -> TOUCH_CANCELING") - return; - } - - }, {passive: true}); - - - canvas.addEventListener("touchmove", (event) => { - - const currentNumTouches = event.touches.length; - - if (currentNumTouches !== 1) { - return; - } - - if (longTouchTimeout) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - - const touchX = event.touches[0].clientX; - const touchY = event.touches[0].clientY; - - touchMoveCanvasPos.set([touchX, touchY]); - - let snapPickResult; - let pickResult; - - switch (this._touchState) { - - case TOUCH_CANCELING: - if (longTouchTimeout) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - break; - - case TOUCH_FINDING_ORIGIN: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_FINDING_ORIGIN") - break; - - case QUICK_TOUCH_FINDING_ORIGIN: - this._touchState = QUICK_TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_ORIGIN -> QUICK_TOUCH_FINDING_ORIGIN") - break; - - case LONG_TOUCH_FINDING_ORIGIN: - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - pointerWorldPos.set(snapPickResult.snappedWorldPos); - if (!this._currentDistanceMeasurement) { - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: snapPickResult.snappedWorldPos - }, - target: { - worldPos: snapPickResult.snappedWorldPos - } - }); - this._currentDistanceMeasurement.labelsVisible = false; - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.wireVisible = false; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.clickable = false; - } else { - this._currentDistanceMeasurement.origin.worldPos = snapPickResult.snappedWorldPos; - } - - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - pointerWorldPos.set(pickResult.worldPos); - if (!this._currentDistanceMeasurement) { - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentDistanceMeasurement.labelsVisible = false; - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.wireVisible = false; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.clickable = false; - } else { - this._currentDistanceMeasurement.origin.worldPos = pickResult.worldPos; - } - - this.fire("measurementStart", this._currentDistanceMeasurement); - } else { - if (pointerLens) { - pointerLens.cursorPos = null; - pointerLens.snapped = false; - } - } - } - this._touchState = LONG_TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= LONG_TOUCH_FINDING_ORIGIN -> LONG_TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_TARGET: - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchmove: this._touchState= TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - break; - - case QUICK_TOUCH_FINDING_TARGET: - this._touchState = QUICK_TOUCH_FINDING_TARGET; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_TARGET -> QUICK_TOUCH_FINDING_TARGET") - break; - - case LONG_TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 && longTouchTimeout !== null) { // Two or more fingers down - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - if (pointerLens) { - pointerLens.visible = false; - } - enableCameraMouseControl(); - this._touchState = TOUCH_CANCELING; - // console.log("touchmove: this._touchState= QUICK_TOUCH_FINDING_TARGET -> TOUCH_CANCELING") - return; - } - if (pointerLens) { - pointerLens.centerPos = touchMoveCanvasPos; - } - snapPickResult = scene.snapPick({ - canvasPos: touchMoveCanvasPos, - snapVertex: this._snapVertex, - snapEdge: this._snapEdge - }); - if (snapPickResult && snapPickResult.snappedWorldPos) { - if (pointerLens) { - pointerLens.cursorPos = snapPickResult.snappedCanvasPos; - pointerLens.snapped = true; - } - this._currentDistanceMeasurement.target.worldPos = snapPickResult.snappedWorldPos; - this._currentDistanceMeasurement.targetVisible = true; - this._currentDistanceMeasurement.wireVisible = true; - this._currentDistanceMeasurement.labelsVisible = true; - } else { - pickResult = scene.pick({ - canvasPos: touchMoveCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentDistanceMeasurement.target.worldPos = pickResult.worldPos; - this._currentDistanceMeasurement.targetVisible = true; - this._currentDistanceMeasurement.wireVisible = true; - this._currentDistanceMeasurement.labelsVisible = true; - - } - } - this._touchState = LONG_TOUCH_FINDING_TARGET; - break; - - default: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchmove: this._touchState= default -> TOUCH_FINDING_ORIGIN") - break; - } - }, {passive: true}); - - canvas.addEventListener("touchend", this._onCanvasTouchEnd = (event) => { - - const currentNumTouches = event.changedTouches.length; - - if (currentNumTouches !== 1) { - return; - } - - enableCameraMouseControl(); - - if (longTouchTimeout) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - - const touchX = event.changedTouches[0].clientX; - const touchY = event.changedTouches[0].clientY; - - touchEndCanvasPos.set([touchX, touchY]); - - switch (this._touchState) { - - case TOUCH_CANCELING: - if (longTouchTimeout) { - clearTimeout(longTouchTimeout); - longTouchTimeout = null; - } - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= TOUCH_CANCELING -> TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_ORIGIN: - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= TOUCH_FINDING_ORIGIN -> TOUCH_FINDING_ORIGIN") - break; - - case TOUCH_FINDING_TARGET: - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - break; - - case QUICK_TOUCH_FINDING_ORIGIN: - if (currentNumTouches !== 1 || - touchEndCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchEndCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchEndCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchEndCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (pointer moved, destroy measurement) -> TOUCH_FINDING_ORIGIN") - break; - } else { - const pickResult = scene.pick({ - canvasPos: touchEndCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentDistanceMeasurement = plugin.createMeasurement({ - id: math.createUUID(), - origin: { - worldPos: pickResult.worldPos - }, - target: { - worldPos: pickResult.worldPos - } - }); - this._currentDistanceMeasurement.labelsVisible = false; - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = false; - this._currentDistanceMeasurement.clickable = false; - this._currentDistanceMeasurement.wireVisible = false; - this.fire("measurementStart", this._currentDistanceMeasurement); - // enableCameraMouseControl(); - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (picked, begin measurement) -> TOUCH_FINDING_TARGET") - break; - } else { - if (this._currentDistanceMeasurement) { // Not likely needed, but safe - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (nothing picked) -> TOUCH_FINDING_ORIGIN") - break; - } - } - - case LONG_TOUCH_FINDING_ORIGIN: - if (pointerLens) { - pointerLens.visible = false; - } - if (!this._currentDistanceMeasurement) { - if (pointerLens) { - pointerLens.snapped = false; - pointerLens.visible = false; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_ORIGIN (no measurement) -> TOUCH_FINDING_ORIGIN") - } else { - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_ORIGIN (picked, begin measurement) -> TOUCH_FINDING_TARGET") - } - break; - - case QUICK_TOUCH_FINDING_TARGET: - if (currentNumTouches !== 1 || - touchEndCanvasPos[0] > touchStartCanvasPos[0] + clickTolerance || - touchEndCanvasPos[0] < touchStartCanvasPos[0] - clickTolerance || - touchEndCanvasPos[1] > touchStartCanvasPos[1] + clickTolerance || - touchEndCanvasPos[1] < touchStartCanvasPos[1] - clickTolerance) { - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_TARGET; - // console.log("touchend: (moved) this._touchState= QUICK_TOUCH_FINDING_TARGET -> TOUCH_FINDING_TARGET") - } else { - const pickResult = scene.pick({ - canvasPos: touchEndCanvasPos, - pickSurface: true - }) - if (pickResult && pickResult.worldPos) { - if (pointerLens) { - pointerLens.cursorPos = pickResult.canvasPos; - pointerLens.snapped = false; - } - this._currentDistanceMeasurement.xAxisVisible = false; - this._currentDistanceMeasurement.yAxisVisible = false; - this._currentDistanceMeasurement.zAxisVisible = false; - this._currentDistanceMeasurement.wireVisible = true; - this._currentDistanceMeasurement.originVisible = true; - this._currentDistanceMeasurement.targetVisible = true; - this._currentDistanceMeasurement.target.worldPos = pickResult.worldPos; - this._currentDistanceMeasurement.labelsVisible = true; - this._currentDistanceMeasurement.clickable = false; - this.fire("measurementEnd", this._currentDistanceMeasurement); - this._currentDistanceMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_TARGET (picked, begin measurement) -> TOUCH_FINDING_ORIGIN") - } else { - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= QUICK_TOUCH_FINDING_ORIGIN (nothing picked, destroy measurement) -> TOUCH_FINDING_ORIGIN") - - } - } - break; - - case LONG_TOUCH_FINDING_TARGET: - if (pointerLens) { - pointerLens.visible = false; - } - if (!this._currentDistanceMeasurement || !this._currentDistanceMeasurement.targetVisible) { - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_TARGET (no target found) -> TOUCH_FINDING_ORIGIN") - } else { - this._currentDistanceMeasurement.clickable = true; - this.fire("measurementEnd", this._currentDistanceMeasurement); - this._currentDistanceMeasurement = null; - this._touchState = TOUCH_FINDING_ORIGIN; - // console.log("touchend: this._touchState= LONG_TOUCH_FINDING_TARGET -> TOUCH_FINDING_ORIGIN") - } - break; - - // case TOUCH_CANCELING: - // if (pointerLens) { - // pointerLens.visible = false; - // } - // this._currentDistanceMeasurement = null; - // this._touchState = TOUCH_FINDING_ORIGIN; - // // console.log("touchend: this._touchState= default -> TOUCH_FINDING_ORIGIN") - // break; - } - - }, {passive: true}); - } - - this._active = true; } /** * Deactivates this DistanceMeasurementsControl, making it unresponsive to input. * - * Destroys any {@link DistanceMeasurement} under construction. + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsControl. + * + * @abstract */ deactivate() { - - if (!this._active) { - return; - } - - if (this.plugin.pointerLens) { - this.plugin.pointerLens.visible = false; - } - - this.reset(); - - const input = this.plugin.viewer.scene.input; - input.off(this._onInputMouseDown); - input.off(this._onInputMouseUp); - - const cameraControl = this.plugin.viewer.cameraControl; - cameraControl.off(this._onCameraControlHoverSnapOrSurface); - cameraControl.off(this._onCameraControlHoverSnapOrSurfaceOff); - - const canvas = this.plugin.viewer.scene.canvas.canvas; - canvas.removeEventListener("touchstart", this._onCanvasTouchStart); - canvas.removeEventListener("touchend", this._onCanvasTouchEnd); - - if (this._currentDistanceMeasurement) { - this.fire("measurementCancel", this._currentDistanceMeasurement); - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } - - this._active = false; } /** * Resets this DistanceMeasurementsControl. * - * Destroys any {@link DistanceMeasurement} under construction. + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsControl. * * Does nothing if the DistanceMeasurementsControl is not active. + * + * @abstract */ reset() { - if (!this._active) { - return; - } - if (this._currentDistanceMeasurement) { - this.fire("measurementCancel", this._currentDistanceMeasurement); - this._currentDistanceMeasurement.destroy(); - this._currentDistanceMeasurement = null; - } } /** - * @private + * Destroys this DistanceMeasurementsControl. + * + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsControl. + * + * @abstract */ destroy() { - this.deactivate(); - super.destroy(); - } -} - -const getCanvasPosFromEvent = function (event, canvasPos) { - if (!event) { - event = window.event; - canvasPos[0] = event.x; - canvasPos[1] = event.y; - } else { - let element = event.target; - let totalOffsetLeft = 0; - let totalOffsetTop = 0; - while (element.offsetParent) { - totalOffsetLeft += element.offsetLeft; - totalOffsetTop += element.offsetTop; - element = element.offsetParent; - } - canvasPos[0] = event.pageX - totalOffsetLeft; - canvasPos[1] = event.pageY - totalOffsetTop; } - return canvasPos; -}; - -export {DistanceMeasurementsControl}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js new file mode 100644 index 0000000000..115027b095 --- /dev/null +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js @@ -0,0 +1,354 @@ +import {Component} from "../../viewer/scene/Component.js"; +import {math} from "../../viewer/scene/math/math.js"; +import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; + +const MOUSE_FIRST_CLICK_EXPECTED = 0; +const MOUSE_SECOND_CLICK_EXPECTED = 1; + +/** + * Creates {@link DistanceMeasurement}s in a {@link DistanceMeasurementsPlugin} from mouse input. + * + * ## Usage + * + * [[Run example](/examples/measurement/#distance_createWithMouse_snapping)] + * + * ````javascript + * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, PointerLens} from "xeokit-sdk.es.js"; + * + * const viewer = new Viewer({ + * canvasId: "myCanvas", + * }); + * + * viewer.camera.eye = [-3.93, 2.85, 27.01]; + * viewer.camera.look = [4.40, 3.72, 8.89]; + * viewer.camera.up = [-0.01, 0.99, 0.039]; + * + * const xktLoader = new XKTLoaderPlugin(viewer); + * + * const sceneModel = xktLoader.load({ + * id: "myModel", + * src: "Duplex.xkt" + * }); + * + * const distanceMeasurements = new DistanceMeasurementsPlugin(viewer); + * + * const distanceMeasurementsControl = new DistanceMeasurementsMouseControl(DistanceMeasurements, { + * pointerLens: new PointerLens(viewer) + * }) + * + * distanceMeasurementsControl.snapToVertex = true; + * distanceMeasurementsControl.snapToEdge = true; + * + * distanceMeasurementsControl.activate(); + * ```` + */ +export class DistanceMeasurementsMouseControl extends DistanceMeasurementsControl { + + /** + * Creates a DistanceMeasurementsMouseControl bound to the given DistanceMeasurementsPlugin. + */ + constructor(distanceMeasurementsPlugin, cfg = {}) { + + super(distanceMeasurementsPlugin.viewer.scene); + + this.pointerLens = cfg.pointerLens; + + this._active = false; + + const markerDiv = document.createElement('div'); + const canvas = this.scene.canvas.canvas; + canvas.parentNode.insertBefore(markerDiv, canvas); + markerDiv.style.background = "black"; + markerDiv.style.border = "2px solid blue"; + markerDiv.style.borderRadius = "10px"; + markerDiv.style.width = "5px"; + markerDiv.style.height = "5px"; + markerDiv.style.margin = "-200px -200px"; + markerDiv.style.zIndex = "100"; + markerDiv.style.position = "absolute"; + markerDiv.style.pointerEvents = "none"; + + this._markerDiv = markerDiv; + + this._currentDistanceMeasurement = null; + + this._currentDistanceMeasurementInitState = { + wireVisible: null, + axisVisible: null, + xAxisVisible: null, + yaxisVisible: null, + zAxisVisible: null, + targetVisible: null, + } + + this._onCameraControlHoverSnapOrSurface = null; + this._onCameraControlHoverSnapOrSurfaceOff = null; + this._onInputMouseDown = null; + this._onInputMouseUp = null; + this._onCanvasTouchStart = null; + this._onCanvasTouchEnd = null; + this._snapToEdge = cfg.snapToEdge !== false; + this._snapToVertex = cfg.snapToVertex !== false; + this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; + + this._attachPlugin(distanceMeasurementsPlugin, cfg); + } + + _attachPlugin(distanceMeasurementsPlugin, cfg = {}) { + + /** + * The {@link DistanceMeasurementsPlugin} that owns this DistanceMeasurementsMouseControl. + * @type {DistanceMeasurementsPlugin} + */ + this.distanceMeasurementsPlugin = distanceMeasurementsPlugin; + + /** + * The {@link DistanceMeasurementsPlugin} that owns this DistanceMeasurementsMouseControl. + * @type {DistanceMeasurementsPlugin} + */ + this.plugin = distanceMeasurementsPlugin; + } + + /** + * Gets if this DistanceMeasurementsMouseControl is currently active, where it is responding to input. + * + * @returns {boolean} True if this DistanceMeasurementsMouseControl is active. + */ + get active() { + return this._active; + } + + /** + * Sets whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. + * + * This is `true` by default. + * + * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this DistanceMeasurementsMouseControl. + */ + set snapToVertex(snapToVertex) { + this._snapToVertex = snapToVertex; + } + + /** + * Gets whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. + * + * This is `true` by default. + * + * @returns {boolean} Whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. + */ + get snapToVertex() { + return this._snapToVertex; + } + + /** + * Sets whether snap-to-edge is enabled for this DistanceMeasurementsMouseControl. + * + * This is `true` by default. + * + * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this DistanceMeasurementsMouseControl. + */ + set snapToEdge(snapToEdge) { + this._snapToEdge = snapToEdge; + } + + /** + * Gets whether snap-to-edge is enabled for this DistanceMeasurementsMouseControl. + * + * This is `true` by default. + * + * @returns {boolean} Whether snap-to-edge is enabled for this DistanceMeasurementsControl. + */ + get snapToEdge() { + return this._snapToEdge; + } + + /** + * Activates this DistanceMeasurementsMouseControl, ready to respond to input. + */ + activate() { + + if (this._active) { + return; + } + + const distanceMeasurementsPlugin = this.distanceMeasurementsPlugin; + const scene = this.scene; + const cameraControl = distanceMeasurementsPlugin.viewer.cameraControl; + const canvas = scene.canvas.canvas; + const input = scene.input; + let mouseHovering = false; + const pointerWorldPos = math.vec3(); + const pointerCanvasPos = math.vec2(); + let pointerDownCanvasX; + let pointerDownCanvasY; + const clickTolerance = 20; + + this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; + + this._onCameraControlHoverSnapOrSurface = cameraControl.on("hoverSnapOrSurface", event => { + mouseHovering = true; + pointerWorldPos.set(event.worldPos); + pointerCanvasPos.set(event.canvasPos); + if (this._mouseState === MOUSE_FIRST_CLICK_EXPECTED) { + this._markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; + this._markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; + this._markerDiv.style.background = "pink"; + if (event.snappedToVertex || event.snappedToEdge) { + if (this.pointerLens) { + this.pointerLens.visible = true; + this.pointerLens.centerPos = event.cursorPos || event.canvasPos; + this.pointerLens.cursorPos = event.canvasPos; + this.pointerLens.snapped = true; + } + this._markerDiv.style.background = "greenyellow"; + this._markerDiv.style.border = "2px solid green"; + } else { + if (this.pointerLens) { + this.pointerLens.visible = true; + this.pointerLens.centerPos = event.cursorPos || event.canvasPos; + this.pointerLens.cursorPos = event.canvasPos; + this.pointerLens.snapped = false; + } + this._markerDiv.style.background = "pink"; + this._markerDiv.style.border = "2px solid red"; + } + } else { + this._markerDiv.style.marginLeft = `-10000px`; + this._markerDiv.style.marginTop = `-10000px`; + } + canvas.style.cursor = "pointer"; + if (this._currentDistanceMeasurement) { + this._currentDistanceMeasurement.wireVisible = this._currentDistanceMeasurementInitState.wireVisible; + this._currentDistanceMeasurement.axisVisible = this._currentDistanceMeasurementInitState.axisVisible && this.distanceMeasurementsPlugin.defaultAxisVisible; + this._currentDistanceMeasurement.xAxisVisible = this._currentDistanceMeasurementInitState.xAxisVisible && this.distanceMeasurementsPlugin.defaultXAxisVisible; + this._currentDistanceMeasurement.yAxisVisible = this._currentDistanceMeasurementInitState.yAxisVisible && this.distanceMeasurementsPlugin.defaultYAxisVisible; + this._currentDistanceMeasurement.zAxisVisible = this._currentDistanceMeasurementInitState.zAxisVisible && this.distanceMeasurementsPlugin.defaultZAxisVisible; + this._currentDistanceMeasurement.targetVisible = this._currentDistanceMeasurementInitState.targetVisible; + this._currentDistanceMeasurement.target.worldPos = pointerWorldPos.slice(); + this._markerDiv.style.marginLeft = `-10000px`; + this._markerDiv.style.marginTop = `-10000px`; + } + }); + + + this._onInputMouseDown = input.on("mousedown", (coords) => { + pointerDownCanvasX = coords[0]; + pointerDownCanvasY = coords[1]; + }); + + this._onInputMouseUp = input.on("mouseup", (coords) => { + if (coords[0] > pointerDownCanvasX + clickTolerance || + coords[0] < pointerDownCanvasX - clickTolerance || + coords[1] > pointerDownCanvasY + clickTolerance || + coords[1] < pointerDownCanvasY - clickTolerance) { + return; + } + if (this._currentDistanceMeasurement) { + if (mouseHovering) { + this._currentDistanceMeasurement.clickable = true; + this.distanceMeasurementsPlugin.fire("measurementEnd", this._currentDistanceMeasurement); + this._currentDistanceMeasurement = null; + } else { + this._currentDistanceMeasurement.destroy(); + this.distanceMeasurementsPlugin.fire("measurementCancel", this._currentDistanceMeasurement); + this._currentDistanceMeasurement = null; + } + } else { + if (mouseHovering) { + this._currentDistanceMeasurement = distanceMeasurementsPlugin.createMeasurement({ + id: math.createUUID(), + origin: { + worldPos: pointerWorldPos.slice() + }, + target: { + worldPos: pointerWorldPos.slice() + }, + approximate: true + }); + this._currentDistanceMeasurementInitState.axisVisible = this._currentDistanceMeasurement.axisVisible && this.distanceMeasurementsPlugin.defaultAxisVisible; + this._currentDistanceMeasurementInitState.xAxisVisible = this._currentDistanceMeasurement.xAxisVisible && this.distanceMeasurementsPlugin.defaultXAxisVisible; + this._currentDistanceMeasurementInitState.yAxisVisible = this._currentDistanceMeasurement.yAxisVisible && this.distanceMeasurementsPlugin.defaultYAxisVisible; + this._currentDistanceMeasurementInitState.zAxisVisible = this._currentDistanceMeasurement.zAxisVisible && this.distanceMeasurementsPlugin.defaultZAxisVisible; + this._currentDistanceMeasurementInitState.wireVisible = this._currentDistanceMeasurement.wireVisible; + this._currentDistanceMeasurementInitState.targetVisible = this._currentDistanceMeasurement.targetVisible; + this._currentDistanceMeasurement.clickable = false; + this.fire("measurementStart", this._currentDistanceMeasurement); + } + } + }); + + this._onCameraControlHoverSnapOrSurfaceOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { + if (this.pointerLens) { + this.pointerLens.visible = true; + this.pointerLens.centerPos = event.cursorPos || event.canvasPos; + this.pointerLens.cursorPos = event.canvasPos; + } + mouseHovering = false; + this._markerDiv.style.marginLeft = `-100px`; + this._markerDiv.style.marginTop = `-100px`; + if (this._currentDistanceMeasurement) { + this._currentDistanceMeasurement.wireVisible = false; + this._currentDistanceMeasurement.targetVisible = false; + this._currentDistanceMeasurement.axisVisible = false; + } + canvas.style.cursor = "default"; + }); + + this._active = true; + } + + /** + * Deactivates this DistanceMeasurementsMouseControl, making it unresponsive to input. + * + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsMouseControl. + */ + deactivate() { + if (!this._active) { + return; + } + if (this.pointerLens) { + this.pointerLens.visible = false; + } + this.reset(); + const input = this.distanceMeasurementsPlugin.viewer.scene.input; + input.off(this._onInputMouseDown); + input.off(this._onInputMouseUp); + const cameraControl = this.distanceMeasurementsPlugin.viewer.cameraControl; + cameraControl.off(this._onCameraControlHoverSnapOrSurface); + cameraControl.off(this._onCameraControlHoverSnapOrSurfaceOff); + if (this._currentDistanceMeasurement) { + this.distanceMeasurementsPlugin.fire("measurementCancel", this._currentDistanceMeasurement); + this._currentDistanceMeasurement.destroy(); + this._currentDistanceMeasurement = null; + } + this._active = false; + } + + /** + * Resets this DistanceMeasurementsMouseControl. + * + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsMouseControl. + * + * Does nothing if the DistanceMeasurementsMouseControl is not active. + */ + reset() { + if (!this._active) { + return; + } + if (this._currentDistanceMeasurement) { + this.distanceMeasurementsPlugin.fire("measurementCancel", this._currentDistanceMeasurement); + this._currentDistanceMeasurement.destroy(); + this._currentDistanceMeasurement = null; + } + } + + /** + * Destroys this DistanceMeasurementsMouseControl. + * + * Destroys any {@link DistanceMeasurement} under construction by this DistanceMeasurementsMouseControl. + */ + destroy() { + this.deactivate(); + super.destroy(); + } +} diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsPlugin.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsPlugin.js index dd42f4525b..dd38030606 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsPlugin.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsPlugin.js @@ -1,6 +1,6 @@ import {Plugin} from "../../viewer/Plugin.js"; import {DistanceMeasurement} from "./DistanceMeasurement.js"; -import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; +import {DistanceMeasurementsMouseControl} from "./DistanceMeasurementsMouseControl"; /** * {@link Viewer} plugin for measuring point-to-point distances. @@ -17,7 +17,7 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * * As shown on the screen capture above, a DistanceMeasurement has one wire (light blue) that shows the direct point-to-point measurement, * and three more wires (red, green and blue) that show the distance on each of the World-space X, Y and Z axis. * * Create DistanceMeasurements programmatically with {@link DistanceMeasurementsPlugin#createMeasurement}. - * * Create DistanceMeasurements interactively using the {@link DistanceMeasurementsControl}, located at {@link DistanceMeasurementsPlugin#control}. + * * Create DistanceMeasurements interactively using a {@link DistanceMeasurementsControl}. * * Existing DistanceMeasurements are registered by ID in {@link DistanceMeasurementsPlugin#measurements}. * * Destroy DistanceMeasurements using {@link DistanceMeasurementsPlugin#destroyMeasurement}. * * Configure global measurement units and scale via {@link Metrics}, located at {@link Scene#metrics}. @@ -29,7 +29,7 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * Note how each DistanceMeasurement has ````origin```` and ````target```` endpoints, which each indicate a 3D World-space * position on the surface of an {@link Entity}. The endpoints can be attached to the same Entity, or to different Entitys. * - * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/#measurements_distance_modelWithMeasurements)] + * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/measurements/#distance_modelWithMeasurements)] * * ````JavaScript * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin} from "xeokit-sdk.es.js"; @@ -83,20 +83,21 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * }); * ```` * - * ## Example 2: Creating DistanceMeasurements Interactively + * ## Example 2: Creating DistanceMeasurements with Mouse Input * - * In our second example, we'll use an {@link XKTLoaderPlugin} to load a model, then we'll use the DistanceMeasurementPlugin's {@link DistanceMeasurementsControl} to interactively create {@link DistanceMeasurement}s with mouse or touch input. + * In our second example, we'll use an {@link XKTLoaderPlugin} to load a model, then we'll use a + * {@link DistanceMeasurementsMouseControl} to interactively create {@link DistanceMeasurement}s with mouse input. * - * After we've activated the DistanceMeasurementsControl, the first click on any {@link Entity} begins constructing a DistanceMeasurement, fixing its + * After we've activated the DistanceMeasurementsMouseControl, the first click on any {@link Entity} begins constructing a DistanceMeasurement, fixing its * origin to that Entity. The next click on any Entity will complete the DistanceMeasurement, fixing its target to that second Entity. * - * The DistanceMeasurementControl will then wait for the next click on any Entity, to begin constructing + * The DistanceMeasurementsMouseControl will then wait for the next click on any Entity, to begin constructing * another DistanceMeasurement, and so on, until deactivated again. * - * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/#measurements_distance_createWithMouse)] + * [[Run example](/examples/measurement/#distance_createWithMouse)] * * ````JavaScript - * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin} from "xeokit-sdk.es.js"; + * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, PointerLens} from "xeokit-sdk.es.js"; * * const viewer = new Viewer({ * canvasId: "myCanvas", @@ -111,11 +112,18 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * * const distanceMeasurements = new DistanceMeasurementsPlugin(viewer); * + * const distanceMeasurementsControl = new DistanceMeasurementsMouseControl(distanceMeasurements, { + * pointerLens : new PointerLens(viewer) + * }) + * + * distanceMeasurementsControl.snapToVertex = true; + * distanceMeasurementsControl.snapToEdge = true; + * + * distanceMeasurementsControl.activate(); + * * const model = xktLoader.load({ * src: "./models/xkt/duplex/duplex.xkt" * }); - * - * distanceMeasurements.control.activate(); // <------------ Activate the DistanceMeasurementsControl * ```` * * ## Example 3: Configuring Measurement Units and Scale @@ -135,13 +143,13 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * * ## Example 4: Attaching Mouse Handlers * - * In our fourth example, we'll attach even handlers to our plugin, to catch when the user + * In our fourth example, we'll attach event handlers to our plugin, to catch when the user * hovers or right-clicks over our measurements. * * [[Run example](https://xeokit.github.io/xeokit-sdk/examples/#measurements_distance_modelWithMeasurements)] * * ````javascript - * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin} from "xeokit-sdk.es.js"; + * import {Viewer, XKTLoaderPlugin, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, PointerLens} from "xeokit-sdk.es.js"; * * const viewer = new Viewer({ * canvasId: "myCanvas", @@ -156,6 +164,15 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * * const distanceMeasurements = new DistanceMeasurementsPlugin(viewer); * + * const distanceMeasurementsControl = new DistanceMeasurementsMouseControl(distanceMeasurements, { + * pointerLens : new PointerLens(viewer) + * }) + * + * distanceMeasurementsControl.snapToVertex = true; + * distanceMeasurementsControl.snapToEdge = true; + * + * distanceMeasurementsControl.activate(); + * * distanceMeasurements.on("mouseOver", (e) => { * e.measurement.setHighlighted(true); * }); @@ -170,7 +187,7 @@ import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; * }); * * const model = xktLoader.load({ - * src: "./models/xkt/duplex/duplex.xkt" + * src: "Duplex.xkt" * }); * * model.on("loaded", () => { @@ -225,7 +242,7 @@ class DistanceMeasurementsPlugin extends Plugin { * @param {boolean} [cfg.defaultZAxisVisible=true] The default value of the DistanceMeasurements `zAxisVisible` property. * @param {string} [cfg.defaultColor=#00BBFF] The default color of the length dots, wire and label. * @param {number} [cfg.zIndex] If set, the wires, dots and labels will have this zIndex (+1 for dots and +2 for labels). - * @param {PointerLens} [cfg.pointerLens] A PointerLens to help the user position the pointer. This can be shared with other plugins. + * @param {PointerCircle} [cfg.pointerLens] A PointerLens to help the user position the pointer. This can be shared with other plugins. */ constructor(viewer, cfg = {}) { @@ -235,7 +252,7 @@ class DistanceMeasurementsPlugin extends Plugin { this._container = cfg.container || document.body; - this._control = new DistanceMeasurementsControl(this, {}); + this._defaultControl = null; this._measurements = {}; @@ -298,19 +315,23 @@ class DistanceMeasurementsPlugin extends Plugin { /** * Gets the PointerLens attached to this DistanceMeasurementsPlugin. - * @returns {PointerLens} + * @returns {PointerCircle} */ get pointerLens() { return this._pointerLens; } /** - * Gets the {@link DistanceMeasurementsControl}, which creates {@link DistanceMeasurement}s from user input. + * Gets the default {@link DistanceMeasurementsControl}. * * @type {DistanceMeasurementsControl} + * @deprecated */ get control() { - return this._control; + if (!this._defaultControl) { + this._defaultControl = new DistanceMeasurementsMouseControl(this, {}); + } + return this._defaultControl; } /** diff --git a/src/plugins/DistanceMeasurementsPlugin/index.js b/src/plugins/DistanceMeasurementsPlugin/index.js index 18e1b6c286..7ef9280499 100644 --- a/src/plugins/DistanceMeasurementsPlugin/index.js +++ b/src/plugins/DistanceMeasurementsPlugin/index.js @@ -1 +1,3 @@ -export * from "./DistanceMeasurementsPlugin.js"; \ No newline at end of file +export * from "./DistanceMeasurementsPlugin.js"; +export * from "./DistanceMeasurementsControl.js"; +export * from "./DistanceMeasurementsMouseControl.js"; diff --git a/src/viewer/scene/CameraControl/CameraControl.js b/src/viewer/scene/CameraControl/CameraControl.js index 44fbe92f69..cd1119e393 100644 --- a/src/viewer/scene/CameraControl/CameraControl.js +++ b/src/viewer/scene/CameraControl/CameraControl.js @@ -645,8 +645,8 @@ class CameraControl extends Component { smartPivot: false, doubleClickTimeFrame: 250, - snapVertex: DEFAULT_SNAP_VERTEX, - snapEdge: DEFAULT_SNAP_EDGE, + snapToVertex: DEFAULT_SNAP_VERTEX, + snapToEdge: DEFAULT_SNAP_EDGE, snapRadius: DEFAULT_SNAP_PICK_RADIUS, // Rotation @@ -905,10 +905,10 @@ class CameraControl extends Component { /** * Sets whether the pointer snap to vertex. * - * @param {boolean} snapVertex + * @param {boolean} snapToVertex */ - set snapVertex(snapVertex) { - this._configs.snapVertex = !!snapVertex; + set snapToVertex(snapToVertex) { + this._configs.snapToVertex = !!snapToVertex; } /** @@ -916,17 +916,17 @@ class CameraControl extends Component { * * @returns {boolean} */ - get snapVertex() { - return this._configs.snapVertex; + get snapToVertex() { + return this._configs.snapToVertex; } /** * Sets whether the pointer snap to edge. * - * @param {boolean} snapEdge + * @param {boolean} snapToEdge */ - set snapEdge(snapEdge) { - this._configs.snapEdge = !!snapEdge; + set snapToEdge(snapToEdge) { + this._configs.snapToEdge = !!snapToEdge; } /** @@ -934,8 +934,8 @@ class CameraControl extends Component { * * @returns {boolean} */ - get snapEdge() { - return this._configs.snapEdge; + get snapToEdge() { + return this._configs.snapToEdge; } /** diff --git a/src/viewer/scene/CameraControl/lib/controllers/PickController.js b/src/viewer/scene/CameraControl/lib/controllers/PickController.js index a1fde2bc44..f80616156a 100644 --- a/src/viewer/scene/CameraControl/lib/controllers/PickController.js +++ b/src/viewer/scene/CameraControl/lib/controllers/PickController.js @@ -98,8 +98,8 @@ class PickController { const snapPickResult = this._scene.snapPick({ canvasPos: this.pickCursorPos, snapRadius: this._configs.snapRadius, - snapVertex: this._configs.snapVertex, - snapEdge: this._configs.snapEdge, + snapToVertex: this._configs.snapToVertex, + snapToEdge: this._configs.snapToEdge, }); if (snapPickResult && snapPickResult.snappedWorldPos) { this.snapPickResult = snapPickResult; diff --git a/src/viewer/scene/scene/Scene.js b/src/viewer/scene/scene/Scene.js index 5d0f5b0da7..a7a843e232 100644 --- a/src/viewer/scene/scene/Scene.js +++ b/src/viewer/scene/scene/Scene.js @@ -2216,15 +2216,15 @@ class Scene extends Component { * @param {Object} params Picking parameters. * @param {Number[]} [params.canvasPos] Canvas-space coordinates. When ray-picking, this will override the **origin** and ** direction** parameters and will cause the ray to be fired through the canvas at this position, directly along the negative View-space Z-axis. * @param {Number} [params.snapRadius=30] The snap radius, in canvas pixels - * @param {boolean} [params.snapVertex=true] Whether to snap to vertex. - * @param {boolean} [params.snapEdge=true] Whether to snap to edge. + * @param {boolean} [params.snapToVertex=true] Whether to snap to vertex. + * @param {boolean} [params.snapToEdge=true] Whether to snap to edge. */ snapPick(params) { return this._renderer.snapPick( params.canvasPos, params.snapRadius || 30, - params.snapVertex, - params.snapEdge, + params.snapToVertex, + params.snapToEdge, ); } diff --git a/src/viewer/scene/webgl/Renderer.js b/src/viewer/scene/webgl/Renderer.js index 7ba97013f0..8554c269a9 100644 --- a/src/viewer/scene/webgl/Renderer.js +++ b/src/viewer/scene/webgl/Renderer.js @@ -1270,14 +1270,14 @@ const Renderer = function (scene, options) { /** * @param {[number, number]} canvasPos * @param {number} [snapRadiusInPixels=30] - * @param {boolean} [snapVertex=true] - * @param {boolean} [snapEdge=true] + * @param {boolean} [snapToVertex=true] + * @param {boolean} [snapToEdge=true] * * @returns {{worldPos:number[],snappedWorldPos:null|number[],snappedCanvasPos:null|number[], snapType:null|"vertex"|"edge"}} */ - this.snapPick = function (canvasPos, snapRadiusInPixels = 30, snapVertex = true, snapEdge = true) { + this.snapPick = function (canvasPos, snapRadiusInPixels = 30, snapToVertex = true, snapToEdge = true) { - if (!snapVertex && !snapEdge) { + if (!snapToVertex && !snapToEdge) { return this.pick({canvasPos, pickSurface: true}); } @@ -1346,7 +1346,7 @@ const Renderer = function (scene, options) { gl.depthMask(false); - if (snapVertex && snapEdge) { + if (snapToVertex && snapToEdge) { frameCtx.snapMode = "edge"; snapPickDrawSnapDepths(frameCtx); @@ -1355,7 +1355,7 @@ const Renderer = function (scene, options) { snapPickDrawSnapDepths(frameCtx); } else { - frameCtx.snapMode = snapVertex ? "vertex" : "edge"; + frameCtx.snapMode = snapToVertex ? "vertex" : "edge"; snapPickDrawSnapDepths(frameCtx); } @@ -1403,7 +1403,7 @@ const Renderer = function (scene, options) { x, y, dist, - isVertex: snapVertex && snapEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapVertex, + isVertex: snapToVertex && snapToEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapToVertex, result: [ snapPickResultArray[i + 0], snapPickResultArray[i + 1], From dbdba586d4a1c16c70baa5f2eaae451566edfa2d Mon Sep 17 00:00:00 2001 From: lindsay Date: Thu, 12 Oct 2023 00:48:24 +0200 Subject: [PATCH 2/3] Ability to enable / disable snapping on measurements controls --- .../AngleMeasurementsControl.js | 39 +-- .../AngleMeasurementsMouseControl.js | 231 +++++++++--------- .../DistanceMeasurementsControl.js | 39 +-- .../DistanceMeasurementsMouseControl.js | 151 ++++++------ 4 files changed, 208 insertions(+), 252 deletions(-) diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js index 36ef2cd8fe..fa2491b054 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsControl.js @@ -18,47 +18,28 @@ export class AngleMeasurementsControl extends Component { } /** - * Gets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * Sets whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsControl. * * This is `true` by default. * - * @returns {boolean} - * @abstract - */ - get snapToVertex() { - } - - /** - * Sets whether snap-to-vertex is enabled for this AngleMeasurementsControl. + * Internally, this deactivates then activates the AngleMeasurementsControl when changed, which means that + * it will destroy any AngleMeasurements currently under construction, and incurs some overhead, since it unbinds + * and rebinds various input handlers. * - * This is `true` by default. - * - * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this AngleMeasurementsControl. - * @abstract + * @param {boolean} snapping Whether to enable snap-to-vertex and snap-edge for this AngleMeasurementsControl. */ - set snapToVertex(snapToVertex) { + set snapping(snapping) { } /** - * Gets whether snap-to-edge is enabled for this AngleMeasurementsControl. + * Gets whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsControl. * * This is `true` by default. * - * @returns {boolean} - * @abstract - */ - get snapToEdge() { - } - - /** - * Sets whether snap-to-edge is enabled for this AngleMeasurementsControl. - * - * This is `true` by default. - * - * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this AngleMeasurementsControl. - * @abstract + * @returns {boolean} Whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsControl. */ - set snapToEdge(snapToEdge) { + get snapping() { + return true; } /** diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js index f4c2597a70..9172905e80 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js @@ -36,8 +36,7 @@ const MOUSE_FINDING_TARGET = 2; * pointerLens : new PointerLens(viewer) * }) * - * angleMeasurementsMouseControl.snapToVertex = true; - * angleMeasurementsMouseControl.snapToEdge = true; + * angleMeasurementsMouseControl.snapping = true; * * angleMeasurementsMouseControl.activate(); * ```` @@ -46,6 +45,11 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { /** * Creates a AngleMeasurementsMouseControl bound to the given AngleMeasurementsPlugin. + * + * @param {AngleMeasurementsPlugin} angleMeasurementsPlugin The AngleMeasurementsPlugin to control. + * @param {*} [cfg] Configuration + * @param {PointerLens} [cfg.pointerLens] A PointerLens to use to provide a magnified view of the cursor when snapping is enabled. + * @param {boolean} [cfg.snapping=true] Whether to initially enable snap-to-vertex and snap-to-edge for this AngleMeasurementsMouseControl. */ constructor(angleMeasurementsPlugin, cfg = {}) { @@ -81,8 +85,7 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { this._onInputMouseDown = null; this._onInputMouseUp = null; - this._snapToEdge = cfg.snapToEdge !== false; - this._snapToVertex = cfg.snapToVertex !== false; + this._snapping = cfg.snapping !== false; this._attachPlugin(angleMeasurementsPlugin, cfg); } @@ -114,47 +117,35 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { } /** - * Sets whether snap-to-vertex is enabled for this AngleMeasurementsMouseControl. + * Sets whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsMouseControl. * * This is `true` by default. * - * @param snapToVertex True if this nap-to-vertex is to be enabled for this AngleMeasurementsMouseControl. - */ - set snapToVertex(snapToVertex) { - this._snapToVertex = snapToVertex; - } - - /** - * Gets whether snap-to-vertex is enabled for this AngleMeasurementsMouseControl. - * - * This is `true` by default. - * - * @returns {boolean} True if snap-to-vertex is enabled for this AngleMeasurementsMouseControl. - */ - get snapToVertex() { - return this._snapToVertex; - } - - /** - * Sets whether snap-to-edge is enabled for this AngleMeasurementsMouseControl. - * - * This is `true` by default. + * Internally, this deactivates then activates the AngleMeasurementsMouseControl when changed, which means that + * it will destroy any AngleMeasurements currently under construction, and incurs some overhead, since it unbinds + * and rebinds various input handlers. * - * @param {boolean} snapToEdge True if this snap-to-edge is to be enabled for this AngleMeasurementsMouseControl. + * @param {boolean} snapping Whether to enable snap-to-vertex and snap-edge for this AngleMeasurementsMouseControl. */ - set snapToEdge(snapToEdge) { - this._snapToEdge = snapToEdge; + set snapping(snapping) { + if (snapping !== this._snapping) { + this._snapping = snapping; + this.deactivate(); + this.activate(); + } else { + this._snapping = snapping; + } } /** - * Gets whether snap-to-edge is enabled for this AngleMeasurementsMouseControl. + * Gets whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsMouseControl. * * This is `true` by default. * - * @returns {boolean} True if this snap-to-edge is enabled for this AngleMeasurementsMouseControl. + * @returns {boolean} Whether snap-to-vertex and snap-to-edge are enabled for this AngleMeasurementsMouseControl. */ - get snapToEdge() { - return this._snapToEdge; + get snapping() { + return this._snapping; } /** @@ -178,60 +169,64 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { const mouseWorldPos = math.vec3(); const mouseHoverCanvasPos = math.vec2(); this._currentAngleMeasurement = null; - this._onMouseHoverSurface = cameraControl.on("hoverSnapOrSurface", event => { - if (event.snappedToVertex || event.snappedToEdge) { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = true; - } - this.markerDiv.style.background = "greenyellow"; - this.markerDiv.style.border = "2px solid green"; - } else { - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos || event.canvasPos; - pointerLens.cursorPos = event.canvasPos; - pointerLens.snapped = false; - } - this.markerDiv.style.background = "pink"; - this.markerDiv.style.border = "2px solid red"; - } - mouseHovering = true; - mouseHoverEntity = event.entity; - mouseWorldPos.set(event.worldPos); - mouseHoverCanvasPos.set(event.canvasPos); - switch (this._mouseState) { - case MOUSE_FINDING_ORIGIN: - this.markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; - this.markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; - break; - case MOUSE_FINDING_CORNER: - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.originWireVisible = true; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.cornerVisible = true; - this._currentAngleMeasurement.angleVisible = false; - this._currentAngleMeasurement.corner.worldPos = event.worldPos; + this._onMouseHoverSurface = cameraControl.on( + this._snapping + ? "hoverSnapOrSurface" + : "hoverSurface", + event => { + if (event.snappedToVertex || event.snappedToEdge) { + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos || event.canvasPos; + pointerLens.cursorPos = event.canvasPos; + pointerLens.snapped = true; } - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - canvas.style.cursor = "pointer"; - break; - case MOUSE_FINDING_TARGET: - if (this._currentAngleMeasurement) { - this._currentAngleMeasurement.targetWireVisible = true; - this._currentAngleMeasurement.targetVisible = true; - this._currentAngleMeasurement.angleVisible = true; - this._currentAngleMeasurement.target.worldPos = event.worldPos; + this.markerDiv.style.background = "greenyellow"; + this.markerDiv.style.border = "2px solid green"; + } else { + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos || event.canvasPos; + pointerLens.cursorPos = event.canvasPos; + pointerLens.snapped = false; } - this.markerDiv.style.marginLeft = `-10000px`; - this.markerDiv.style.marginTop = `-10000px`; - canvas.style.cursor = "pointer"; - break; - } - }); + this.markerDiv.style.background = "pink"; + this.markerDiv.style.border = "2px solid red"; + } + mouseHovering = true; + mouseHoverEntity = event.entity; + mouseWorldPos.set(event.worldPos); + mouseHoverCanvasPos.set(event.canvasPos); + switch (this._mouseState) { + case MOUSE_FINDING_ORIGIN: + this.markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; + this.markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; + break; + case MOUSE_FINDING_CORNER: + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.originWireVisible = true; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.cornerVisible = true; + this._currentAngleMeasurement.angleVisible = false; + this._currentAngleMeasurement.corner.worldPos = event.worldPos; + } + this.markerDiv.style.marginLeft = `-10000px`; + this.markerDiv.style.marginTop = `-10000px`; + canvas.style.cursor = "pointer"; + break; + case MOUSE_FINDING_TARGET: + if (this._currentAngleMeasurement) { + this._currentAngleMeasurement.targetWireVisible = true; + this._currentAngleMeasurement.targetVisible = true; + this._currentAngleMeasurement.angleVisible = true; + this._currentAngleMeasurement.target.worldPos = event.worldPos; + } + this.markerDiv.style.marginLeft = `-10000px`; + this.markerDiv.style.marginTop = `-10000px`; + canvas.style.cursor = "pointer"; + break; + } + }); this._onInputMouseDown = input.on("mousedown", (coords) => { lastMouseCanvasX = coords[0]; lastMouseCanvasY = coords[1]; @@ -306,37 +301,41 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { break; } }); - this._onMouseHoverOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { - mouseHovering = false; - if (pointerLens) { - pointerLens.visible = true; - pointerLens.centerPos = event.cursorPos; - pointerLens.cursorPos = event.cursorPos; - pointerLens.snapped = false; - } - this.markerDiv.style.marginLeft = `-100px`; - this.markerDiv.style.marginTop = `-100px`; - if (this._currentAngleMeasurement) { - switch (this._mouseState) { - case MOUSE_FINDING_ORIGIN: - this._currentAngleMeasurement.originVisible = false; - break; - case MOUSE_FINDING_CORNER: - this._currentAngleMeasurement.cornerVisible = false; - this._currentAngleMeasurement.originWireVisible = false; - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - break; - case MOUSE_FINDING_TARGET: - this._currentAngleMeasurement.targetVisible = false; - this._currentAngleMeasurement.targetWireVisible = false; - this._currentAngleMeasurement.angleVisible = false; - break; + this._onMouseHoverOff = cameraControl.on( + this._snapping + ? "hoverSnapOrSurfaceOff" + : "hoverOff", + event => { + mouseHovering = false; + if (pointerLens) { + pointerLens.visible = true; + pointerLens.centerPos = event.cursorPos; + pointerLens.cursorPos = event.cursorPos; + pointerLens.snapped = false; } - canvas.style.cursor = "default"; - } - }); + this.markerDiv.style.marginLeft = `-100px`; + this.markerDiv.style.marginTop = `-100px`; + if (this._currentAngleMeasurement) { + switch (this._mouseState) { + case MOUSE_FINDING_ORIGIN: + this._currentAngleMeasurement.originVisible = false; + break; + case MOUSE_FINDING_CORNER: + this._currentAngleMeasurement.cornerVisible = false; + this._currentAngleMeasurement.originWireVisible = false; + this._currentAngleMeasurement.targetVisible = false; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.angleVisible = false; + break; + case MOUSE_FINDING_TARGET: + this._currentAngleMeasurement.targetVisible = false; + this._currentAngleMeasurement.targetWireVisible = false; + this._currentAngleMeasurement.angleVisible = false; + break; + } + canvas.style.cursor = "default"; + } + }); this._active = true; } @@ -384,7 +383,7 @@ export class AngleMeasurementsMouseControl extends AngleMeasurementsControl { } /** - * Destroys this DistanceMeasurementsMouseControl. + * Destroys this AngleMeasurementsMouseControl. */ destroy() { this.deactivate(); diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js index d94b462bd4..3ac97b9fe2 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsControl.js @@ -18,47 +18,28 @@ export class DistanceMeasurementsControl extends Component { } /** - * Gets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * Sets whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsControl. * * This is `true` by default. * - * @returns {boolean} Whether snap-to-vertex is enabled for this DistanceMeasurementsControl. - * @abstract - */ - get snapToVertex() { - } - - /** - * Sets whether snap-to-vertex is enabled for this DistanceMeasurementsControl. + * Internally, this deactivates then activates the DistanceMeasurementsControl when changed, which means that + * it will destroy any DistanceMeasurements currently under construction, and incurs some overhead, since it unbinds + * and rebinds various input handlers. * - * This is `true` by default. - * - * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this DistanceMeasurementsControl. - * @abstract + * @param {boolean} snapping Whether to enable snap-to-vertex and snap-edge for this DistanceMeasurementsControl. */ - set snapToVertex(snapToVertex) { + set snapping(snapping) { } /** - * Gets whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * Gets whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsControl. * * This is `true` by default. * - * @returns {boolean} Whether snap-to-edge is enabled for this DistanceMeasurementsControl. - * @abstract - */ - get snapToEdge() { - } - - /** - * Sets whether snap-to-edge is enabled for this DistanceMeasurementsControl. - * - * This is `true` by default. - * - * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this DistanceMeasurementsControl. - * @abstract + * @returns {boolean} Whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsControl. */ - set snapToEdge(snapToEdge) { + get snapping() { + return true; } /** diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js index 115027b095..01f9d870df 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js @@ -1,4 +1,3 @@ -import {Component} from "../../viewer/scene/Component.js"; import {math} from "../../viewer/scene/math/math.js"; import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; @@ -36,16 +35,20 @@ const MOUSE_SECOND_CLICK_EXPECTED = 1; * pointerLens: new PointerLens(viewer) * }) * - * distanceMeasurementsControl.snapToVertex = true; - * distanceMeasurementsControl.snapToEdge = true; + * distanceMeasurementsControl.snapping = true; * * distanceMeasurementsControl.activate(); * ```` */ -export class DistanceMeasurementsMouseControl extends DistanceMeasurementsControl { +export class DistanceMeasurementsMouseControl extends DistanceMeasurementsControl { /** * Creates a DistanceMeasurementsMouseControl bound to the given DistanceMeasurementsPlugin. + * + * @param {DistanceMeasurementsPlugin} distanceMeasurementsPlugin The AngleMeasurementsPlugin to control. + * @param [cfg] Configuration + * @param {PointerLens} [cfg.pointerLens] A PointerLens to use to provide a magnified view of the cursor when snapping is enabled. + * @param {boolean} [cfg.snapping=true] Whether to initially enable snap-to-vertex and snap-to-edge for this DistanceMeasurementsMouseControl. */ constructor(distanceMeasurementsPlugin, cfg = {}) { @@ -87,8 +90,7 @@ export class DistanceMeasurementsMouseControl extends DistanceMeasurementsContr this._onInputMouseUp = null; this._onCanvasTouchStart = null; this._onCanvasTouchEnd = null; - this._snapToEdge = cfg.snapToEdge !== false; - this._snapToVertex = cfg.snapToVertex !== false; + this._snapping = cfg.snapping !== false; this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; this._attachPlugin(distanceMeasurementsPlugin, cfg); @@ -119,47 +121,35 @@ export class DistanceMeasurementsMouseControl extends DistanceMeasurementsContr } /** - * Sets whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. + * Sets whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsMouseControl. * * This is `true` by default. * - * @param {boolean} snapToVertex Whether to enable snap-to-vertex for this DistanceMeasurementsMouseControl. - */ - set snapToVertex(snapToVertex) { - this._snapToVertex = snapToVertex; - } - - /** - * Gets whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. - * - * This is `true` by default. + * Internally, this deactivates then activates the DistanceMeasurementsMouseControl when changed, which means that + * it will destroy any DistanceMeasurements currently under construction, and incurs some overhead, since it unbinds + * and rebinds various input handlers. * - * @returns {boolean} Whether snap-to-vertex is enabled for this DistanceMeasurementsMouseControl. + * @param {boolean} snapping Whether to enable snap-to-vertex and snap-edge for this DistanceMeasurementsMouseControl. */ - get snapToVertex() { - return this._snapToVertex; - } - - /** - * Sets whether snap-to-edge is enabled for this DistanceMeasurementsMouseControl. - * - * This is `true` by default. - * - * @param snapToEdge {boolean} snapToEdge Whether to enable snap-to-edge for this DistanceMeasurementsMouseControl. - */ - set snapToEdge(snapToEdge) { - this._snapToEdge = snapToEdge; + set snapping(snapping) { + if (snapping !== this._snapping) { + this._snapping = snapping; + this.deactivate(); + this.activate(); + } else { + this._snapping = snapping; + } } /** - * Gets whether snap-to-edge is enabled for this DistanceMeasurementsMouseControl. + * Gets whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsMouseControl. * * This is `true` by default. * - * @returns {boolean} Whether snap-to-edge is enabled for this DistanceMeasurementsControl. + * @returns {boolean} Whether snap-to-vertex and snap-to-edge are enabled for this DistanceMeasurementsMouseControl. */ - get snapToEdge() { - return this._snapToEdge; + get snapping() { + return this._snapping; } /** @@ -185,51 +175,53 @@ export class DistanceMeasurementsMouseControl extends DistanceMeasurementsContr this._mouseState = MOUSE_FIRST_CLICK_EXPECTED; - this._onCameraControlHoverSnapOrSurface = cameraControl.on("hoverSnapOrSurface", event => { - mouseHovering = true; - pointerWorldPos.set(event.worldPos); - pointerCanvasPos.set(event.canvasPos); - if (this._mouseState === MOUSE_FIRST_CLICK_EXPECTED) { - this._markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; - this._markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; - this._markerDiv.style.background = "pink"; - if (event.snappedToVertex || event.snappedToEdge) { - if (this.pointerLens) { - this.pointerLens.visible = true; - this.pointerLens.centerPos = event.cursorPos || event.canvasPos; - this.pointerLens.cursorPos = event.canvasPos; - this.pointerLens.snapped = true; + this._onCameraControlHoverSnapOrSurface = cameraControl.on( + this._snapping + ? "hoverSnapOrSurface" + : "hoverSurface", event => { + mouseHovering = true; + pointerWorldPos.set(event.worldPos); + pointerCanvasPos.set(event.canvasPos); + if (this._mouseState === MOUSE_FIRST_CLICK_EXPECTED) { + this._markerDiv.style.marginLeft = `${event.canvasPos[0] - 5}px`; + this._markerDiv.style.marginTop = `${event.canvasPos[1] - 5}px`; + this._markerDiv.style.background = "pink"; + if (event.snappedToVertex || event.snappedToEdge) { + if (this.pointerLens) { + this.pointerLens.visible = true; + this.pointerLens.centerPos = event.cursorPos || event.canvasPos; + this.pointerLens.cursorPos = event.canvasPos; + this.pointerLens.snapped = true; + } + this._markerDiv.style.background = "greenyellow"; + this._markerDiv.style.border = "2px solid green"; + } else { + if (this.pointerLens) { + this.pointerLens.visible = true; + this.pointerLens.centerPos = event.cursorPos || event.canvasPos; + this.pointerLens.cursorPos = event.canvasPos; + this.pointerLens.snapped = false; + } + this._markerDiv.style.background = "pink"; + this._markerDiv.style.border = "2px solid red"; } - this._markerDiv.style.background = "greenyellow"; - this._markerDiv.style.border = "2px solid green"; } else { - if (this.pointerLens) { - this.pointerLens.visible = true; - this.pointerLens.centerPos = event.cursorPos || event.canvasPos; - this.pointerLens.cursorPos = event.canvasPos; - this.pointerLens.snapped = false; - } - this._markerDiv.style.background = "pink"; - this._markerDiv.style.border = "2px solid red"; + this._markerDiv.style.marginLeft = `-10000px`; + this._markerDiv.style.marginTop = `-10000px`; } - } else { - this._markerDiv.style.marginLeft = `-10000px`; - this._markerDiv.style.marginTop = `-10000px`; - } - canvas.style.cursor = "pointer"; - if (this._currentDistanceMeasurement) { - this._currentDistanceMeasurement.wireVisible = this._currentDistanceMeasurementInitState.wireVisible; - this._currentDistanceMeasurement.axisVisible = this._currentDistanceMeasurementInitState.axisVisible && this.distanceMeasurementsPlugin.defaultAxisVisible; - this._currentDistanceMeasurement.xAxisVisible = this._currentDistanceMeasurementInitState.xAxisVisible && this.distanceMeasurementsPlugin.defaultXAxisVisible; - this._currentDistanceMeasurement.yAxisVisible = this._currentDistanceMeasurementInitState.yAxisVisible && this.distanceMeasurementsPlugin.defaultYAxisVisible; - this._currentDistanceMeasurement.zAxisVisible = this._currentDistanceMeasurementInitState.zAxisVisible && this.distanceMeasurementsPlugin.defaultZAxisVisible; - this._currentDistanceMeasurement.targetVisible = this._currentDistanceMeasurementInitState.targetVisible; - this._currentDistanceMeasurement.target.worldPos = pointerWorldPos.slice(); - this._markerDiv.style.marginLeft = `-10000px`; - this._markerDiv.style.marginTop = `-10000px`; - } - }); - + canvas.style.cursor = "pointer"; + if (this._currentDistanceMeasurement) { + this._currentDistanceMeasurement.wireVisible = this._currentDistanceMeasurementInitState.wireVisible; + this._currentDistanceMeasurement.axisVisible = this._currentDistanceMeasurementInitState.axisVisible && this.distanceMeasurementsPlugin.defaultAxisVisible; + this._currentDistanceMeasurement.xAxisVisible = this._currentDistanceMeasurementInitState.xAxisVisible && this.distanceMeasurementsPlugin.defaultXAxisVisible; + this._currentDistanceMeasurement.yAxisVisible = this._currentDistanceMeasurementInitState.yAxisVisible && this.distanceMeasurementsPlugin.defaultYAxisVisible; + this._currentDistanceMeasurement.zAxisVisible = this._currentDistanceMeasurementInitState.zAxisVisible && this.distanceMeasurementsPlugin.defaultZAxisVisible; + this._currentDistanceMeasurement.targetVisible = this._currentDistanceMeasurementInitState.targetVisible; + this._currentDistanceMeasurement.target.worldPos = pointerWorldPos.slice(); + this._markerDiv.style.marginLeft = `-10000px`; + this._markerDiv.style.marginTop = `-10000px`; + } + }); this._onInputMouseDown = input.on("mousedown", (coords) => { pointerDownCanvasX = coords[0]; @@ -277,7 +269,10 @@ export class DistanceMeasurementsMouseControl extends DistanceMeasurementsContr } }); - this._onCameraControlHoverSnapOrSurfaceOff = cameraControl.on("hoverSnapOrSurfaceOff", event => { + this._onCameraControlHoverSnapOrSurfaceOff = cameraControl.on( + this._snapping + ? "hoverSnapOrSurfaceOff" + : "hoverOff", event => { if (this.pointerLens) { this.pointerLens.visible = true; this.pointerLens.centerPos = event.cursorPos || event.canvasPos; From f60398b87bc9eecdf88cd0e85f5f5d95abba4272 Mon Sep 17 00:00:00 2001 From: lindsay Date: Thu, 12 Oct 2023 00:51:52 +0200 Subject: [PATCH 3/3] Ability to enable / disable snapping on measurements controls --- .../angle_createWithMouse_nosnapping.html | 3 +- .../angle_createWithMouse_snapping.html | 16 +- .../distance_createWithMouse_nosnapping.html | 290 ++++++++++++++++++ .../distance_createWithMouse_snapping.html | 4 +- ...istance_createWithMouse_snapping_Lyon.html | 2 + examples/measurement/index.html | 11 +- 6 files changed, 314 insertions(+), 12 deletions(-) create mode 100644 examples/measurement/distance_createWithMouse_nosnapping.html diff --git a/examples/measurement/angle_createWithMouse_nosnapping.html b/examples/measurement/angle_createWithMouse_nosnapping.html index f6851e92fb..4ad66afe63 100644 --- a/examples/measurement/angle_createWithMouse_nosnapping.html +++ b/examples/measurement/angle_createWithMouse_nosnapping.html @@ -73,8 +73,7 @@

    Resources

    pointerLens : new PointerLens(viewer) }) - angleMeasurementsMouseControl.snapToEdge = false; - angleMeasurementsMouseControl.snapToVertex = false; + angleMeasurementsMouseControl.snapping = false; angleMeasurementsMouseControl.activate(); diff --git a/examples/measurement/angle_createWithMouse_snapping.html b/examples/measurement/angle_createWithMouse_snapping.html index 6e42c3249e..27c9478a91 100644 --- a/examples/measurement/angle_createWithMouse_snapping.html +++ b/examples/measurement/angle_createWithMouse_snapping.html @@ -48,7 +48,13 @@

    Resources

    + + + + + + + +
    + +

    DistanceMeasurementPlugin with DistanceMeasurementsMouseControl and PointerLens

    +

    Click on the model to create distance measurements, with no vertex and edge snapping

    +

    In this example, we're loading a BIM model from the file system, then creating distance measurements wherever the + user clicks on the model with the mouse.

    +

    Components Used

    + +

    Resources

    + +
    + + + \ No newline at end of file diff --git a/examples/measurement/distance_createWithMouse_snapping.html b/examples/measurement/distance_createWithMouse_snapping.html index 930143fd8a..63b55024ad 100644 --- a/examples/measurement/distance_createWithMouse_snapping.html +++ b/examples/measurement/distance_createWithMouse_snapping.html @@ -86,7 +86,7 @@

    DistanceMeasurementPlugin with DistanceMeasurementsMouseControl and PointerLens

    -

    Click on the model to create distance measurements, with vertex and edge snapping

    +

    Click on the model to create distance measurements, with no vertex and edge snapping

    In this example, we're loading a BIM model from the file system, then creating distance measurements wherever the user clicks on the model with the mouse.

    Components Used

    @@ -176,6 +176,8 @@

    Resources

    pointerLens : new PointerLens(viewer) }) + distanceMeasurementsMouseControl.snapping = true; + distanceMeasurementsMouseControl.activate(); //------------------------------------------------------------------------------------------------------------------ diff --git a/examples/measurement/distance_createWithMouse_snapping_Lyon.html b/examples/measurement/distance_createWithMouse_snapping_Lyon.html index 7319b09b9d..55df1cf292 100644 --- a/examples/measurement/distance_createWithMouse_snapping_Lyon.html +++ b/examples/measurement/distance_createWithMouse_snapping_Lyon.html @@ -247,6 +247,8 @@

    Resources

    pointerLens : new PointerLens(viewer) }) + distanceMeasurementsMouseControl.snapping = true; + distanceMeasurementsMouseControl.activate(); //---------------------------------------------------------------------------------------------------------------------- diff --git a/examples/measurement/index.html b/examples/measurement/index.html index 7de3774ba5..69fc0f1ea1 100644 --- a/examples/measurement/index.html +++ b/examples/measurement/index.html @@ -259,9 +259,10 @@ "Measuring Distances": [ "# Creating distance measurements with mouse", - ["distance_createWithMouse_snapping", "Click with mouse on model to create distance measurements"], - ["distance_createWithMouse_snapping_Lyon", "Click with mouse on model to create distance measurements"], + ["distance_createWithMouse_snapping", "Click with mouse on model to create distance measurements, snapping enabled"], + ["distance_createWithMouse_snapping_Lyon", "Click with mouse on model to create distance measurements, snapping enabled"], + "# Creating distance measurements with mouse, snapping disabled", ["distance_createWithMouse_nosnapping", "Click with mouse on model to create distance measurements, snapping disabled"], "# Creating distance measurements programmatically", @@ -272,10 +273,10 @@ "Measuring Angles": [ - "# Creating angle measurements with mouse", - ["angle_createWithMouse_snapping", "Click with mouse on model to create angle measurements"], - + "# Creating angle measurements with mouse, snapping enabled", + ["angle_createWithMouse_snapping", "Click with mouse on model to create angle measurements, snapping enabled"], + "# Creating angle measurements with mouse, snapping disabled", ["angle_createWithMouse_nosnapping", "Click with mouse on model to create angle measurements, snapping disabled"], "# Creating angle measurements programmatically",