tags:

views:

60

answers:

1

I've got a Canvas and I put some stuff on it, then I scaled it. But, when you run the scales, they don't originate from the point they should, like, if you scale it from one origin a bit, then scale from another origin, the second origin acts as if it's been offset somewhat in a really strange fashion instead of behaving as intended (and as the first origin behaves). I've been using ths code to perform the scaling:

        foreach (UIElement element in canvas1.Children)
        {
            Point p = e.MouseDevice.GetPosition(element);
            Matrix m = element.RenderTransform.Value;
            if (e.Delta > 0)
                m.ScaleAt(1.1, 1.1, p.X, p.Y);
            else
                m.ScaleAt(1 / 1.1, 1 / 1.1, p.X, p.Y);
            element.RenderTransform = new MatrixTransform(m);
        }

What on earth could be going on?

I realized that my original question text wasn't as clear as it could be. I scale from the mouse origin, so you move your mouse to the left of my example pair of text objects, scroll the wheel and they zoom in or out and move across the screen in a perfectly good fashion. But if you then move your mouse to the right far on the other side of the objects, and try to scroll, then they act as if the mouse in the middle of them - that is, they zoom in and out without moving across the screen at all.

+1  A: 

When you create a transformation using ScaleAt, you are creating a translation as well. For example, if the mouse pointer is 100 pixels left of the element, then you are scaling it by 1.1 and also moving it 10 pixels to the right. The next time your code runs, you are scaling that translation as well, so are moving it 1 more pixel to the right in addition to the new translation.

One solution is to separate out the translation from the previous matrix before applying the scale and then add it back at the end:

foreach (UIElement element in canvas1.Children)
{
    Point p = e.MouseDevice.GetPosition(element);
    Matrix m = element.RenderTransform.Value;
    Matrix m2 = m;
    m2.Translate(-m.OffsetX, -m.OffsetY);
    if (e.Delta > 0)
        m2.ScaleAt(1.1, 1.1, p.X, p.Y);
    else
        m2.ScaleAt(1 / 1.1, 1 / 1.1, p.X, p.Y);
    m2.Translate(m.OffsetX, m.OffsetY);
    element.RenderTransform = new MatrixTransform(m2);
}

A similar option is to always remove the scaling from the RenderTransform and encode it in Canvas.Left and Canvas.Top:

foreach (UIElement element in canvas1.Children)
{
    Point p = e.MouseDevice.GetPosition(element);
    Matrix m = element.RenderTransform.Value;
    if (e.Delta > 0)
        m.ScaleAt(1.1, 1.1, p.X, p.Y);
    else
        m.ScaleAt(1 / 1.1, 1 / 1.1, p.X, p.Y);
    Canvas.SetLeft(element, Canvas.GetLeft(element) + m.OffsetX);
    Canvas.SetTop(element, Canvas.GetTop(element) + m.OffsetY);
    m.Translate(-m.OffsetX, -m.OffsetY);
    element.RenderTransform = new MatrixTransform(m);
}

Update: Actually, I think I missed the larger, related effect. Because you are applying the new scale after the old one, the translation by p.X and p.Y is getting scaled when it should not be. When the scale is large, this gets to be a large error (while the error I mentioned above is always a 10% error).

Try using ScaleAtPrepend to apply the new transformation first:

foreach (UIElement element in canvas1.Children)
{
    Point p = e.MouseDevice.GetPosition(element);
    Matrix m = element.RenderTransform.Value;
    if (e.Delta > 0)
        m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
    else
        m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);
    Canvas.SetLeft(element, Canvas.GetLeft(element) + m.OffsetX);
    Canvas.SetTop(element, Canvas.GetTop(element) + m.OffsetY);
    m.Translate(-m.OffsetX, -m.OffsetY);
    element.RenderTransform = new MatrixTransform(m);
}
Quartermeister
As you move in, the two elements squidge together and don't separate if you zoom back out. Conceptually, I get your point and think that your code should work, but in my testbed it produces even stranger results. Both exhibited the same behaviour.
DeadMG
Thanks, that's the winrar! Can't observe any unintended behaviour now.
DeadMG