# Repositioning components based on mouse position

• 06-20-2011, 08:31 PM
robbie.26
Repositioning components based on mouse position
Hello, I am implementing a zoom feature for a JPanel that is populated with many smaller JPanels.
The premise is: when the user spins the mouse scroll wheel the "zoomPercent" is increased/decreased and then all the child components are resized based on their "defaultSize" and the new "zoomPercent" value. This all works fine. Now the problem I've run into is how to get the child components to relocate towards the mouse pointer based on the "zoomPercent" to give the illusion of zooming. I believe the Trig involved requires the use of the AAA Similar Triangle Theorem but I can't quite figure it out. What I have right now \/ is giving me odd results. When I scroll the mouse wheel forward the elements seem to move towards the mouse pointer, but they don't move away from the pointer when I scroll backwards on the scroll wheel. Eventually, after scrolling forwards and backwards a few times the child components will all be perfectly overlapped and jump to the mouse pointer's position when I try scrolling. Any guidance?

Code:

```private int zoomPercent = 100; public void mouseWheelMoved(MouseWheelEvent e) {     int oldZoomPercent = Math.max(1, zoomPercent);     int notches = -e.getWheelRotation();     int increment = 50;     zoomPercent += increment * notches;     zoomPercent = Math.max(0, zoomPercent);     //mouse position     int x = e.getX();     int y = e.getY();     for (Node node: nodes)     {         int newWidth = node.defaultWidth * zoomPercent / 100;         int newHeight = node.defaultHeight * zoomPercent / 100;         node.setSize(newWidth, newHeight);         int nodeX = node.getLocation().x;         int nodeY = node.getLocation().y;         int dx = Math.abs(x - nodeX);         int dy = Math.abs(y - nodeY);         //distance between component and mouse pointer         double hyp = Math.sqrt(dx * dx + dy * dy);         //angle of hypotenuse         double angle = Math.asin(dy / hyp);         //new hypotenuse length         double newHyp = hyp * zoomPercent / oldZoomPercent;         //horizontal component of hypotenuse         double sin = Math.sin(angle);         //vertical component of hypotenuse         double cos = Math.cos(angle);         //change in horizontal component         dx = (int) Math.abs(sin * newHyp - sin * hyp);         //change in vertical component         dy = (int) Math.abs(cos * newHyp - cos * hyp);         int finalX, finalY = 0;         if (nodeX > x) {             finalX = x + dx;         } else {             finalX = x - dx;         }         if (nodeY > y) {             finalY = y + dy;         } else {             finalY = y - dy;         }         node.setLocation(finalX, finalY);     }     validate(); }```
• 06-21-2011, 01:53 AM
pbrockway2
If you want to perform an enlargement whose center is at the mouse position, I'm not sure I would bother with the angle.

* Find nodeX-x, the x-distance to the node. (Don't Math.abs() it because the sign is important)
* Multiply this "delta" by the scale factor
* Add the result back to x to get the new x-position of the node
* Repeat for y
• 06-21-2011, 03:33 AM
robbie.26
Quote:

Originally Posted by pbrockway2
If you want to perform an enlargement whose center is at the mouse position, I'm not sure I would bother with the angle.

* Find nodeX-x, the x-distance to the node. (Don't Math.abs() it because the sign is important)
* Multiply this "delta" by the scale factor
* Add the result back to x to get the new x-position of the node
* Repeat for y

Very much appreciated. This was my first inclination but I made errors in other places and assumed my approach was incorrect.
Well now it works wonderfully! The zoom effect is perfect. Now there's just one little hick-up. If I zoom out so far that the nodes actually disappear
when I zoom back in and they reappear they are all occupying the same exact position and hence, are overlapping.
Code:

```public void mouseWheelMoved(MouseWheelEvent e) {     double oldZoomPercent = Math.max(1, zoomPercent);     int notches = -e.getWheelRotation();     int increment = 50;     zoomPercent += increment * notches;     zoomPercent = Math.max(0, zoomPercent);     //mouse position     int x = e.getX();     int y = e.getY();     for (Node node: nodes)     {         int newWidth = (int) (node.defaultWidth * zoomPercent / 100);         int newHeight = (int) (node.defaultHeight * zoomPercent / 100);         node.setSize(newWidth, newHeight);         int nodeX = node.getLocation().x;         int nodeY = node.getLocation().y;         int dx = (int) ((nodeX - x) * zoomPercent / oldZoomPercent);         int dy = (int) ((nodeY - y) * zoomPercent / oldZoomPercent);         node.setLocation(x + dx, y + dy);     }     validate(); }```
edit: dx seems to get stuck at 0 once the nodes disappear. When they reappear dx is still at 0...
• 06-21-2011, 06:55 AM
DarrylBurke
Looks like the usual integer math 'problem'. I think you'll have to maintain a pair of double variables for the 'last computed location' of the node. I would also declare dx and dy as double, and cast the final incremented value in setLocation.
Code:

``` // x, dx, y, dy are of type double // x and y are retained, possibly as custom fields of 'node' x += dx; y += dy; node.setLocation((int) x, (int) y);```
db