From d1f88783141b9a53c9eb2f68ab622f14a3f682ec Mon Sep 17 00:00:00 2001 From: Michal Dybizbanski Date: Tue, 21 Jan 2025 15:06:00 +0100 Subject: [PATCH 1/4] XCD-237 Fix AngleMeasurementTouchControl canvas offset (and adjust DistanceMeasurementsTouchControl accordingly) --- .../AngleMeasurementsTouchControl.js | 29 ++++++------ .../DistanceMeasurementsTouchControl.js | 44 +++++++------------ 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js index a7ed75da7a..96a1a8a9a7 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js @@ -1,7 +1,7 @@ import {math} from "../../viewer/scene/math/math.js"; import {PointerCircle} from "../../extras/PointerCircle/PointerCircle.js"; import {AngleMeasurementsControl} from "./AngleMeasurementsControl.js"; - +import {transformToNode} from "../lib/ui/index.js"; const WAITING_FOR_ORIGIN_TOUCH_START = 0; const WAITING_FOR_ORIGIN_QUICK_TOUCH_END = 1; @@ -155,6 +155,13 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { this._touchState = WAITING_FOR_ORIGIN_TOUCH_START; } + const copyCanvasPos = (event, dst) => { + dst[0] = event.clientX; + dst[1] = event.clientY; + transformToNode(canvas.ownerDocument.documentElement, canvas, dst); + return dst; + }; + canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { const currentNumTouches = event.touches.length; @@ -168,11 +175,8 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { } const touch = event.touches[0]; - const touchX = touch.clientX; - const touchY = touch.clientY; - - touchStartCanvasPos.set([touchX, touchY]); - touchMoveCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchStartCanvasPos); + touchMoveCanvasPos.set(touchStartCanvasPos); switch (this._touchState) { @@ -482,14 +486,11 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { } const touch = event.touches[0]; - const touchX = touch.clientX; - const touchY = touch.clientY; - if (touch.identifier !== touchId) { return; } - touchMoveCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchMoveCanvasPos); let snapPickResult; let pickResult; @@ -662,9 +663,6 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { } const touch = event.changedTouches[0]; - const touchX = touch.clientX; - const touchY = touch.clientY; - if (touch.identifier !== touchId) { return; } @@ -674,7 +672,10 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { longTouchTimeout = null; } - touchEndCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchEndCanvasPos); + + const touchX = touchEndCanvasPos[0]; + const touchY = touchEndCanvasPos[1]; switch (this._touchState) { diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js index 599aaac91c..7700092e0c 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js @@ -1,7 +1,7 @@ import {math} from "../../viewer/scene/math/math.js"; import {PointerCircle} from "../../extras/PointerCircle/PointerCircle.js"; import {DistanceMeasurementsControl} from "./DistanceMeasurementsControl.js"; - +import {transformToNode} from "../lib/ui/index.js"; const WAITING_FOR_ORIGIN_TOUCH_START = 0; const WAITING_FOR_ORIGIN_QUICK_TOUCH_END = 1; @@ -159,6 +159,13 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro this._touchState = WAITING_FOR_ORIGIN_TOUCH_START; } + const copyCanvasPos = (event, dst) => { + dst[0] = event.clientX; + dst[1] = event.clientY; + transformToNode(canvas.ownerDocument.documentElement, canvas, dst); + return dst; + }; + canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { const currentNumTouches = event.touches.length; @@ -172,16 +179,8 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro } const touch = event.touches[0]; - - // Get the canvas's bounding rectangle - const rect = canvas.getBoundingClientRect(); - - // Calculate the touch position relative to the canvas - const touchX = touch.clientX - rect.left; - const touchY = touch.clientY - rect.top; - - touchStartCanvasPos.set([touchX, touchY]); - touchMoveCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchStartCanvasPos); + touchMoveCanvasPos.set(touchStartCanvasPos); switch (this._touchState) { @@ -386,19 +385,11 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro } const touch = event.touches[0]; - - // Get the canvas's bounding rectangle - const rect = canvas.getBoundingClientRect(); - - // Calculate the touch position relative to the canvas - const touchX = touch.clientX - rect.left; - const touchY = touch.clientY - rect.top; - if (touch.identifier !== touchId) { return; } - touchMoveCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchMoveCanvasPos); let snapPickResult; let pickResult; @@ -563,14 +554,6 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro } const touch = event.changedTouches[0]; - - // Get the canvas's bounding rectangle - const rect = canvas.getBoundingClientRect(); - - // Calculate the touch position relative to the canvas - const touchX = touch.clientX - rect.left; - const touchY = touch.clientY - rect.top; - if (touch.identifier !== touchId) { return; } @@ -580,7 +563,10 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro longTouchTimeout = null; } - touchEndCanvasPos.set([touchX, touchY]); + copyCanvasPos(touch, touchEndCanvasPos); + + const touchX = touchEndCanvasPos[0]; + const touchY = touchEndCanvasPos[1]; switch (this._touchState) { From 865d5ffe10c441c2363d13c56a97e23501974910 Mon Sep 17 00:00:00 2001 From: Michal Dybizbanski Date: Tue, 21 Jan 2025 17:12:26 +0100 Subject: [PATCH 2/4] Fix Angle and DistanceMeasurementTouchControl's pointerCircle coordinates --- .../AngleMeasurementsTouchControl.js | 20 +++++++++++++------ .../DistanceMeasurementsTouchControl.js | 16 +++++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js index 96a1a8a9a7..99bcca23c5 100644 --- a/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js +++ b/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js @@ -17,6 +17,8 @@ const WAITING_FOR_TARGET_LONG_TOUCH_END = 8; const TOUCH_CANCELING = 7; +const tmpVec2 = math.vec2(); + /** * Creates {@link AngleMeasurement}s from touch input. * @@ -162,6 +164,12 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { return dst; }; + const toBodyPos = (pos, dst) => { + dst.set(pos); + transformToNode(canvas, canvas.ownerDocument.documentElement, dst); + return dst; + }; + canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { const currentNumTouches = event.touches.length; @@ -192,7 +200,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { }); if (snapPickResult && snapPickResult.snapped) { pointerWorldPos.set(snapPickResult.worldPos); - this.pointerCircle.start(snapPickResult.snappedCanvasPos); + this.pointerCircle.start(toBodyPos(snapPickResult.snappedCanvasPos, tmpVec2)); } else { const pickResult = scene.pick({ canvasPos: touchMoveCanvasPos, @@ -200,7 +208,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { }) if (pickResult && pickResult.worldPos) { pointerWorldPos.set(pickResult.worldPos); - this.pointerCircle.start(pickResult.canvasPos); + this.pointerCircle.start(toBodyPos(pickResult.canvasPos, tmpVec2)); } else { return; } @@ -307,7 +315,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { this.pointerLens.cursorPos = snapPickResult.snappedCanvasPos; this.pointerLens.snapped = true; } - this.pointerCircle.start(snapPickResult.snappedCanvasPos); + this.pointerCircle.start(toBodyPos(snapPickResult.snappedCanvasPos, tmpVec2)); pointerWorldPos.set(snapPickResult.worldPos); this._currentAngleMeasurement.corner.worldPos = snapPickResult.worldPos; this._currentAngleMeasurement.corner.entity = snapPickResult.entity; @@ -329,7 +337,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { this.pointerLens.cursorPos = pickResult.canvasPos; this.pointerLens.snapped = false; } - this.pointerCircle.start(pickResult.canvasPos); + this.pointerCircle.start(toBodyPos(pickResult.canvasPos, tmpVec2)); pointerWorldPos.set(pickResult.worldPos); this._currentAngleMeasurement.corner.worldPos = pickResult.worldPos; this._currentAngleMeasurement.corner.entity = pickResult.entity; @@ -400,7 +408,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { this.pointerLens.cursorPos = snapPickResult.snappedCanvasPos; this.pointerLens.snapped = true; } - this.pointerCircle.start(snapPickResult.snappedCanvasPos); + this.pointerCircle.start(toBodyPos(snapPickResult.snappedCanvasPos, tmpVec2)); pointerWorldPos.set(snapPickResult.worldPos); this._currentAngleMeasurement.target.worldPos = snapPickResult.worldPos; this._currentAngleMeasurement.target.entity = snapPickResult.entity; @@ -422,7 +430,7 @@ export class AngleMeasurementsTouchControl extends AngleMeasurementsControl { this.pointerLens.cursorPos = pickResult.canvasPos; this.pointerLens.snapped = false; } - this.pointerCircle.start(pickResult.canvasPos); + this.pointerCircle.start(toBodyPos(pickResult.canvasPos, tmpVec2)); pointerWorldPos.set(pickResult.worldPos); this._currentAngleMeasurement.target.worldPos = pickResult.worldPos; this._currentAngleMeasurement.target.entity = pickResult.entity; diff --git a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js index 7700092e0c..72b7b4637e 100644 --- a/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js +++ b/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js @@ -13,6 +13,8 @@ const WAITING_FOR_TARGET_LONG_TOUCH_END = 5; const TOUCH_CANCELING = 7; +const tmpVec2 = math.vec2(); + /** * Creates {@link DistanceMeasurement}s from touch input. * @@ -166,6 +168,12 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro return dst; }; + const toBodyPos = (pos, dst) => { + dst.set(pos); + transformToNode(canvas, canvas.ownerDocument.documentElement, dst); + return dst; + }; + canvas.addEventListener("touchstart", this._onCanvasTouchStart = (event) => { const currentNumTouches = event.touches.length; @@ -196,7 +204,7 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro }); if (snapPickResult && snapPickResult.snapped) { pointerWorldPos.set(snapPickResult.worldPos); - this.pointerCircle.start(snapPickResult.snappedCanvasPos); + this.pointerCircle.start(toBodyPos(snapPickResult.snappedCanvasPos, tmpVec2)); } else { const pickResult = scene.pick({ canvasPos: touchMoveCanvasPos, @@ -204,7 +212,7 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro }) if (pickResult && pickResult.worldPos) { pointerWorldPos.set(pickResult.worldPos); - this.pointerCircle.start(pickResult.canvasPos); + this.pointerCircle.start(toBodyPos(pickResult.canvasPos, tmpVec2)); } else { return; } @@ -308,7 +316,7 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro this.pointerLens.cursorPos = snapPickResult.snappedCanvasPos; this.pointerLens.snapped = true; } - this.pointerCircle.start(snapPickResult.snappedCanvasPos); + this.pointerCircle.start(toBodyPos(snapPickResult.snappedCanvasPos, tmpVec2)); pointerWorldPos.set(snapPickResult.worldPos); this._currentDistanceMeasurement.target.worldPos = snapPickResult.worldPos; this._currentDistanceMeasurement.target.entity = snapPickResult.entity; @@ -326,7 +334,7 @@ export class DistanceMeasurementsTouchControl extends DistanceMeasurementsContro this.pointerLens.cursorPos = pickResult.canvasPos; this.pointerLens.snapped = false; } - this.pointerCircle.start(pickResult.canvasPos); + this.pointerCircle.start(toBodyPos(pickResult.canvasPos, tmpVec2)); pointerWorldPos.set(pickResult.worldPos); this._currentDistanceMeasurement.target.worldPos = pickResult.worldPos; this._currentDistanceMeasurement.target.entity = pickResult.entity; From bc7e5244bc88f2744ddad0011074d5ca3d81aa60 Mon Sep 17 00:00:00 2001 From: Michal Dybizbanski Date: Tue, 21 Jan 2025 17:42:54 +0100 Subject: [PATCH 3/4] Abstract PointerLens::_updateActiveVisible --- src/extras/PointerLens/PointerLens.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/extras/PointerLens/PointerLens.js b/src/extras/PointerLens/PointerLens.js index b291587e07..d2d27f7b27 100644 --- a/src/extras/PointerLens/PointerLens.js +++ b/src/extras/PointerLens/PointerLens.js @@ -257,19 +257,23 @@ export class PointerLens { get snapped() { return this._snapped; } - + + _updateActiveVisible() { + this._lensContainer.style.visibility = (this._active && this._visible) ? "visible" : "hidden"; + if (!this._active || !this._visible) { + this._lensCursorDiv.style.marginLeft = `-100px`; + this._lensCursorDiv.style.marginTop = `-100px`; + } + this.update(); + } + /** * Sets if this PointerLens is active. * @param active */ set active(active) { this._active = active; - this._lensContainer.style.visibility = (active && this._visible) ? "visible" : "hidden"; - if (!active || !this._visible ) { - this._lensCursorDiv.style.marginLeft = `-100px`; - this._lensCursorDiv.style.marginTop = `-100px`; - } - this.update(); + this._updateActiveVisible(); } /** @@ -288,12 +292,7 @@ export class PointerLens { */ set visible(visible) { this._visible = visible; - this._lensContainer.style.visibility = (visible && this._active) ? "visible" : "hidden"; - if (!visible || !this._active) { - this._lensCursorDiv.style.marginLeft = `-100px`; - this._lensCursorDiv.style.marginTop = `-100px`; - } - this.update(); + this._updateActiveVisible(); } /** From 39a547cd05303a82abf03720e331cf39f4ed222a Mon Sep 17 00:00:00 2001 From: Michal Dybizbanski Date: Tue, 21 Jan 2025 18:55:05 +0100 Subject: [PATCH 4/4] Change _lensCursorDiv to be parented by _lensContainer - fixes incorrect cursor offsets when canvas not in page origin --- src/extras/PointerLens/PointerLens.js | 56 +++++++++------------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/src/extras/PointerLens/PointerLens.js b/src/extras/PointerLens/PointerLens.js index d2d27f7b27..1c71e6f6dd 100644 --- a/src/extras/PointerLens/PointerLens.js +++ b/src/extras/PointerLens/PointerLens.js @@ -53,13 +53,10 @@ export class PointerLens { this.scene = this.viewer.scene; this._lensCursorDiv = document.createElement('div'); - this.viewer.scene.canvas.canvas.parentNode.insertBefore(this._lensCursorDiv, this.viewer.scene.canvas.canvas); - this._lensCursorDiv.style.background = "pink"; - this._lensCursorDiv.style.border = "2px solid red"; - this._lensCursorDiv.style.borderRadius = "20px"; - this._lensCursorDiv.style.width = "10px"; - this._lensCursorDiv.style.height = "10px"; - this._lensCursorDiv.style.margin = "-200px -200px"; + this._lensParams = { canvasSize: 300, cursorBorder: 2, cursorSize: 10 }; + this._lensCursorDiv.style.borderRadius = "50%"; + this._lensCursorDiv.style.width = this._lensParams.cursorSize + "px"; + this._lensCursorDiv.style.height = this._lensParams.cursorSize + "px"; this._lensCursorDiv.style.zIndex = "100000"; this._lensCursorDiv.style.position = "absolute"; this._lensCursorDiv.style.pointerEvents = "none"; @@ -72,8 +69,8 @@ export class PointerLens { this._lensContainer.style.background = "white"; // this._lensContainer.style.opacity = "0"; this._lensContainer.style.borderRadius = "50%"; - this._lensContainer.style.width = "300px"; - this._lensContainer.style.height = "300px"; + this._lensContainer.style.width = this._lensParams.canvasSize + "px"; + this._lensContainer.style.height = this._lensParams.canvasSize + "px"; this._lensContainer.style.zIndex = "15000"; this._lensContainer.style.position = "absolute"; @@ -85,13 +82,14 @@ export class PointerLens { // this._lensCanvas.style.background = "darkblue"; this._lensCanvas.style.borderRadius = "50%"; - this._lensCanvas.style.width = "300px"; - this._lensCanvas.style.height = "300px"; + this._lensCanvas.style.width = this._lensParams.canvasSize + "px"; + this._lensCanvas.style.height = this._lensParams.canvasSize + "px"; this._lensCanvas.style.zIndex = "15000"; this._lensCanvas.style.pointerEvents = "none"; document.body.appendChild(this._lensContainer); this._lensContainer.appendChild(this._lensCanvas); + this._lensContainer.appendChild(this._lensCursorDiv); this._lensCanvasContext = this._lensCanvas.getContext('2d'); this._canvasElement = this.viewer.scene.canvas.canvas; @@ -109,7 +107,7 @@ export class PointerLens { this._active = (cfg.active !== false); this._visible = false; - this._snapped = false; + this.snapped = false; this._onViewerRendering = this.viewer.scene.on("rendering", () => { if (this._active && this._visible) { @@ -156,21 +154,13 @@ export class PointerLens { this._lensCanvas.height // destination height ); - const centerLensCanvas = [ - (lensRect.left + lensRect.right) / 2 - canvasRect.left, - (lensRect.top + lensRect.bottom) / 2 - canvasRect.top - ]; + const middle = this._lensParams.canvasSize / 2 - this._lensParams.cursorSize / 2 - this._lensParams.cursorBorder; - if (this._snappedCanvasPos) { - const deltaX = this._snappedCanvasPos[0] - this._canvasPos[0]; - const deltaY = this._snappedCanvasPos[1] - this._canvasPos[1]; + const deltaX = this._snappedCanvasPos ? (this._snappedCanvasPos[0] - this._canvasPos[0]) : 0; + const deltaY = this._snappedCanvasPos ? (this._snappedCanvasPos[1] - this._canvasPos[1]) : 0; - this._lensCursorDiv.style.marginLeft = `${centerLensCanvas[0] + deltaX * this._zoomLevel - 10}px`; - this._lensCursorDiv.style.marginTop = `${centerLensCanvas[1] + deltaY * this._zoomLevel - 10}px`; - } else { - this._lensCursorDiv.style.marginLeft = `${centerLensCanvas[0] - 10}px`; - this._lensCursorDiv.style.marginTop = `${centerLensCanvas[1] - 10}px`; - } + this._lensCursorDiv.style.left = `${middle + deltaX * this._zoomLevel}px`; + this._lensCursorDiv.style.top = `${middle + deltaY * this._zoomLevel}px`; } @@ -238,14 +228,10 @@ export class PointerLens { * @private */ set snapped(snapped) { - this._snapped = snapped; - if (snapped) { - this._lensCursorDiv.style.background = "greenyellow"; - this._lensCursorDiv.style.border = "2px solid green"; - } else { - this._lensCursorDiv.style.background = "pink"; - this._lensCursorDiv.style.border = "2px solid red"; - } + this._snapped = snapped; + const [ bg, border ] = snapped ? [ "greenyellow", "green" ] : [ "pink", "red" ]; + this._lensCursorDiv.style.background = bg; + this._lensCursorDiv.style.border = this._lensParams.cursorBorder + "px solid " + border; } /** @@ -260,10 +246,6 @@ export class PointerLens { _updateActiveVisible() { this._lensContainer.style.visibility = (this._active && this._visible) ? "visible" : "hidden"; - if (!this._active || !this._visible) { - this._lensCursorDiv.style.marginLeft = `-100px`; - this._lensCursorDiv.style.marginTop = `-100px`; - } this.update(); }