SFML community forums
Help => Graphics => Topic started by: wilbefast 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
-
You could define a linear interpolation function like this:
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:
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
}
}
-
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 :(
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;
}
-
Is this all of the code in which you implemented the zooming? Because your missing quite a lot.
-
Yeah - that was my original code. Here's a changed version, but clearly I'm not implementing this correctly:
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;
}
:?
-
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.
#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;
}
-
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: