Home > Blockchain >  Java rotating a Polgon deforms it
Java rotating a Polgon deforms it

Time:11-05

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);

CodePudding user response:

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);

    }
}

CodePudding user response:

The problem is mostly caused by using integer coordinates for the polygon.
Instead of accumulating the rotations in the polygon itself, use a variable to hold the angle and calculate a new polygon based on the original one every time the angle is changed. The original polygon is not changed.

I tried to maintain the original code as much as possible1:

package cfh.test.sf;

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.Shape;
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 PolygonTest extends JLabel{

    public static void main(String[] args) {
        new PolygonTest();
    }
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

    private static final long serialVersionUID = 1L;

    Polygon poly;
    Shape rotated;
    Point center;
    int angle = 0;

    JFrame jf;

    public PolygonTest() {

        init();
        createPolygon();

        Timer timer = new Timer(20, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                rotatePoly();
                repaint();
            }});
        timer.start();
    }
    public void rotatePoly() {
        angle  = 2;
        AffineTransform transf = AffineTransform.getRotateInstance(Math.toRadians(angle), center.x, center.y);
        rotated = transf.createTransformedShape(poly);
    }
    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);

        poly = toPolygon(points);
        rotated = poly;

        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(rotated);

    }
}

1 - instead of having an additional shape in a field, I would prefer to create the rotated shape inside paintComponent. Not sure if that conflicts with the rest of the program (e.g. calculating intersection)


Alternative, not tested: use Point2D.Float or Point2D.Double instead of Point for the coordinates.

  • Related