Image anchoring

Started by
6 comments, last by taby 1 month, 1 week ago

Right now I am struggling to get image anchoring working in my world editor.

In the app initialization, I set the anchor to equal:

	image_anchor.x =  (float(window_w) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f);
	image_anchor.y = (float(window_h) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f);

And this results in a centred image:

When I zoom in or out, I want it to be centred about the centre of the image, but I'm not sure how to perform the necessary arithmetic quite yet. When I zoom, I do this:

if (last_mousewheel < 0)
	zoom_factor *= 0.5;
else if (last_mousewheel > 0)
	zoom_factor *= 2.0;

if (last_mousewheel != 0)
{
	image_anchor.x =  float(window_w) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f;
	image_anchor.y =  float(window_h) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f;
}

But it does not work as desired. Any basic ideas that I'm missing out on?

P.S. The whole code is at https://github.com/sjhalayka/tiles/tree/c77ae21ab9a1417926ef7e69d259310e7d096af0

The whole code relies on the ImGui, SDL 2, and OpenCV libraries.

Advertisement

No time working through your entire code, but what you need to do is adjust eigther the anchor, or the camera, by a fraction of the point that you want to center the zoom around. Here is my working code for that:

static constexpr float zoomStep = 2.0f;

float newZoom = m_zoom;
if (distance < 0)
{
	newZoom /= zoomStep;
	newZoom = std::max(newZoom, 0.0625f);
}
else
{
	newZoom *= zoomStep;
	newZoom = std::min(newZoom, 32.0f);
}

if (m_zoom == newZoom)
	return;

const auto vCursor = m_pView->CursorRelativeTo().To<float>() * m_zoom;
const auto zoomFactor = m_zoom > newZoom ? -0.5f : 1;
const auto vAddOffset = vCursor * zoomFactor;

m_zoom = newZoom;


m_camera.SetPosition(m_camera.GetPosition() - vAddOffset);

CursorRelativeTo gives the mouse-coordinates in relation to the game-view (so 0,0 in the upper left corner), as my zoom is always around the mouse-position. I then shift the camera-position by a negative fraction of that (not sure about the reasoning for the exact calculation of vAddOffset, but I can assure you it does work). In your case, you'd use the center-coordinate of the image instead.

taby said:
But it does not work as desired.

What do you desire, and how does it behave differently from that?

if (last_mousewheel < 0)
	zoom_factor *= 0.5;
else if (last_mousewheel > 0)
	zoom_factor *= 2.0;

if (last_mousewheel != 0)
{
	image_anchor.x =  float(window_w) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f;
	image_anchor.y =  float(window_h) / 2.0 - 36.0f * float(tiles_per_dimension) / 2.0f;
}

Why do you need to recalculate anchor, although the zoom_factor affected by mousewheel does not affect it?

Maybe zoom should affect the magic number 36?

Well, I'd like it to zoom in and out of whatever's in the centre of the screen like in Paint.net or Photoshop or Gimp (and not the bottom-left corner of the image like currently in my code).

Thank you guys. I still haven't got it working yet, but I do appreciate your help!

I had some success, thanks to @warp9 .

The near-perfect solution is:

glm::vec3 view_focus(0.5, 0.5, 0);

image_anchor.x = window_w / 2.0 - 36.0f * float(tiles_per_dimension) * zoom_factor * view_focus.x;
image_anchor.y = window_h / 2.0 - 36.0f * float(tiles_per_dimension) * zoom_factor * view_focus.y;

The only thing left to do is calculate view_focus, which somehow involves detecting which pixel location is in the centre of the screen.

Any further ideas? Thanks again for your time and interest.

taby said:
Well, I'd like it to zoom in and out of whatever's in the centre of the screen like in Paint.net or Photoshop or Gimp

That's not how you zoom in such programs.
Usually there is an option (e.g. kb shortcut) to scale about the mouse cursor.
This way you can use scaling also to translate the view, by zooming out, move cursor to point of interest, zoom in.
This is so much better than using those fiddly, tiny scrollbars.

The math is similar to the frequent problem of creating a transformation matrix to rotate around a given point.
Here some example:

Mat4 xf;
xf.Rotation()->FromQuat(rot); // sets the upper 3x3 from a rotation quaternion
xf[3] = cor - xf.Rotation()->Rotate(cor); // the hard part: subtract the rotated center of rotation from the center of rotation

I always forget this, and then it takes me lots of trial and error to figure it out again.
I guess it's the same for you, just you have scale instead rotation.
Maybe it helps, but you will figure it out…

I tried the following, but it doesn't quite work:

float win_x = (float)window_w / 2.0f;
float win_y = (float)window_h / 2.0f;

win_x -= image_anchor.x;
win_y -= image_anchor.y;

win_x /= 36.0f * float(tiles_per_dimension);
win_y /= 36.0f * float(tiles_per_dimension);

view_focus.x = win_x;
view_focus.y = win_y;

This topic is closed to new replies.

Advertisement