title: How to zoom in to JPanel without moving the center: math or Swing?
body: <p>I am trying to make a zoomable map with Swing. The map is a JPanel in a JScrollPane. When zoomed in, the map changes size and paint() paints the elements in a different position. This all works great.</p>
  
  <p>However, the ScrollPane didn't change the viewport while increasing the image size, so that zooming in always moved the elements I was looking at out of the screen. I tried to solve this with <code>scrollRectToVisible()</code>, but I don't manage to get the right coordinates for the rectangle, either because I fail at doing the geometry or because I don't understand Swing all that well.</p>
  
  <p>Here is what I have:</p>
  
  <pre><code>public class MapPanel extends JPanel {
      [...]
  
  public void setZoom(double zoom) {
      // get the current viewport rectangle and its center in the scaled coordinate system
      JViewport vp = (JViewport) this.getParent();
      Rectangle rect = vp.getViewRect(); 
      Point middle = getMiddle(rect); 
      Dimension dim = rect.getSize();
  
      // zoom in
      scaler.setZoom(zoom);
      setPreferredSize(scaler.transform(dim));    
      this.revalidate();  
  
  // calculate the full size of the scaled coordinate system  
      Dimension fullDim = scaler.transform(dim); 
      // calculate the non-scaled center of the viewport
      Point nMiddle = new Point((int) ((double) (middle.x)/fullDim.width*dim.width),(int) ((double) (middle.y)/fullDim.height*dim.height));
  
      // this should do the trick, but is always a bit off towards the origin
      scrollRectToVisible(getRectangleAroundPoint(nMiddle)); 
  
      // the below alternative always zooms in perfectly to the center of the map 
      // scrollRectToVisible(getRectangleAroundPoint(new Point(400,300)));
  }
  
  private Rectangle getRectangleAroundPoint(Point p){
      Point newP = scaler.transform(p);
      Dimension d = railMap.getDimension();
      Point corner = new Point(newP.x-d.width/2,newP.y-d.height/2);
      return new Rectangle(corner,d);
  }
  
  private Point getMiddle(Rectangle r){
      return new Point(r.x+r.width/2,r.y+r.height/2);
  }
  }
  </code></pre>
  
  <p>And here's the Scaler class (which does nothing very surprising, I think):</p>
  
  <pre><code>public class Scaler {
      private double zoom = 1;
  
  public void setZoom(double zoom) {
      this.zoom = zoom;
  }
  
  
  public Point transform(Point2D p){
      return new Point((int) (p.getX()*zoom), (int) (p.getY()*zoom));
  }
  
  
  public Dimension transform(Dimension d){
      return new Dimension((int) (d.width*zoom), (int) (d.height*zoom));
  }
  
  }
  </code></pre>
  
  <p>Who can tell me where things are going wrong? It seems to me I did a valid calculation of the current center of the map, and with a fixed zoom point it does work...</p>
  
  <p><strong>Edit:</strong> so the hard thing here is to create the new viewport rectangle based on the old viewport rectangle.</p>