Anthony Nichols
Anthony Nichols

Reputation: 1668

Scaling Zoom factors

I am using the scroll wheel to zoom in and out on an object using the following method:

void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    lastMousePositionOnTarget = Mouse.GetPosition(grid);

    double max = 255;
    double min = .005;

    var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
    var delta = e.Delta > 0 ? Math.Max(e.Delta * deltaScale, .5) : Math.Min(e.Delta * deltaScale, -.5);
    double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);

    scaleTransform.ScaleX = newScale;
    scaleTransform.ScaleY = newScale;

    System.Diagnostics.Debug.WriteLine(newScale);

    e.Handled = true;
}

I want to have it zoom more slowly when it is zoomed in closer to the max, and faster when the number is closer to the minimum. What I currently have sort of works but not very well. How can I fix this to do what I want?

Upvotes: 1

Views: 404

Answers (3)

Anthony Nichols
Anthony Nichols

Reputation: 1668

This is my final answer, I wouldn't have been able to come up with it if not for GEEF & Pawel so a big thanks to them for giving it their best shot.

void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    double max = 255;
    double min = .005;
    double scaler = 4;

    var deltaScale = Math.Min(Math.Max(scaler * scaleTransform.ScaleX / max, min),1) * Math.Sign(e.Delta);
    double newScale = Math.Max(Math.Min(deltaScale + scaleTransform.ScaleX, max), min);

    scaleTransform.ScaleX = newScale;
    scaleTransform.ScaleY = newScale;

    e.Handled = true;
}

I use a ratio of the current scale to the max to get the slope -- and don't allow it to go to fast (max 1) or too slow, min == the min scale. Works OK.

Upvotes: 2

Pawel Troka
Pawel Troka

Reputation: 853

First of all I would not use value e.Delta for calculations (sign is of course needed). This value is not very reliable - in my experience it was close to random noise. Instead use constant value or some counter like how many times in a row zoom in was invoked and how many times in a row zoom out was invoked.

So my first try (with constant value) would look somehow like this.

void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    lastMousePositionOnTarget = Mouse.GetPosition(grid);

    double max = 255;
    double min = .005;

    var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
    var delta = e.Delta > 0 ? Math.Max(1 * deltaScale, .5) : Math.Min(1 * deltaScale, -.5);
    double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);

    scaleTransform.ScaleX = newScale;
    scaleTransform.ScaleY = newScale;

    System.Diagnostics.Debug.WriteLine(newScale);

    e.Handled = true;
}

Version with counters would look something like this.

private double counter=0;
private bool isZoomIn=true;
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    lastMousePositionOnTarget = Mouse.GetPosition(grid);

    if( (e.Delta > 0 && isZoomIn) ||  (e.Delta < 0 && !isZoomIn))
               IncreaseCounter(counter);//is another one in a row
    else
               counter=1;

    isZoomIn = e.Delta > 0;//we set this flag for next time

    double max = 255;
    double min = .005;

    var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
    var delta = e.Delta > 0 ? Math.Max(counter * deltaScale, .5) : Math.Min(counter * deltaScale, -.5);
    double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);

    scaleTransform.ScaleX = newScale;
    scaleTransform.ScaleY = newScale;

    System.Diagnostics.Debug.WriteLine(newScale);

    e.Handled = true;
}

Note that I used pseudofunction IncreaceCounter instead of just incrementaion because you may want something else than just linear increase.

Upvotes: 1

GEEF
GEEF

Reputation: 1185

Assuming I understand this right, you want your deltaScale to be large when zoomed in, but small when zoomed out. Thereby giving you fast zooming when close and slow zooming when far.

If scaletransform.ScaleX gets smaller as you're zoomed in, try something like this:

var deltaScale = Math.Max(K * 1/(scaleTransform.ScaleX), double.Epsilon);

where K is just some const you mess around with until it feels right.

If scaletransform.ScaleX gets larger as you're zoomed in, then try something like like your log, with a larger coefficient than 1:

var deltaScale = Math.Max(5*Math.Log(scaleTransform.ScaleX), double.Epsilon);

Upvotes: 4

Related Questions