Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Zoom to mouse (as in Google maps)?  (Read 8252 times)

0 Members and 1 Guest are viewing this topic.

wilbefast

  • Newbie
  • *
  • Posts: 31
    • View Profile
    • http://wilbefast.com
Zoom to mouse (as in Google maps)?
« on: April 12, 2011, 06:35:05 pm »
Hi all,

I'm trying to implement a zoom towards the mouse similar to the system used for Google Maps or Supreme Commander. I can't seem to get it to work though: has anyone done anything similar recently?

Thanks,


William

Rubenknex

  • Newbie
  • *
  • Posts: 21
    • View Profile
Zoom to mouse (as in Google maps)?
« Reply #1 on: April 16, 2011, 11:19:29 am »
You could define a linear interpolation function like this:

Code: [Select]

float lerp(float value, float start, float end)
{
    return start + (end - start) * value;
}


Then set the center of the View with sf::View::SetCenter(mouseX, mouseY) and zoom into that position with the result of the lerp with sf::View::Zoom().

Some pseudo code:
Code: [Select]

targetzoom = 0
currentzoom = 0
previouszoom = 0
zooming = false

void update()
{
    if (move mousewheel)
    {
        zooming = true

        targetzoom = 0.5
        currentzoom = 0
    }

    if (zooming)
    {
        currentzoom = lerp(0.01, currentzoom, targetzoom)

        view.Zoom(currentzoom - previouszoom) // Zoom is relative.

        previouszoom = currentzoom

        if (abs(targetzoom - currentzoom) < 0.1) // Lerp will never reach the target.
            zooming = false
    }
}


wilbefast

  • Newbie
  • *
  • Posts: 31
    • View Profile
    • http://wilbefast.com
