1

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

Community
  • 1
  • 1
Towni0
  • 87
  • 1
  • 6
  • Aside: See also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Apr 13 '14 at 12:16
  • Thanks, already using that in the main program but I could of added it here aswell. – Towni0 Apr 13 '14 at 15:08
  • AFAIK, you'll need an inverse transform, as discussed in the example cited and [here](http://stackoverflow.com/a/3666903/230513). – trashgod Apr 13 '14 at 22:20
  • Thanks @trashgod! That fixed the problem of getting the correct mouse cords. – Towni0 Apr 14 '14 at 18:56
  • +1 for updating your [mcve](http://stackoverflow.com/help/mcve). You can [answer your own question](http://meta.stackexchange.com/q/17463/163188), perhaps citing your example and those that guided you. – trashgod Apr 14 '14 at 19:10

0 Answers0