I am trying to implement a zoom tool for my game editor. I am using a Color[] to store all the "Colors/tiles" and I want to be able to zoom inside my JPanel to view all the tiles upclose or from far away i.e zoomed in or zoomed out. These are some requirements that I would like to have(Any examples are appreciated and they don't need to include all or any of the requirements. They are there to help you understand what I am aiming for):
- Zooming should be determined by a zoom variable that can be changed dynamicly(prefferably mouseWheel)
- When zoomed(in or out) you should be able to navigate horisontaly and vertically with as little as possible tearing or other graphical glitches(using the JScrollBar s in the example)
- The zooming should be based on where the mouse cursor is and zoom towards that point
- The zooming can use anything inside normal Java which means no external libraries.
This is a very simple version of my editor. I added some test code for zooming but it doesn't work. I am giving you this code so you have something to start from if neccesary. See the PaintComponent method and the mouseWheel listener.
EDITED: Thanks to @trashgod you can now place colors at the right position. See MouseTest class! The tearing is gone.
package stuff;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
public class ZoomPane extends JPanel implements MouseWheelListener, Scrollable {
private int width, height, screenWidth, screenHeight, tileSize;
private Color[] colorMap;
private double scale = 1.0;
// Zooming
AffineTransform at;
class MouseTest extends MouseAdapter {
public void mousePressed(MouseEvent e) {
try {
int newX = (int) at.inverseTransform(e.getPoint(), null).getX();
int newY = (int) at.inverseTransform(e.getPoint(), null).getY();
colorMap[(newX / tileSize) + ((newY / tileSize) * width)] = getRandomColor();
} catch (NoninvertibleTransformException e1) {
e1.printStackTrace();
}
repaint();
}
@Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
try {
System.out.println("Dragging");
int newX = (int) at.inverseTransform(e.getPoint(), null).getX();
int newY = (int) at.inverseTransform(e.getPoint(), null).getY();
colorMap[(newX / tileSize) + ((newY / tileSize) * width)] = getRandomColor();
} catch (NoninvertibleTransformException e1) {
e1.printStackTrace();
}
repaint();
}
}
public ZoomPane(int width, int height, int tileSize) {
super();
this.width = width;
this.height = height;
this.screenWidth = width * tileSize;
this.screenHeight = height * tileSize;
this.tileSize = tileSize;
this.colorMap = new Color[width * height];
addMouseWheelListener(this);
addMouseListener(new MouseTest());
addMouseMotionListener(new MouseTest());
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
final Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Scale
at = null;
at = g2d.getTransform();
// Translate code here?
at.scale(scale, scale);
g2d.setTransform(at);
final Rectangle clip = g2d.getClipBounds();
g2d.setColor(Color.DARK_GRAY);
g2d.fill(clip);
int topX = clip.x / tileSize;
int topY = clip.y / tileSize;
int bottomX = clip.x + clip.width / tileSize + 1 + (int) (tileSize * scale);
int bottomY = clip.y + clip.height / tileSize + 1;
// Draw colors
for (int y = topY; y < bottomY; y++) {
for (int x = topX; x < bottomX; x++) {
Rectangle r = new Rectangle(width, height);
if (r.contains(x, y) && colorMap[x + y * width] != null) {
g2d.setColor(colorMap[x + y * width]);
g2d.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
}
}
}
g2d.dispose();
}
private Color getRandomColor() {
Random rand = new Random();
return new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255));
}
@Override
public Dimension getPreferredSize() {
return new Dimension((int) (screenWidth), (int) (screenHeight));
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
double delta = 0.05 * e.getPreciseWheelRotation();
if (scale + delta > 0)
scale += delta;
revalidate();
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Zoom test");
// Frame settings
frame.setVisible(true);
frame.setPreferredSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane pane = new JScrollPane(new ZoomPane(50, 50, 32));
frame.add(pane);
frame.pack();
}
});
}
@Override
public Dimension getPreferredScrollableViewportSize() {
repaint();
return null;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
repaint();
return 0;
}
@Override
public boolean getScrollableTracksViewportHeight() {
repaint();
return false;
}
@Override
public boolean getScrollableTracksViewportWidth() {
repaint();
return false;
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
repaint();
return 0;
}
}
If you try to run the program you will see these problems:
- When you move the JScrollBars it seams that the mouse position gets an offset when you try to place a color.
- If you zoom/scroll the mouse whell everything shifts up/down and left/right. I would like to zoom at the mouse cursor/point or atleast not shift everything sideways.
This is what works at the moment:
- Placing of colors works correctly now.
- Moving verticaly or horizontaly doesnt tear anymore.
As you see most of the requirements I mentioned at the top can be found in articles here on SO and most of them are really good and well described but I don't know/ can't implement them so that I can solve the problems above.
My goal with this zoom would be something like the example in here with the exception of drawing my "Colors/tiles" instead of the rectangles he draws and that I am using AffineTransform. Otherwise that example is really what I am aiming for.
It doesn't have to use any of the code he uses. I would preffer to use AffineTransform. If any of you knows how to implement what I am looking for in another way I am all ears. I thought that the example might make it easier for you guys to understand what I am aiming for.
I accept any changes or comments about the code or the implementaion and I will respond as quickly as I can and change the post.
Many thanks, Towni0