0

I have a 2D texture image that is zoomed in/out via 2-finger touch and pinch.
Currently the image is not panned, i.e. the center of the image is always in the middle.
I want the center point between the twoFinger touch to stay between the 2 fingers.

If I pinch exactly in the center of the image, then the center image point will stay between the fingers - good!
But if I pinch near the corner of the image the point will move away, relative to the 2 fingers, because of the zoom.
So I need to apply some pan in addition to the zoom, to make the point appear in the same place.

I basically need to transftorm the camera position such that, for every zoom, the same world coordinate is projected to the same screen coord.

Figures 1-3 illustrate the problem.
Figure1 is the original image.
Currently when I zoom in the camera stays in the same position, so the object between the 2 fingers (the cat's eye on the right) is drifted from being between the 2 fingers, as the image zooms (Figure 2).
I want to pan the camera such that the object between the 2 fingers stays between the 2 fingers even after the zooming the image (Figure 3).

I used the code below, but the object still drifts as the image zooms in/out.

How should I calculate the amount of shift that needs to be applied to the camera?
Thanks

Code to calculate the amount of shift that needs to be applied to the camera

handleTwoFingerTouchMove( p3_inScreenCoord) {
    // normalize the screen coord to be in the range of [-1, 1]
    // (See method1 in https://stackoverflow.com/questions/13542175/three-js-ray-intersect-fails-by-adding-div/)
    let point2dNormalizedX = ( ( p3_inScreenCoord.x - windowOffset.left ) / windowWidth) * 2 - 1;
    let point2dNormalizedY = -( ( p3_inScreenCoord.y - windowOffset.top ) / windowHeight) * 2 + 1;

    // calc p3 before zoom (in world coords)
    let p3_beforeZoom = new THREE_Vector3( point2dNormalizedX, point2dNormalizedY, -1 ).unproject( this.camera );
    
    // Apply zoom        
    this.dollyInOut( this.getZoomScale(), true );

    // calc p3 after zoom (in world coords)
    let p3_afterZoom = new THREE_Vector3( point2dNormalizedX, point2dNormalizedY, -1 ).unproject( this.camera );

    // calc the required shift in camera position
    let deltaX = p3_afterZoom.x - p3_beforeZoom.x;
    let deltaZ = p3_afterZoom.z - p3_beforeZoom.z;

    // shift in camera position
    this.pan( deltaX, deltaZ );
};

enter image description here

Avner Moshkovitz
  • 1,138
  • 1
  • 18
  • 35

1 Answers1

1

I was able to solve my problem. Here is my solution in the hope that it helps others.

When first applying a 2-finger touch (i.e. on touchstart event), the code computes:

  • the world-coordinate of the object pointed at (e.g. the cat's eye on the right), when starting two-finger touch centerPoint3d_inWorldCoord0 (Vector3)
  • the screen-coordinate anchor for zooming via two-finger touch centerPoint2dBetweenTwoFingerTouch_inScreenCoordNormalized (Vector2)

While zooming in/out via two-finger pinch in/out (on touchmove event) in the event listener function, immediately after the applying the zoom, I call the following code:

// Calculate centerPoint3d_inWorldCoord2, which is the new world-coordinate, for 
// centerPoint2dBetweenTwoFingerTouch_inScreenCoordNormalized given the new zoom setting.
let centerPoint3d_inWorldCoord2 = new THREE_Vector3( centerPoint2dBetweenTwoFingerTouch_inScreenCoordNormalized.x,
                                                     centerPoint2dBetweenTwoFingerTouch_inScreenCoordNormalized.y,
                                                     -1 ).unproject( camera );

// compute the shift in world-coordinate between the new vs the original world-coordinate
let delta_inWorldCoords = new THREE_Vector2(centerPoint3d_inWorldCoord2.x - centerPoint3d_inWorldCoord0.x,
                                            centerPoint3d_inWorldCoord2.z - centerPoint3d_inWorldCoord0.z);

// pan the camera, to compensate the shift 
pan_usingWorldCoords( delta_inWorldCoords );

The function pan_usingWorldCoords shifts the camera in the x axis (panLeft), and then in the y axis (panUp)

pan_usingWorldCoords( delta_inWorldCoord ) {
    panLeft( delta_inWorldCoord.x );
    panUp( delta_inWorldCoord.y );
};

The functions panLeft, panUp are similar to the functions that are used in three.js-r114/examples/jsm/controls/OrbitControls.js

Initially the object pointed at was drifting from being between the 2 fingers, as the image zoomed in/out.
I added this.camera.updateProjectionMatrix() at the end of each function.
This updates the projection matrix at the end of panLeft, before using it again in panUp.

With the code above and after updating the projection matrix at the end of panLeft, panUp, the object pointed at (e.g. the eye on the right), when starting two-finger touch, is kept between the 2 fingers, while zooming via two-finger pinch.

Avner Moshkovitz
  • 1,138
  • 1
  • 18
  • 35