Zoom to mouse (as in Google maps)?
« Reply #2 on: April 16, 2011, 11:53:32 am »
Okay, I'll give it a try (and edit in what happens). By the way, this is the code I was using - it didn't quite work :(

Code: [Select]
bool Game :: treatEvents(sf::RenderWindow& window)
{
    static sf::Event event;
    while (window.GetEvent(event))
    {
        switch(event.Type)
        {
            //exit events

            case sf::Event::MouseWheelMoved:
                window.GetDefaultView().Zoom(1+ZOOM_SPEED*event.MouseWheel.Delta);
                window.GetDefaultView().SetCenter(mouse_position);
                mouse_position = window.ConvertCoords(mouse_position.x,mouse_position.y, &window.GetDefaultView());
                break;

            case sf::Event::MouseMoved:
                mouse_position = window.ConvertCoords(event.MouseMove.X,event.MouseMove.Y,&window.GetDefaultView());
                break;

            default:
                break;
        }
    }
    //no exit event detected: continue running
    return true;
}

Rubenknex

  • Newbie
  • *
  • Posts: 21
    • View Profile
Zoom to mouse (as in Google maps)?
« Reply #3 on: April 16, 2011, 12:04:09 pm »
Is this all of the code in which you implemented the zooming? Because your missing quite a lot.

wilbefast

  • Newbie
  • *
  • Posts: 31
    • View Profile
    • http://wilbefast.com
Zoom to mouse (as in Google maps)?
« Reply #4 on: April 16, 2011, 12:39:29 pm »
Yeah - that was my original code. Here's a changed version, but clearly I'm not implementing this correctly:

Code: [Select]
float lerp(float value, float start, float end)
{
    return start + (end - start) * value;
}

bool Game :: treatEvents(RenderWindow& window)
{
    float targetzoom = 0;
    float currentzoom = 0;
    float previouszoom = 0;
    bool zooming = false;

    static Event event;
    while (window.GetEvent(event))
    {
        switch(event.Type)
        {
            //exit events

            case Event::MouseWheelMoved:
                zooming = true;
                targetzoom = 0.5;
                currentzoom = 0;
                break;

            case Event::MouseMoved:
                break;

            default:
                break;
        }
    }

    if (zooming)
    {
        currentzoom = lerp(0.01, currentzoom, targetzoom);
        window.GetDefaultView().Zoom(currentzoom - previouszoom);
        previouszoom = currentzoom;
        if (abs(targetzoom - currentzoom) < 0.1) // Lerp will never reach the target.
            zooming = false;
    }


    //no exit event detected: continue running
    return true;
}
:?

Rubenknex

  • Newbie
  • *
  • Posts: 21
    • View Profile
Zoom to mouse (as in Google maps)?
« Reply #5 on: April 16, 2011, 01:00:21 pm »
I worked on a little demo so you can try to understand what is going on, change a few of the values and see what happens.

I hope this is the effect you wanted :P.

Code: [Select]

#include <SFML/Graphics.hpp>
#include <iostream>
#include <math.h>

float lerp(float value, float start, float end)
{
    return start + (end - start) * value;
}

int main()
{
    sf::RenderWindow App(sf::VideoMode(800, 600), "SFML window");

    const sf::Input& input = App.GetInput();

    // Make a few shapes for testing.
    sf::Shape rect1 = sf::Shape::Rectangle(10, 10, 60, 60, sf::Color(255, 0, 0));
    sf::Shape circle1 = sf::Shape::Circle(100, 100, 40, sf::Color(0, 255, 0));
    sf::Shape line1 = sf::Shape::Line(200, 200, 300, 300, 2, sf::Color(0, 0, 255));

    sf::Vector2f currentCenter;
    sf::Vector2f targetCenter;
    float targetZoom = 0.0f;
    float currentZoom = 0.0f;
    float previousZoom = 0.0f;
    bool zooming = false;

    float lerpFactor = 0.01f; // The speed of the zooming.

    while (App.IsOpened())
    {
        sf::Event Event;
        while (App.GetEvent(Event))
        {
            if (Event.Type == sf::Event::Closed)
                App.Close();
            else if(Event.Type == sf::Event::MouseWheelMoved)
            {
                zooming = true;

                // Zooming in or out.
                if (Event.MouseWheel.Delta == 1)
                    targetZoom = 1.3f;
                else
                    targetZoom = 0.7f;

                currentZoom = 1.0f;
                previousZoom = currentZoom;

                // Store the current center of the view and our target (the position of the mouse).
                currentCenter = App.GetDefaultView().GetCenter();
                targetCenter = App.ConvertCoords(input.GetMouseX(), input.GetMouseY(), &App.GetDefaultView());
            }
        }

        if (zooming)
        {
            // Interpolate between the current zoom and the target zoom for a smooth effect.
            currentZoom = lerp(lerpFactor, currentZoom, targetZoom);
            App.GetDefaultView().Zoom(1.0f + (currentZoom - previousZoom));

            // Interpolate the between the current center of the view and the target.
            currentCenter.x = lerp(lerpFactor, currentCenter.x, targetCenter.x);
            currentCenter.y = lerp(lerpFactor, currentCenter.y, targetCenter.y);
            App.GetDefaultView().SetCenter(currentCenter);

            // Store the previous zoom because we will need the difference between the previous and current zoom.
            // This is because sf::View::Zoom zooms relative to the current zoom.
            previousZoom = currentZoom;

            // If the difference between the current and previous zoom is less then 0.01, stop zooming.
            // Linear interpolation will never reach the target value so we stop here.
            if (fabs(targetZoom - currentZoom) < 0.01f)
                zooming = false;
        }

        App.Clear();

        App.Draw(rect1);
        App.Draw(circle1);
        App.Draw(line1);

        App.Display();
    }

    return 0;
}


wilbefast

  • Newbie
  • *
  • Posts: 31
    • View Profile
    • http://wilbefast.com
Zoom to mouse (as in Google maps)?
« Reply #6 on: April 16, 2011, 01:13:17 pm »
Ah! I completely misunderstood what the code did: so it basically makes the zoom all nice and smooth :D Certainly an effect I'd like to have now that I've seen, but not actually the one I was originally trying to implement  :?

Maybe I should have been more specific in the description. How to explain...

1. go to http://maps.google.com/.
2. place the cursor on a city.
3. zoom in.

Google Maps zooms directly towards or away from whatever your mouse is hovering over. However if I place the mouse on the square in your example, I have to keep move the mouse to zoom in on it accurately.

Still, I'm definitely going to use this smooth zoom of yours  :wink: