From ba9d11890951aa3faaecafd9b8dd46163a31ead7 Mon Sep 17 00:00:00 2001 From: amoki Date: Fri, 14 Aug 2020 18:42:54 +0200 Subject: [PATCH] improve touch zoom/pan --- .../scene/CameraControl/lib/CameraUpdater.js | 7 -- .../handlers/KeyboardPanRotateDollyHandler.js | 5 ++ .../handlers/MousePanRotateDollyHandler.js | 4 + .../handlers/TouchPanRotateAndDollyHandler.js | 85 +++++++++++-------- src/viewer/scene/math/math.js | 18 ++++ 5 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/viewer/scene/CameraControl/lib/CameraUpdater.js b/src/viewer/scene/CameraControl/lib/CameraUpdater.js index 020be16687..5b479d2f45 100644 --- a/src/viewer/scene/CameraControl/lib/CameraUpdater.js +++ b/src/viewer/scene/CameraControl/lib/CameraUpdater.js @@ -73,14 +73,7 @@ class CameraUpdater { countDown = SCALE_DOLLY_EACH_FRAME; if (updates.dollyDelta !== 0) { - if (updates.rotateDeltaY === 0 && updates.rotateDeltaX === 0) { - - pickController.pickCursorPos = states.pointerCanvasPos; - pickController.schedulePickSurface = true; - - pickController.update(); - if (pickController.pickResult && pickController.pickResult.worldPos) { const worldPos = pickController.pickResult.worldPos; pivotController.setPivotPos(worldPos); diff --git a/src/viewer/scene/CameraControl/lib/handlers/KeyboardPanRotateDollyHandler.js b/src/viewer/scene/CameraControl/lib/handlers/KeyboardPanRotateDollyHandler.js index 61350fdac9..65887eac9c 100644 --- a/src/viewer/scene/CameraControl/lib/handlers/KeyboardPanRotateDollyHandler.js +++ b/src/viewer/scene/CameraControl/lib/handlers/KeyboardPanRotateDollyHandler.js @@ -12,6 +12,8 @@ class KeyboardPanRotateDollyHandler { const canvas = scene.canvas.canvas; + const pickController = controllers.pickController; + document.addEventListener("keydown", this._documentKeyDownHandler = (e) => { if (!(configs.active && configs.pointerEnabled) || (!scene.input.keyboardEnabled)) { return; @@ -116,6 +118,9 @@ class KeyboardPanRotateDollyHandler { } else if (dollyBackwards) { updates.dollyDelta += dollyDelta; } + pickController.pickCursorPos = states.pointerCanvasPos; + pickController.schedulePickSurface = true; + pickController.update(); } } diff --git a/src/viewer/scene/CameraControl/lib/handlers/MousePanRotateDollyHandler.js b/src/viewer/scene/CameraControl/lib/handlers/MousePanRotateDollyHandler.js index 5ecea11902..2df9361d10 100644 --- a/src/viewer/scene/CameraControl/lib/handlers/MousePanRotateDollyHandler.js +++ b/src/viewer/scene/CameraControl/lib/handlers/MousePanRotateDollyHandler.js @@ -339,6 +339,10 @@ class MousePanRotateDollyHandler { } const normalizedDelta = delta / Math.abs(delta); updates.dollyDelta += -normalizedDelta * secsElapsed * configs.mouseWheelDollyRate; + pickController.pickCursorPos = states.pointerCanvasPos; + pickController.schedulePickSurface = true; + pickController.update(); + e.preventDefault(); }); } diff --git a/src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js b/src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js index 2c94a5f887..858b76dc6f 100644 --- a/src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js +++ b/src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js @@ -23,6 +23,13 @@ class TouchPanRotateAndDollyHandler { const canvas = this._scene.canvas.canvas; + let waitForTick = false; + + this._onTick = scene.on("tick", () => { + waitForTick = false; + }); + + canvas.addEventListener("touchstart", this._canvasTouchStartHandler = (event) => { if (!(configs.active && configs.pointerEnabled)) { @@ -62,6 +69,16 @@ class TouchPanRotateAndDollyHandler { tapStartTime = -1; } + if (touches.length === 2) { + const touch0 = touches[0]; + const touch1 = touches[1]; + const currentMiddleTouch = math.geometricMeanVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]); + + pickController.pickCursorPos = currentMiddleTouch; + pickController.schedulePickSurface = true; + pickController.update(); + } + while (lastTouches.length < touches.length) { lastTouches.push(new Float32Array(2)); } @@ -81,6 +98,11 @@ class TouchPanRotateAndDollyHandler { if (!(configs.active && configs.pointerEnabled)) { return; } + if (waitForTick) { + // Limit changes detection to one per frame + return; + } + waitForTick = true; // Scaling drag-rotate to canvas boundary const canvasBoundary = scene.canvas.boundary; @@ -140,62 +162,52 @@ class TouchPanRotateAndDollyHandler { const touch0 = touches[0]; const touch1 = touches[1]; - math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec); - math.subVec2([touch1.pageX, touch1.pageY], lastTouches[1], touch1Vec); - - const panning = math.dotVec2(touch0Vec, touch1Vec) > 0; - - if (panning) { - - math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec); + const lastMiddleTouch = math.geometricMeanVec2(lastTouches[0], lastTouches[1]); + const currentMiddleTouch = math.geometricMeanVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]); - const xPanDelta = touch0Vec[0]; - const yPanDelta = touch0Vec[1]; - - const camera = scene.camera; + const touchDelta = new Float32Array(2); - // We use only canvasHeight here so that aspect ratio does not distort speed + math.subVec2(lastMiddleTouch, currentMiddleTouch, touchDelta); - if (camera.projection === "perspective") { - //---------------------------- - // TODO: Pick on first touch - //---------------------------- + // PANNING + const xPanDelta = touchDelta[0]; + const yPanDelta = touchDelta[1]; - const touchPicked = false; - const pickedWorldPos = [0, 0, 0]; + const camera = scene.camera; - const depth = Math.abs(touchPicked ? math.lenVec3(math.subVec3(pickedWorldPos, scene.camera.eye, [])) : scene.camera.eyeLookDist); - const targetDistance = depth * Math.tan((camera.perspective.fov / 2) * Math.PI / 180.0); + // We use only canvasHeight here so that aspect ratio does not distort speed - updates.panDeltaX += (xPanDelta * targetDistance / canvasHeight) * configs.touchPanRate; - updates.panDeltaY += (yPanDelta * targetDistance / canvasHeight) * configs.touchPanRate; + if (camera.projection === "perspective") { + const pickedWorldPos = pickController.pickResult ? pickController.pickResult.worldPos : scene.center; - } else { + const depth = Math.abs(math.lenVec3(math.subVec3(pickedWorldPos, scene.camera.eye, []))); + const targetDistance = depth * Math.tan((camera.perspective.fov / 2) * Math.PI / 180.0); - updates.panDeltaX += 0.5 * camera.ortho.scale * (xPanDelta / canvasHeight) * configs.touchPanRate; - updates.panDeltaY += 0.5 * camera.ortho.scale * (yPanDelta / canvasHeight) * configs.touchPanRate; - } + updates.panDeltaX -= (xPanDelta * targetDistance / canvasHeight) * configs.touchPanRate; + updates.panDeltaY -= (yPanDelta * targetDistance / canvasHeight) * configs.touchPanRate; } else { - // Dollying + updates.panDeltaX -= 0.5 * camera.ortho.scale * (xPanDelta / canvasHeight) * configs.touchPanRate; + updates.panDeltaY -= 0.5 * camera.ortho.scale * (yPanDelta / canvasHeight) * configs.touchPanRate; + } - const d1 = math.distVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]); - const d2 = math.distVec2(lastTouches[0], lastTouches[1]); + // Dollying + states.skipNextPick = true; - updates.dollyDelta = (d2 - d1) * configs.touchDollyRate; + const d1 = math.distVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]); + const d2 = math.distVec2(lastTouches[0], lastTouches[1]); - states.pointerCanvasPos[0] = ((touch1.pageX + touch0.pageX) / 2); - states.pointerCanvasPos[1] = ((touch1.pageY + touch0.pageY) / 2); - } + updates.dollyDelta = (d2 - d1) * configs.touchDollyRate; + + states.pointerCanvasPos = currentMiddleTouch; } for (let i = 0; i < numTouches; ++i) { lastTouches[i][0] = touches[i].pageX; lastTouches[i][1] = touches[i].pageY; } - event.stopPropagation(); }, {passive: true}); @@ -205,11 +217,10 @@ class TouchPanRotateAndDollyHandler { } destroy() { - const canvas = this._scene.canvas.canvas; - canvas.removeEventListener("touchstart", this._canvasTouchStartHandler); canvas.removeEventListener("touchmove", this._canvasTouchMoveHandler); + this._scene.off(this._onTick); } } diff --git a/src/viewer/scene/math/math.js b/src/viewer/scene/math/math.js index 2d53919f9f..63c4844f8c 100644 --- a/src/viewer/scene/math/math.js +++ b/src/viewer/scene/math/math.js @@ -370,6 +370,24 @@ const math = { return dest; }, + /** + * Get the geometric mean of the vectors. + * @method geometricMeanVec2 + * @static + * @param {...Array(Number)} vectors Vec2 to mean + * @return {Array(Number)} The geometric mean vec2 + */ + geometricMeanVec2(...vectors) { + const geometricMean = new Float32Array(vectors[0]); + for (let i = 1; i < vectors.length; i++) { + geometricMean[0] += vectors[i][0]; + geometricMean[1] += vectors[i][1]; + } + geometricMean[0] /= vectors.length; + geometricMean[1] /= vectors.length; + return geometricMean; + }, + /** * Subtracts a scalar value from each element of a four-element vector. * @method subVec4Scalar