Search code examples
javaswingrotationpolygonaffinetransform

Java rotating a Polgon deforms it


I have written a method, which randomly generates polygon shapes, which then are rotating and moving across the screen. Since I wanted to detect collision with these shapes, I did not rotate them with Graphics2D and instead used AffineTransform to rotate them. But for some reason, certain shapes are getting messed up by the rotation, while others are unaffected. Below is an example with one of the shapes which caused problems.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

public class Test extends JLabel{

    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

    private static final long serialVersionUID = 1L;

    Polygon poly;

    Point center;
    Point source[];
    Point dest[];

    JFrame jf;

    public Test() {

        init();
        createPolygon();

        Timer timer = new Timer(20, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                rotatePoly();
                repaint();
            }});
        timer.start();
    }
    public void rotatePoly() {

        AffineTransform transf = AffineTransform.getRotateInstance(Math.toRadians(2), center.x, center.y);
        transf.transform(source, 0, dest, 0, source.length);

        poly = toPolygon(dest);

    }
    public Polygon toPolygon(Point[] points) {

        Polygon polygon = new Polygon();

        for (int i = 0; i < points.length; i++) {
            polygon.addPoint(points[i].x, points[i].y);
        }
        return polygon;
    }
    public void createPolygon() {

        Point points[] = new Point[7];

        points[0] = new Point(20, 97);
        points[1] = new Point(82, 70);
        points[2] = new Point(134, 70);
        points[3] = new Point(210, 88);
        points[4] = new Point(210, 106);
        points[5] = new Point(144, 125);
        points[6] = new Point(82, 125);

        source = points;
        dest = points;

        poly = toPolygon(points);

        center = new Point(poly.getBounds().x + poly.getBounds().width / 2, poly.getBounds().y + poly.getBounds().height / 2);

    }
    public void init() {

        setVisible(true);
        setSize(260, 260);

        jf = new JFrame();
        jf.setVisible(true);
        jf.setSize(260, 260);
        jf.setContentPane(new JLabel());
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setLocation((screenSize.width / 2) - (getWidth() / 2), (screenSize.height / 2) - (getHeight() / 2));
        jf.add(this);

    }
    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.RED);

        g2d.drawPolygon(poly);

    }
}

If you replace the points with the following lines, the shape mostly stays the same. The shape below is of course symmetrical, however the rotate method did work with other randomly generated and uneven shapes.

points[0] = new Point(10, 130);
points[1] = new Point(100, 10);
points[2] = new Point(160, 10);
points[3] = new Point(250, 100);
points[4] = new Point(250, 160);
points[5] = new Point(160, 250);
points[6] = new Point(100, 250);

Solution

  • This works with your AffineTransform. It returns a transformed shape instead of modifying the coordinates. I also recommend:

    • JFrame.setLocationRelativeTo(null); for centering on screen.
    • Use RenderingHints with Antialiasing on to smooth out the graphics.
    • Since Polygon implements Shape, a few locations needed to be retyped.
    class Text extends JLabel{
    
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    
        private static final long serialVersionUID = 1L;
    
        Shape poly;
    
        Point center;
        Point source[];
        Point dest[];
    
        JFrame jf;
    
        public static void main(String[] args) {
            new Text();
        }
        public Text() {
    
            init();
            createPolygon();
    
            Timer timer = new Timer(20, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    rotatePoly();
                    repaint();
                }});
            timer.start();
        }
        public void rotatePoly() {
    
            AffineTransform transf = 
            AffineTransform.getRotateInstance(Math.toRadians(2), center.x, center.y);
            poly = transf.createTransformedShape(poly);
    
        }
        public Shape toPolygon(Point[] points) {
    
            Polygon polygon = new Polygon();
    
            for (int i = 0; i < points.length; i++) {
                polygon.addPoint(points[i].x, points[i].y);
            }
            return polygon;
        }
        public void createPolygon() {
    
            Point points[] = new Point[7];
    
            points[0] = new Point(20, 97);
            points[1] = new Point(82, 70);
            points[2] = new Point(134, 70);
            points[3] = new Point(210, 88);
            points[4] = new Point(210, 106);
            points[5] = new Point(144, 125);
            points[6] = new Point(82, 125);
    
            source = points;
            dest = points;
    
            poly = toPolygon(points);
    
            center = new Point(poly.getBounds().x + poly.getBounds().width / 2, poly.getBounds().y + poly.getBounds().height / 2);
    
        }
        public void init() {
    
            setVisible(true);
            setSize(260, 260);
    
            jf = new JFrame();
            jf.setVisible(true);
            jf.setSize(260, 260);
            jf.setContentPane(new JLabel());
            jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jf.setLocation((screenSize.width / 2) - (getWidth() / 2), (screenSize.height / 2) - (getHeight() / 2));
            jf.add(this);
    
        }
        @Override
        public void paintComponent(Graphics g) {
    
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.RED);
    
            g2d.draw(poly);
    
        }
    }