Я пытаюсь сделать масштабируемую карту с помощью Swing. Карта представляет собой JPanel в JScrollPane. При увеличении карта меняет размер, а paint() рисует элементы в другом положении. Все это прекрасно работает.
Однако ScrollPane не менял область просмотра при увеличении размера изображения, поэтому увеличение всегда перемещало элементы, на которые я смотрел, за пределы экрана. Я пытался решить это с помощью scrollRectToVisible()
, но мне не удается получить правильные координаты для прямоугольника, либо потому, что я не умею делать геометрию, либо потому, что я не очень хорошо понимаю Swing.
Вот что у меня есть:
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);
}
}
А вот класс Scaler (который, я думаю, не делает ничего особенно удивительного):
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));
}
}
Кто может сказать мне, где что-то идет не так? Мне кажется, я сделал правильный расчет текущего центра карты, и с фиксированной точкой масштабирования это работает...
Редактировать: поэтому здесь сложно создать новый прямоугольник области просмотра на основе старого прямоугольника области просмотра.