Pan / Zoom Algorithm

Hi,

Im creating something that requires I pan and zoom some graphical data.
So below is the code I have come up with which almost works but Im really struggling to get my head around this whole thing.

So the idea of these methods is that I pop in some absolute coordinates and it gives me the position in which to draw the coordinate in the display (a JPanel).
Im sure you can guess what xScale and yScale are for and xOrigin and yOrigin are used to offset the drawing in relation to panning.

public double getRelativeX(double x){
        double relativeX = (x* xScale) + xOrigin +(getWidth()/2);
        return relativeX;
}

public double getRelativeY(double y){
        double relativeY = (y * -yScale) - yOrigin+(getHeight()/2);
        return relativeY;
}

At the moment it does the job but when I zoom it zooms about the absolute point 0,0 when I want it to do it about the centre of the screen, any ideas how I need to change it?

Also this is my zooming/panning code:

public void mousePressed(MouseEvent e) {
                if(SwingUtilities.isMiddleMouseButton(e)){
                    // capture starting point
                    lastOffsetX = e.getX();
                    lastOffsetY = e.getY();
                }
            }

            public void mouseDragged(MouseEvent e) {
                if(SwingUtilities.isMiddleMouseButton(e)){
                    // new x and y are defined by current mouse location subtracted
                    // by previously processed mouse location
                    int newX = e.getX() - lastOffsetX;
                    int newY = e.getY() - lastOffsetY;

                    // increment last offset to last processed by drag event.
                    lastOffsetX += newX;
                    lastOffsetY += newY;

                    // update the canvas locations
                    xOrigin += newX;
                    yOrigin -= newY;

                    // schedule a repaint.
                    repaint();
                }
            }

[quote]public void mouseWheelMoved(MouseWheelEvent e) {
if(e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {

                    xScale -= (mouseSensitivity * e.getWheelRotation());
                    yScale -= (mouseSensitivity * e.getWheelRotation());

                    xScale = Math.max(0.001, xScale);
                    yScale = Math.max(0.001, yScale);
                    repaint();
            }
        }

[/quote]
One last important note, you may notice the X and Y methods are slightly different, thats because I want the coordinate system to start from the bottom left and not the top left corner and increase as you move upwards not downwards.

Thanks for any help,

Ken.

your position on the image has to be relative to the scaling as well?

  1. you have image/graph whatever, you can move it around the screen
  2. you want to zoom into center of screen

get position of screen center in image coords
scale
adjust the offset accordingly, with scale included

What do you mean?

Yes well If Im panning and then scroll with the mouse wheel I want the graphics to get larger from a base point of the middle of the screen, otherwise if its based on 0,0 then everything goes flying off the screen and I have to pan to find it again…

use percentages instead of pixels maybe.
ex:

Starting position
Center of screen is at Image position X 50%, Y 50%, scale is 100%

Moved and Scaled position
Center of screen is at Image position X 30%, Y 70%, scale is 150%

And use those to calc the draw offsets from the original width and height

this system will have to take into account if the image is completely off screen center

Hmm Im not quite sure what you mean. Im not drawing one pannable/scalable image although maybe I should be drawing to an image first.
Im drawing individual lines and points.

You dont necessarily have to draw it to an image, but that could be a solution to make it clearer - but if you want to manipulate points in realtime definitely not.

I’m unclear as to whether you need to move only left and right on the track?

step back and separate clearly the system:

  1. the original x and y coords are the raw data
  2. the relative x and y coords are scaled coords
  3. the final plotpoints are offset according to left/right scrolling?
    If so,
    Keep track of where you are on the scrolling as a % of the whole possibile scroll area, and the pixels it’s taken to get there

eg you have a 100 pixel wide graph, it fits on the screen.
center pos is 50%
zoom 100x its 10000 pixels wide
selected position is 5000
and finally fit that to screen center depending on screenwidth

I’m having a bad day explaining clearly, i try once more in c++ :stuck_out_tongue:


pointAmount = dataSize - valuesStartIndex;
	//printf("point amount %d\n",pointAmount);
	m_plotpoints = new float* [pointAmount];
	for(unsigned int i=0;i<pointAmount;i++)
		*(m_plotpoints+i)=new float[2];

	//get positions of samples in graph, in screen coords
	float valuerange = (float)(valueMax - valueMin);

	// if only one value, center the point
	float ratioX = (float)(width / 2);
	float ratioY = (float)(height / 2);

	if(pointAmount > 1){
		ratioY = (float)(height / valuerange);
		ratioX = (float)(width / (pointAmount-1));
	}

	for (unsigned int i=0; i<pointAmount; i++){
		// place the plot points x,y in screen space
		//printf("setting point at %f,%f\n",(i * ratioX),((m_data[i+valuesStartIndex]-valueMin) * ratioY));

		m_plotpoints[i][0]  = i * ratioX;
		m_plotpoints[i][1]  = (m_data[i+valuesStartIndex]-valueMin) * ratioY;
	}

this one has no panning but the idea is to first plot the coordinates into
a new copy according to scale, then work with those

What you say makes sense but Im just trying to think how to implement it.

Here is an example of what I mean:
I have a point at 1000,1000, of course this would be off screen if the screen is 500,500 lets say.
So as we pan towards the point the x & y origin value will increase (or decrease depending on hows it done) and for example f we pan 500 pixels to the right then the equation for relative x is:
x = 1000 + (- 500) = 500
so the point would be drawn at 500,1000.

Of course then you have the scale so currently I am doing this:
(1000 * 1) + (-500) + (500/2) = 750

I add half screen width because I want it based on the centre of the screen and not the bottom conrer.

You can test it in excel but right now my brain is fried as I’ve been trying to work this out for ages…

(1000 * 1) + (-500) + (500/2) = 750

where the multiplication is scaling,
scale 1, use the multiplier for the offset also:
(1000 * 1) + ( -500 * 1 ) + (500/2) = 750

scale 1.5
(1000 * 1.5) + ( -500 * 1.5 ) + (500/2) = 1000

scale .5
(1000 * .5) + ( -500 * .5 ) + (500/2) = 500

maybe on the right track?

Omg man I think you have it! How the heck have I missed that…

I was considering if I could do it about the mouse X & Y also but I dont think it would be as easy as the centre of the screen.

Thanks for the help Karmington its been a big help!

This may be a slightly out of date thread, but I created a Ken Burns style Pan & Zoom algorithm for the animated menu system used in Awesome Soccer.

Idoes a couple of clever things to determine image resize performance levels achieved and adjusts the “quality” of the resize accordingly.

The code requires some other utility libraries of mine (otherwise I would have posted the code here), but if someone genuinely needs it, PM me and I can look to extract it… :slight_smile:

Cheers,

Pete.