Zoom to mouse point
#1
How difficult would it be to implement Zoom to mouse point as the default gesture? I notcied that mouse wheel just does a scale zoom but does not translate to keep the mouse pointer as the zoom center. This forces me to zoom and then pan to keep the place I want to zoom in in the same place after zooming.

This would be great to have it as the default zoom behavior. Otherwise, how difficult would it be to implement as a library consumer?

Thanks.
#2
It is quite easy to implement this behavior by yourself.

You need to disable MouseWheel handling on ZoomPanel and do it yourself from the code with ZoomAndTranslateToCenter method on the ZoomPanel.

The following is a sample of the implementation.

XAML:
Code:
<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:zoomPanel="clr-namespace:Ab2d.Controls;assembly=Ab2d.Controls.ZoomPanel"
    Title="Window1" Height="300" Width="300"
    MouseWheel="Window_MouseWheel">
    <Grid>
        <zoomPanel:ZoomPanel Name="ZoomPanel1" IsMouseWheelZoomEnabled="False" ZoomMode="Move">
            <TextBlock Text="12345"/>
        </zoomPanel:ZoomPanel>
    </Grid>
</Window>

Code:
Code:
private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            Point mousePosition;
            double zoomFactor;

            if (e.Delta > 0)
                zoomFactor = 1.3;
            else
                zoomFactor = 1 / 1.3;

            mousePosition = e.GetPosition(ZoomPanel1);

            ZoomPanel1.ZoomAndTranslateToCenter(zoomFactor, mousePosition);

            e.Handled = true;
        }

The code simply translates the ZoomPanel to the current mouse position and than applies the zoom factor based on the mouse wheel movement.

Andrej Benedik
#3
I tried out this code you guys posted but it does not seem to work. My assumption is that Zoom to Point keeps the mouse point in the same location so you can keep zooming into the same point without having to translate.
This above example seems to be translating the mouse point to a different location (I guess to the center) which is not the expected behavior.

So in this example, if I start zooming into the top of number 1, I would expect that point to remain the same (applying the scale zoom and necessary translate transformations). Is that possible?
#4
This way quite tricky :cool:

Sorry for waiting so long - I working on new version of Reader3ds.

So here is the solution:

The XAML part is the same as before:
Code:
<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:zoomPanel="clr-namespace:Ab2d.Controls;assembly=Ab2d.Controls.ZoomPanel"
    Title="Window1" Height="300" Width="300"
    MouseWheel="Window_MouseWheel">
    <Grid>
        <zoomPanel:ZoomPanel Name="ZoomPanel1" IsMouseWheelZoomEnabled="False" ZoomMode="Move">
            <TextBlock Text="12345"/>
        </zoomPanel:ZoomPanel>
    </Grid>
</Window>

The tricky part is in the code:
Code:
private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
{
    Point mousePosition;
    double zoomFactor;

    if (e.Delta > 0)
        zoomFactor = 1.3;
    else
        zoomFactor = 1 / 1.3;

    mousePosition = e.GetPosition(ZoomPanel1);

    ZoomPanel1.Viewbox = GetNewViewbox(zoomFactor, mousePosition);

    e.Handled = true;
}

private Rect GetNewViewbox(double zoomFactor, Point mousePosition)
{
    UIElement content;
    double zoomPanelAspectRatio, contentAspectRatio;
    double aspect;
    Rect newViewbox;
    Point relativeMousePosition;
    Point relativeViewboxPosition;
    Point relativeViewboxPosition2;
    Rect actualViewbox1, actualViewbox2;

    content = ZoomPanel1.Content as UIElement;
    
    if (content == null)
        return ZoomPanel1.Viewbox;

    zoomPanelAspectRatio = ZoomPanel1.ActualWidth / ZoomPanel1.ActualHeight;
    contentAspectRatio = content.DesiredSize.Width / content.DesiredSize.Height;

    // Apsect ration between ZoomPanel and its content
    aspect = contentAspectRatio / zoomPanelAspectRatio;


    relativeMousePosition = new Point(mousePosition.X / ZoomPanel1.ActualWidth,
                                      mousePosition.Y / ZoomPanel1.ActualHeight);


    // Calculate actual viewbox
    actualViewbox1 = AdjustRectForAspectRatio(ZoomPanel1.Viewbox, aspect);

    // mouse position on the original actual viewbox
    relativeViewboxPosition = new Point(actualViewbox1.X + actualViewbox1.Width * relativeMousePosition.X,
                                        actualViewbox1.Y + actualViewbox1.Height * relativeMousePosition.Y);





    // Do the normal zoom to the current center point for the zoomFactor
    double viewboxDx, viewboxDy;

    // viewboxDx and viewboxDy are the actual viewbox changes
    viewboxDx = ZoomPanel1.Viewbox.Width - (ZoomPanel1.Viewbox.Width / zoomFactor);
    viewboxDy = ZoomPanel1.Viewbox.Height - (ZoomPanel1.Viewbox.Height / zoomFactor);

    newViewbox = new Rect(ZoomPanel1.Viewbox.X + viewboxDx / 2,
                          ZoomPanel1.Viewbox.Y + viewboxDy / 2,
                          ZoomPanel1.Viewbox.Width - viewboxDx,
                          ZoomPanel1.Viewbox.Height - viewboxDy);



    // Now get where the mouse position would be in the newViewbox
    actualViewbox2 = AdjustRectForAspectRatio(newViewbox, aspect);


    relativeViewboxPosition2 = new Point(actualViewbox2.X + actualViewbox2.Width * relativeMousePosition.X,
                                         actualViewbox2.Y + actualViewbox2.Height * relativeMousePosition.Y);

    // Adjust the newViewbox so the objects under the mouse position stay on the same place
    newViewbox.X -= relativeViewboxPosition2.X - relativeViewboxPosition.X;
    newViewbox.Y -= relativeViewboxPosition2.Y - relativeViewboxPosition.Y;


    return newViewbox;
}

private Rect AdjustRectForAspectRatio(Rect originalRect, double aspect)
{
    Rect adjustedRect;

    adjustedRect = originalRect;

    if (aspect > 1) // content wider than ZoomPanel
    {
        //actualViewbox2.Y = actualViewbox1.Y * aspect - (actualViewbox1.Height * (aspect - 1) / 2);
        adjustedRect.Y *= aspect;
        adjustedRect.Height *= aspect;
    }
    else
    {
        adjustedRect.X /= aspect;
        adjustedRect.Width /= aspect;
    }

    return adjustedRect;
}

This should work.

The code will be probably included into next version of ZoomPanel (cannot say when it will be published).

Thank you for your recommendation.
  


Forum Jump:


Users browsing this thread:
1 Guest(s)