src/viewer/scene/CameraControl/lib/handlers/TouchPanRotateAndDollyHandler.js
import {math} from "../../../math/math.js";
/**
* @private
*/
class TouchPanRotateAndDollyHandler {
constructor(scene, controllers, configs, states, updates) {
this._scene = scene;
const pickController = controllers.pickController;
const pivotController = controllers.pivotController;
const tapStartPos = math.vec2();
let tapStartTime = -1;
const lastTouches = [];
let numTouches = 0;
const touch0Vec = math.vec2();
const touch1Vec = math.vec2();
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)) {
return;
}
const touches = event.touches;
const changedTouches = event.changedTouches;
states.touchStartTime = Date.now();
if (touches.length === 1 && changedTouches.length === 1) {
tapStartTime = states.touchStartTime;
tapStartPos[0] = touches[0].pageX;
tapStartPos[1] = touches[0].pageY;
if (configs.followPointer) {
pickController.pickCursorPos = tapStartPos;
pickController.schedulePickSurface = true;
pickController.update();
if (!configs.planView ) {
if (pickController.picked && pickController.pickedSurface && pickController.pickResult && pickController.pickResult.worldPos) {
pivotController.setPivotPos(pickController.pickResult.worldPos);
if (!configs.firstPerson && pivotController.startPivot()) {
pivotController.showPivot();
}
} else {
if (!configs.firstPerson && pivotController.startPivot()) { // Continue to use last pivot point
pivotController.showPivot();
}
}
}
}
} else {
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(math.vec2());
}
for (let i = 0, len = touches.length; i < len; ++i) {
lastTouches[i][0] = touches[i].pageX;
lastTouches[i][1] = touches[i].pageY;
}
numTouches = touches.length;
event.stopPropagation();
}, {passive: true});
canvas.addEventListener("touchmove", this._canvasTouchMoveHandler = (event) => {
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;
const canvasWidth = canvasBoundary[2] - canvasBoundary[0];
const canvasHeight = canvasBoundary[3] - canvasBoundary[1];
const touches = event.touches;
if (event.touches.length !== numTouches) {
// Two fingers were pressed, then one of them is removed
// We don't want to rotate in this case (weird behavior)
return;
}
if (numTouches === 1) {
const touch0 = touches[0];
//-----------------------------------------------------------------------------------------------
// Drag rotation
//-----------------------------------------------------------------------------------------------
if (configs.planView) { // No rotating in plan-view mode
math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec);
const xPanDelta = touch0Vec[0];
const yPanDelta = touch0Vec[1];
const camera = scene.camera;
// We use only canvasHeight here so that aspect ratio does not distort speed
if (camera.projection === "perspective") {
const touchPicked = false;
const pickedWorldPos = [0, 0, 0];
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);
updates.panDeltaX += (xPanDelta * targetDistance / canvasHeight) * configs.touchPanRate;
updates.panDeltaY += (yPanDelta * targetDistance / canvasHeight) * configs.touchPanRate;
} else {
updates.panDeltaX += 0.5 * camera.ortho.scale * (xPanDelta / canvasHeight) * configs.touchPanRate;
updates.panDeltaY += 0.5 * camera.ortho.scale * (yPanDelta / canvasHeight) * configs.touchPanRate;
}
} else {
updates.rotateDeltaY -= ((touch0.pageX - lastTouches[0][0]) / canvasWidth) * configs.dragRotationRate / 2; // Full horizontal rotation
updates.rotateDeltaX += ((touch0.pageY - lastTouches[0][1]) / canvasHeight) * (configs.dragRotationRate / 4); // Half vertical rotation
}
} else if (numTouches === 2) {
const touch0 = touches[0];
const touch1 = touches[1];
const lastMiddleTouch = math.geometricMeanVec2(lastTouches[0], lastTouches[1]);
const currentMiddleTouch = math.geometricMeanVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]);
const touchDelta = math.vec2();
math.subVec2(lastMiddleTouch, currentMiddleTouch, touchDelta);
// PANNING
const xPanDelta = touchDelta[0];
const yPanDelta = touchDelta[1];
const camera = scene.camera;
// We use only canvasHeight here so that aspect ratio does not distort speed
if (camera.projection === "perspective") {
const pickedWorldPos = pickController.pickResult ? pickController.pickResult.worldPos : scene.center;
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 -= (xPanDelta * targetDistance / canvasHeight) * configs.touchPanRate;
updates.panDeltaY -= (yPanDelta * targetDistance / canvasHeight) * configs.touchPanRate;
} else {
updates.panDeltaX -= 0.5 * camera.ortho.scale * (xPanDelta / canvasHeight) * configs.touchPanRate;
updates.panDeltaY -= 0.5 * camera.ortho.scale * (yPanDelta / canvasHeight) * configs.touchPanRate;
}
// Dollying
const d1 = math.distVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]);
const d2 = math.distVec2(lastTouches[0], lastTouches[1]);
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});
}
reset() {
}
destroy() {
const canvas = this._scene.canvas.canvas;
canvas.removeEventListener("touchstart", this._canvasTouchStartHandler);
canvas.removeEventListener("touchmove", this._canvasTouchMoveHandler);
this._scene.off(this._onTick);
}
}
export {TouchPanRotateAndDollyHandler};