Search code examples
javarotationimage-rotation

Java - rotate image in place


I have a tank cannon layer that should rotate toward the mouse position, which I have done with the following code:

double theta = Math.atan2(point.getY() - center.getY(), point.getX() - center.getX());

then I rotate the graphics object's transform accordingly. This works, but the cannon rotates around the cannon's center. I want the cannon's base to stay in position when the rotation happens. I've tried many different positions to rotate around, but I can't get one that will keep its base in position. I think that I must translate the graphics object after rotation to where the cannon's base should be, but I don't know how. My cannon with both layers: enter image description here

As you can see, the base (the light green part) must stay in it's position. How can I achieve this?


Solution

  • Okay, assuming that the turret and base are separate images, and the turret isn't the same size as the tank (cause then it becomes complicated....more then it actually is :P)

    You can use a AffineTransform and compound the transformations...

        // This is the x/y position of the top, at the top/left point,
        // I've placed it at the center of my screen, but you get the idea
        double x = (getWidth() - base.getWidth()) / 2d;
        double y = (getHeight() - base.getHeight()) / 2d;
    
        // Translate the location to the x/y, this makes the top/left 0x0...
        // much easier to deal with...
        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
        g2d.setTransform(at);
        // Draw the base...
        g2d.drawImage(base, 0, 0, this);
    
        // Offset the turret, in my testing, this was 8x8 from the bases
        // top/left
        at.translate(8, 8);
        if (targetPoint != null) {
            // Calculate the delta between the mouse and the center point
            // of the turret, this is in screen coordinates and not
            // translated coordinates
            double deltaX = (x + 8) - targetPoint.x;
            double deltaY = (y + 8) - targetPoint.y;
    
            // Calculate the rotation required to point at the mouse
            // Had to apply an offset to allow for the default orientation
            // of the tank...
            double rotation = Math.atan2(deltaY, deltaX) + Math.toRadians(180d);
            // Rotate around the anchor point of the turret
            // Remember, we've translated so the top/left (0x0) is now the
            // turrets default position
            at.rotate(rotation, 4, 4);
        }
        // Transform the Graphics context
        g2d.setTransform(at);
        // Paint the turret
        g2d.drawImage(turret, 0, 0, this);
    }
    g2d.dispose();
    

    And because I went to effort...

    My assets...

    BaseTurret

    Tank

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Line2D;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class FollowMe {
    
        public static void main(String[] args) {
            new FollowMe();
        }
    
        public FollowMe() {
            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 Point targetPoint;
            private BufferedImage turret;
            private BufferedImage base;
    
            public TestPane() {
                addMouseMotionListener(new MouseAdapter() {
    
                    @Override
                    public void mouseMoved(MouseEvent e) {
                        targetPoint = e.getPoint();
                        repaint();
                    }
    
                });
                try {
                    base = ImageIO.read(getClass().getResource("/Base.png"));
                    turret = ImageIO.read(getClass().getResource("/Turret.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
                g.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
                if (base != null) {
                    double x = (getWidth() - base.getWidth()) / 2d;
                    double y = (getHeight() - base.getHeight()) / 2d;
                    // Test line from center of tank to mouse poisition
                    if (targetPoint != null) {
                        g2d.draw(new Line2D.Double((x + 12), (y + 12), targetPoint.x, targetPoint.y));
                    }
                    AffineTransform at = AffineTransform.getTranslateInstance(x, y);
                    g2d.setTransform(at);
                    g2d.drawImage(base, 0, 0, this);
                    at.translate(8, 8);
                    if (targetPoint != null) {
                        double deltaX = (x + 8) - targetPoint.x;
                        double deltaY = (y + 8) - targetPoint.y;
    
                        double rotation = Math.atan2(deltaY, deltaX) + Math.toRadians(180d);
                        at.rotate(rotation, 4, 4);
                    }
                    g2d.setTransform(at);
                    g2d.drawImage(turret, 0, 0, this);
                }
                g2d.dispose();
            }
    
        }
    
    }
    

    Have a look at Transforming Shapes, Text, and Images for more details