Search code examples
wpfmathtouchpixelsense

How to calculate translate while zooming in WPF/Surface


I'm doing a surface app where I need to scale my scene (zoom) while the user scales with their fingers (i.e pinching)

Currently I have it working ok, but the issue is that I need to zoom in on the center point between the users fingers.

I have the point, but the maths behind the translation is hard to grasp.

When I apply a ScaleTransform to my Canvas scene, it zooms in on the top left of the canvas, i need it to zoom in on the center point of my pinch gesture (which, again, I do have).

How would the maths for the translation work to keep the zoom to appear to zoom in on the center point of the gesture?

Edit:

This is basically what I have:

void Affine2DManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
{
    //apply zoom and translate
    if (e.ScaleDelta != 1)
        ApplyZoom(e);
    else if (e.Delta.Length != 0)
        ApplyTranslation(e);
}

private void ApplyZoom(Affine2DOperationDeltaEventArgs e)
{
    //scale
    var newScale = _zoomTransform.ScaleX * e.ScaleDelta;
    newScale = GetCappedZoomLevel(newScale);

    _zoomTransform.ScaleX = newScale;
    _zoomTransform.ScaleY = newScale;
}

private void ApplyTranslation(Affine2DOperationDeltaEventArgs e)
{
    var xDiff = e.ManipulationOrigin.X - _screenStartPoint.X;
    var yDiff = e.ManipulationOrigin.Y - _screenStartPoint.Y;

    var translateX = xDiff + _startOffset.X;
    var translateY = yDiff + _startOffset.Y;

    //bounds testing to limit translation
    var rect = new Rect(0.0, 0.0, ZoomCanvas.RenderSize.Width, ZoomCanvas.RenderSize.Height);
    Rect bounds = ZoomCanvas.TransformToAncestor(MainViewportCanvas).TransformBounds(rect);

    if (CanTranslateX(translateX, bounds))
        _translateTransform.X = translateX;

    if (CanTranslateY(translateY, bounds))
        _translateTransform.Y = translateY;
}

Pretty basic really, but it works to a point...

_zoomTransform is a ScaleTransform, and _translateTransform is a TranslateTransform

MainViewport is a canvas that contains ZoomCanvas which is the canvas that I apply the transforms to.


Solution

  • Here is my implementation that ended up working just fine

    private void ApplyZoom(Affine2DOperationDeltaEventArgs e)
        {
            //scale
            var newScale = _zoomTransform.ScaleX * e.ScaleDelta;
            newScale = GetCappedZoomLevel(newScale);
            var pinchPoint = e.ManipulationOrigin;
            DoZoom(newScale, _transformGroup.Inverse.Transform(pinchPoint), pinchPoint);
        }
    
        private void DoZoom(double newScale, Point pinchPosition, Point physicalPosition)
        {
            _translateTransform.X = -1 * (pinchPosition.X * newScale - physicalPosition.X);
            _translateTransform.Y = -1 * (pinchPosition.Y * newScale - physicalPosition.Y);
            _zoomTransform.ScaleX = newScale;
            _zoomTransform.ScaleY = newScale;
        }