Java Graphics2D Translate and Scale

17k views Asked by At

I have a problem. I want to be able to zoom into my Graphics2D screen using the mouse wheel but I want to be able to translate the Graphics2D as well so that it's right on the spot that I scaled. Here's what's happening so far: "http://cdn.makeagif.com/media/6-11-2015/E0kYGY.gif" It's translating to the player's position correctly but I want it to be zoomable in and out with the scrolling of the mouse wheel. Here's the code I have so far to do it:

private AffineTransform getCamTransform()
{
    AffineTransform t = new AffineTransform();
    float a = 400 - player.getX();
    float b = 250 - player.getY();
    t.translate(a, b);
    /*
     * Here's where I want to scale using the zoom
     * variable and also translate the g2d according
     * to the zoom variable.
     */
    return t;
}

private double zoom = 2.0D;

@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
    zoom += e.getPreciseWheelRotation();
}

This is just a small segment of the code I have. I have a method that draws everything to the screen but this is just to return the transform that must be applied to the camera. The transform is then set to the previous transform of the Graphics2D.

Let me know if there's an easier way to do this or I'm doing it all wrong. I do also want to be efficient on the CPU as well. Thanks to whoever answers!

1

There are 1 answers

2
MadProgrammer On BEST ANSWER

The basic idea is, you want to make it appear as if the graphics is remaining centred within the viewable area. I first thought about trying to scale the translation points, and maybe that might work, but I couldn'y get it to work (3 year old wanting to read books didn't give me much time to experiment).

Basically, you need to add another translation in which moves the x/y point the appropriate amount based on the viewable area and the zoom so the image appears to remain stationary within the viewable area, then apply the zoom and the normal translation on-top of it...

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

        double zoomWidth = width * zoom;
        double zoomHeight = height * zoom;

        double anchorx = (width - zoomWidth) / 2;
        double anchory = (height - zoomHeight) / 2;

        AffineTransform at = new AffineTransform();
        at.translate(anchorx, anchory);
        at.scale(zoom, zoom);
        at.translate(-100, -100);

So, the anchorx/y represent the amount the resulting graphics would need be offset by, based on the viewable areas width and height, so that the image will remain "centred" within in it...

Scroll

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private double zoom = 1d;
        private BufferedImage img;

        public TestPane() {
            try {
                img = ImageIO.read(new File("..."));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            addMouseWheelListener(new MouseAdapter() {
                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    if (e.getPreciseWheelRotation() < 0) {
                        zoom -= 0.1;
                    } else {
                        zoom += 0.1;
                    }
//                  zoom += e.getPreciseWheelRotation();
                    if (zoom < 0.01) {
                        zoom = 0.01;
                    }

                    repaint();

                }
            });
        }

        public String format(double value) {
            return NumberFormat.getNumberInstance().format(value);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

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

            double zoomWidth = width * zoom;
            double zoomHeight = height * zoom;

            double anchorx = (width - zoomWidth) / 2;
            double anchory = (height - zoomHeight) / 2;

            AffineTransform at = new AffineTransform();
            at.translate(anchorx, anchory);
            at.scale(zoom, zoom);
            at.translate(-100, -100);

            g2d.setTransform(at);
            g2d.drawImage(img, 0, 0, this);

            g2d.dispose();
        }

    }

}

There's probably a really awesome mathematical formula which you could use instead, but me be dumb ;)