2012-10-04 9 views
5

Ich versuche eine zoombare Karte mit Swing zu machen. Die Map ist ein JPanel in einem JScrollPane. Wenn gezoomt wird, ändert die Karte die Größe und paint() zeichnet die Elemente an einer anderen Position. Das alles funktioniert großartig.Wie zoomen Sie in JPanel hinein, ohne das Zentrum zu verschieben: Mathe oder Swing?

Das ScrollPane änderte jedoch nicht das Ansichtsfenster, während die Bildgröße erhöht wurde, so dass das Einzoomen die Elemente, die ich betrachtete, immer aus dem Bildschirm herausbewegte. Ich habe versucht, dies mit scrollRectToVisible() zu lösen, aber ich bekomme nicht die richtigen Koordinaten für das Rechteck, entweder weil ich die Geometrie nicht schaffe oder weil ich Swing nicht so gut verstehe.

Hier ist, was ich habe:

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); 
} 
} 

Und hier ist die Scaler-Klasse (die nichts anderes tut sehr überraschend, glaube ich):

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)); 
} 

} 

Wer kann mir sagen, wo die Dinge schief gehen? Es scheint mir, dass ich eine gültige Berechnung der aktuellen Mitte der Karte hat, und mit einem Zoom-Punkt fixiert es funktioniert ...

Edit: so die harte Sache hier ist die neue Ansichtsfenster Rechteck basierend zu erstellen auf dem alten Ansichtsfensterrechteck.

+0

railMap? Du würdest nicht an etwas wie JMRI arbeiten, oder? ;-) – geowar

Antwort

5

Ich habe gerade diese wirklich schnelles Beispiel, das ist im Grunde das Scroll-Scheibe Zentrum rund um die Mitte des zugeführten Bild zu halten versucht

public class TestZooming { 

    public static void main(String[] args) { 
     new TestZooming(); 
    } 

    public TestZooming() { 

     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 

       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException classNotFoundException) { 
       } catch (InstantiationException instantiationException) { 
       } catch (IllegalAccessException illegalAccessException) { 
       } catch (UnsupportedLookAndFeelException unsupportedLookAndFeelException) { 
       } 

       JFrame frame = new JFrame(); 
       frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setSize(400, 400); 
       frame.setLocationRelativeTo(null); 
       frame.setLayout(new BorderLayout()); 
       final ZoomPane pane = new ZoomPane(); 
       frame.add(new JScrollPane(pane)); 
       frame.setVisible(true); 

       SwingUtilities.invokeLater(new Runnable() { 

        @Override 
        public void run() { 
         pane.centerInViewport(); 
        } 

       }); 

      } 
     }); 

    } 

    protected class ZoomPane extends JPanel { 

     private Image background; 
     private Image scaled; 
     private float zoom = 1f; 

     private Dimension scaledSize; 
     private JViewport con; 

     public ZoomPane() { 

      try { 
       background = ImageIO.read(new File("...")); 
       scaled = background; 
       scaledSize = new Dimension(background.getWidth(this), background.getHeight(this)); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 

      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "plus"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.SHIFT_DOWN_MASK), "plus"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "minus"); 

      am.put("plus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        setZoom(getZoom() + 0.1f); 
       } 
      }); 
      am.put("minus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        setZoom(getZoom() - 0.1f); 
       } 
      }); 

      setFocusable(true); 
      requestFocusInWindow(); 

     } 

     @Override 
     public void addNotify() { 

      super.addNotify(); 

     } 

     public float getZoom() { 
      return zoom; 
     } 

     public void setZoom(float value) { 
      if (zoom != value) { 
       zoom = value; 

       if (zoom < 0) { 
        zoom = 0f; 
       } 

       int width = (int) Math.floor(background.getWidth(this) * zoom); 
       int height = (int) Math.floor(background.getHeight(this) * zoom); 
       scaled = background.getScaledInstance(width, height, Image.SCALE_SMOOTH); 
       scaledSize = new Dimension(width, height); 

       if (getParent() instanceof JViewport) { 

        int centerX = width/2; 
        int centerY = height/2; 

        JViewport parent = (JViewport) getParent(); 
        Rectangle viewRect = parent.getViewRect(); 
        viewRect.x = centerX - (viewRect.width/2); 
        viewRect.y = centerY - (viewRect.height/2); 
        scrollRectToVisible(viewRect); 
       } 

       invalidate(); 
       repaint(); 

      } 
     } 

     @Override 
     public Dimension getPreferredSize() { 

      return scaledSize; 

     } 

     @Override 
     protected void paintComponent(Graphics g) { 

      super.paintComponent(g); 

      if (scaled != null) { 

       g.drawImage(scaled, 0, 0, this); 

      } 

     } 

     protected void centerInViewport() { 

      Container container = getParent(); 
      if (container instanceof JViewport) { 

       JViewport port = (JViewport) container; 
       Rectangle viewRect = port.getViewRect(); 

       int width = getWidth(); 
       int height = getHeight(); 

       viewRect.x = (width - viewRect.width)/2; 
       viewRect.y = (height - viewRect.height)/2; 

       scrollRectToVisible(viewRect); 

      } 

     } 
    } 
} 

Wie, warum Ihr nicht funktioniert, kann ich nicht sagen, Ich kann das Beispiel nicht ausführen, aber vielleicht gibt dir das zumindest ein paar Ideen ...

+0

Danke. Wenn ich jedoch richtig verstehe, zoome immer in die Mitte des Bildes. Das hat für mich funktioniert. Was nicht berechnet wurde, berechnete das Zentrum des aktuellen Blickpunkts und zoomte in dieses Zentrum hinein. Denkst du, du könntest dein Beispiel daran anpassen? Ich denke, es sollte wie Ihre centerInViewport() -Funktion aussehen, außer dass 'viewRect.x' und' .y' etwas anders berechnet werden sollten. Ich denke auch, ich weiß wie (siehe mein Beispiel), außer dass sich herausstellt, dass es nicht funktioniert ... – roelandvanbeek

+0

Nevermind, ich habe es mit einigen Hinweisen aus deinem Beispiel gelöst. Werde meine Lösung nach Ablauf der acht Stunden posten. – roelandvanbeek

1

Gelöst wurde es. Yay. Immer noch nicht sicher, wo es wirklich schief gelaufen ist, aber das ursprüngliche Rechteck zu bewegen (danke @MadProgrammer), anstatt einen neuen zu erstellen und im Scaler richtig zu runden, mag den Trick gemacht haben.

private Point getViewportCenter() { 
    JViewport vp = (JViewport) this.getParent(); 
    Point p = vp.getViewPosition(); 
    return new Point(p.x+vp.getWidth()/2,p.y+vp.getHeight()/2); 
} 

private void setViewportCenter(Point p) { 
    JViewport vp = (JViewport) this.getParent(); 
    Rectangle viewRect = vp.getViewRect(); 

    viewRect.x = p.x - viewRect.width/2; 
    viewRect.y = p.y - viewRect.height/2; 

    scrollRectToVisible(viewRect); 
} 

public void setZoom(double zoom) { 
    // determine unscaled center and dimensions 
    Point oCenter = scaler.inverseTransform(getViewportCenter()); 
    Dimension dim = railMap.getDimension(); 

    // zoom 
    scaler.setZoom(zoom); 

    // fix size and viewport 
    setPreferredSize(scaler.transform(dim)); 
    setViewportCenter(scaler.transform(oCenter)); // should be a transformed point 

    // finish 
    invalidate(); 
    repaint(); 
}