Skip to content

Commit

Permalink
Merge pull request #415 from Amoki/touch-improvment
Browse files Browse the repository at this point in the history
Improve CameraControl touch dolly/pan
  • Loading branch information
xeolabs authored Aug 15, 2020
2 parents 442159f + ba9d118 commit b7bcd98
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 44 deletions.
7 changes: 0 additions & 7 deletions src/viewer/scene/CameraControl/lib/CameraUpdater.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -116,6 +118,9 @@ class KeyboardPanRotateDollyHandler {
} else if (dollyBackwards) {
updates.dollyDelta += dollyDelta;
}
pickController.pickCursorPos = states.pointerCanvasPos;
pickController.schedulePickSurface = true;
pickController.update();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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));
}
Expand All @@ -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;
Expand Down Expand Up @@ -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});
Expand All @@ -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);
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/viewer/scene/math/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b7bcd98

Please sign in to comment.