TRU7H
TRU7H

Reputation: 197

How to rotate a polygon/points around a point in Java

I've been trying to rotate a polygon around a specified center point but everything I've tried has failed miserably. I've googled for example and found many but not a single one seems to work.

The result I'm trying to replicate is similar with the first answer to this

How to rotate an image gradually in Swing?

The difference is that I need the polygon to actually rotate, just drawing it in an angle won't cut it. (it's for simple physics modelling)

Here's my code, with several different methods I've tried to implement

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;

public class rotationPanel extends JPanel {

    private static final int SIZE = 500;
    private static final Shape outline = makeShape();

    Point p;
    Point p2;
    Point p3;
    Point p4;
    Point[] points;
    Point[] npoints;

    Point center;
    Polygon poly;

    double angle;

    Timer timer;
    long start;
    long sleepTime;
    static int runTime;

    public rotationPanel(){
        setSize(500,500);
        setBackground(Color.DARK_GRAY);
        setVisible(true);

        runTime = 100;  //ms

        start = 0;
        sleepTime = 0;
        timer = new Timer();

        center = new Point(250,250);

        p = new Point(200,200);
        p2 = new Point(150,150);
        p3 = new Point(250,150);
        p4 = new Point(200,100);

        /*
        points = new Point[4];
        points[0]=p;
        points[1]=p2;
        points[2]=p3;
        points[3]=p4;

        npoints = new Point[4];
        npoints[0]=p;
        npoints[1]=p2;
        npoints[2]=p3;
        npoints[3]=p4;

        poly = new Polygon();
        */

    }

    public void mainloop(){
        start= System.currentTimeMillis();

        //rotate(points,2);

        p = rotatePoint(p,center);
        p2 = rotatePoint(p2,center);
        p3 = rotatePoint(p3,center);
        p4 = rotatePoint(p4,center);

        repaint();

        sleepTime = runTime -(System.currentTimeMillis()-start);
        System.out.println("Looped. Sleeping for:" +sleepTime+"ms");

        if(sleepTime>0)
            timer.schedule(new loop(), sleepTime);
        else
            mainloop();

    }

     private static Shape makeShape() {
            AffineTransform at = new AffineTransform();
            at.translate(SIZE/2, SIZE/2);
            at.scale(20, 20);
            at.rotate(Math.toRadians(35));
            return at.createTransformedShape(initPoly());       
        }

        /** Create a U shaped outline. */
        private static Polygon initPoly() {
            Polygon poly = new Polygon();
            poly.addPoint( 1,  0);
            poly.addPoint( 1, -2);
            poly.addPoint( 2, -2);
            poly.addPoint( 2,  1);
            poly.addPoint(-2,  1);
            poly.addPoint(-2, -2);
            poly.addPoint(-1, -2);
            poly.addPoint(-1,  0);
            return poly;
        }

    public void rotatePoint(Point pt, double rotationAngle){

        AffineTransform.getRotateInstance
        (Math.toRadians(rotationAngle), center.x, center.y)
                .transform(pt,pt);          
    }

    public Point rotatePoint(Point pt, Point center)
    {
        angle = (Math.toRadians(150));
        double cosAngle = Math.cos(angle);
        double sinAngle = Math.sin(angle);

        pt.x = center.x + (int) ((pt.x-center.x)*cosAngle-(pt.y-center.y)*sinAngle);
        pt.y = center.y + (int) ((pt.x-center.x)*sinAngle+(pt.y-center.y)*cosAngle);
        return pt;
    }

    public void rotate(Point[] pts, int angle){

        AffineTransform.getRotateInstance
        (Math.toRadians(angle), center.x, center.y)
                .transform(pts,0,npoints,0,4);

        points = new Point[4];
        points[0]=npoints[0];
        points[1]=npoints[1];
        points[2]=npoints[2];
        points[3]=npoints[3];

    }

    public void paintComponent(Graphics g) {
           super.paintComponent(g);  

           g.setColor(Color.BLUE);
           g.fillRect(center.x-4, center.y-4, 8, 8);

           g.setColor(Color.YELLOW);
           //g.fillRect(p.x-4, p.y-4, 8, 8);
           //g.fillRect(p2.x-4, p2.y-4, 8, 8);
           //g.fillRect(p3.x-4, p3.y-4, 8, 8);
           //g.fillRect(p4.x-4, p4.y-4, 8, 8);

           g.fillRect(p.x, p.y, 2, 2);
           g.fillRect(p2.x, p2.y, 2, 2);
           g.fillRect(p3.x, p3.y, 2, 2);
           g.fillRect(p4.x, p4.y, 2, 2);
    }

    class loop extends TimerTask{

        public void run() {
            mainloop();         
        }

    }

}

Upvotes: 2

Views: 17745

Answers (2)

verydul
verydul

Reputation: 31

Here is a simple method to build a polygon from a set of points, rotated around a center point, at a specified angle:

/**
 * Builds a polygon from a set of points, rotated around a point, at the
 * specified rotation angle.
 * 
 * @param centerX the int center x coordinate around which to rotate
 * @param centerY the int center y coordinate around which to rotate
 * @param xp the int[] of x points which make up our polygon points. This
 *           array is parallel to the yp array where each index in this array
 *           corresponds to the same index in the yp array.
 * @param yp the int[] of y points which make up our polygon points. This
 *           array is parallel to the xp array where each index in this array
 *           corresponds to the same index in the xp array.
 * @param rotationAngle the double angle in which to rotate the provided
 *                      coordinates (specified in degrees).
 * @return a Polygon of the provided coordinates rotated around the center point
 *         at the specified angle.
 * @throws IllegalArgumentException when the provided x points array is not the
 *                                  same length as the provided y points array
 */
private Polygon buildPolygon(int centerX, int centerY, int[] xp, int[] yp, double rotationAngle) throws IllegalArgumentException {
    // copy the arrays so that we dont manipulate the originals, that way we can
    // reuse them if necessary
    int[] xpoints = Arrays.copyOf(xp,xp.length);
    int[] ypoints = Arrays.copyOf(yp,yp.length);
    if(xpoints.length != ypoints.length){
        throw new IllegalArgumentException("The provided x points are not the same length as the provided y points.");
    }

    // create a list of Point2D pairs
    ArrayList<Point2D> list = new ArrayList();
    for(int i = 0; i < ypoints.length; i++){
        list.add(new Point2D.Double(xpoints[i], ypoints[i]));
    }

    // create an array which will hold the rotated points
    Point2D[] rotatedPoints = new Point2D[list.size()];

    // rotate the points
    AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(rotationAngle), centerX, centerY);
    transform.transform(list.toArray(new Point2D[0]), 0, rotatedPoints, 0, rotatedPoints.length);

    // build the polygon from the rotated points and return it
    int[] ixp = new int[list.size()];
    int[] iyp = new int[list.size()];
    for(int i = 0; i < ixp.length; i++){
        ixp[i] = (int)rotatedPoints[i].getX();
        iyp[i] = (int)rotatedPoints[i].getY();
    }
    return new Polygon(ixp, iyp, ixp.length);
}

Upvotes: 2

TRU7H
TRU7H

Reputation: 197

As you didn't help me vary much I was forced to figure this out by myself. Here we go:

The correct approach (or at least one of them) is to use affine transform to the points of the polygon you wish to rotate. The catch is that you cannot rotate the same polygon over and over again as it will severely deform due to the continuous rounding.

So the trick is to keep " an original version" of the polygon and always rotate that one.

Of course, this approach is only critical when rotating the polygon several times. If you want to only rotate it once you can simply use the values from the polygon you want to rotate.

Here's a little example I managed to put together:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;

import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class rotationPanel extends JPanel {

    private static final long serialVersionUID = 117L;



    private static final int SIZE = 500;


    // point arrays which contain the points that are rotated around the center 
    Point[] points1;
    Point[] points2;
    Point[] points3;

    // The center of rotation  
    Point center;

    // the polygons being rotated
    Polygon poly1;
    Polygon poly2;
    Polygon poly3;

    // the angle of rotation
    double angle;


    Timer timer;
    long start;
    long sleepTime;
    static int runTime;





    public rotationPanel(){
        setSize(500,500);
        setBackground(Color.DARK_GRAY);
        setVisible(true);

        // time loop is set to run at fixed rate of 50 ms
        runTime = 50; 

        start = 0;
        sleepTime = 0;
        timer = new Timer();
        angle = 0;


        // initializing the arrays (not neccesary)
        points1 = getOriginalPoints(1);
        points3 = getOriginalPoints(3);
        points2 = getOriginalPoints(2);

        // setting the rotation to the middle of the screen
        center = new Point(250,250);


        // start the looping
        mainloop();

    }

    public void mainloop(){
        start= System.currentTimeMillis();

        // rotate the points the spcified angle and store the rotated
        //points to the correct array
        rotatePointMatrix(getOriginalPoints(1),angle,points1);
        rotatePointMatrix(getOriginalPoints(2),angle,points2);
        rotatePointMatrix(getOriginalPoints(3),angle,points3);

        // Make the points into a polygon
        poly1 = polygonize(points1);
        poly2 = polygonize(points2);
        poly3 = polygonize(points3);

        // increase the angle by one degree, resulting to rotation in the longer run
        angle++;



        if (angle>=360){
            angle=0;
        }



        // restatring the sequence 
        repaint();

        sleepTime = runTime -(System.currentTimeMillis()-start);
        System.out.println("Looped. Sleeping for:" +sleepTime+"ms");

        if(sleepTime>0)
            timer.schedule(new loop(), sleepTime);
        else
            mainloop();

    }





    public void rotatePointMatrix(Point[] origPoints, double angle, Point[] storeTo){

        /* We ge the original points of the polygon we wish to rotate
         *  and rotate them with affine transform to the given angle. 
         *  After the opeariont is complete the points are stored to the 
         *  array given to the method.
        */
        AffineTransform.getRotateInstance
        (Math.toRadians(angle), center.x, center.y)
                .transform(origPoints,0,storeTo,0,5);


    }

    public Polygon polygonize(Point[] polyPoints){

        //a simple method that makes a new polygon out of the rotated points
        Polygon tempPoly = new Polygon();

         for(int  i=0; i < polyPoints.length; i++){
             tempPoly.addPoint(polyPoints[i].x, polyPoints[i].y);
        }

        return tempPoly;

    }


    public Point[] getOriginalPoints(int type){

        /* In this example the rotated "polygon" are stored in this method. 
         * The Point is that if we want to rotate a polygon constatnly/frequently
         * we cannot use the values of an already rotated polygon as this will 
         * lead to the polygon deforming severely after few translations due 
         * to the points being constantly rounded. So the trick is to save the
         * original Points of the polygon and always rotate that one to the new
         *  angle instead of rotating the same one again and again.
        */
        Point[] originalPoints = new Point[5];

        if(type == 2){

        originalPoints[0]= new Point(200, 100);
        originalPoints[1]= new Point(250, 50);
        originalPoints[2]= new Point(300, 100);
        originalPoints[3]= new Point(300, 400);
        originalPoints[4]= new Point(200, 400);



        }

        else if(type == 1){

        originalPoints[0]= new Point(210, 150);
        originalPoints[1]= new Point(250, 150);
        originalPoints[2]= new Point(250, 190);
        originalPoints[3]= new Point(230, 220);
        originalPoints[4]= new Point(210, 190);


        }
        else{

            originalPoints[0]= new Point(250, 300);
            originalPoints[1]= new Point(290, 300);
            originalPoints[2]= new Point(290, 340);
            originalPoints[3]= new Point(270, 370);
            originalPoints[4]= new Point(250, 340);

        }


        return originalPoints;

    }


    public void paintComponent(Graphics g) {
           super.paintComponent(g);  

           Graphics2D g2d = (Graphics2D) g;

           g2d.setRenderingHint(                    
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

           g2d.setColor(Color.GRAY);
           g2d.fillPolygon(poly2);

           g2d.setColor(Color.yellow);
           g2d.fillPolygon(poly1);

           g2d.setColor(Color.yellow);
           g2d.fillPolygon(poly3);


           g2d.setColor(Color.WHITE);
           for(int  i=0; i < points1.length; i++){
                g2d.fillRect(points1[i].x-1, points1[i].y-1, 3, 3);
                g2d.fillRect(points3[i].x-1, points3[i].y-1, 3, 3);

            }

           g2d.setColor(Color.BLUE);
           g2d.fillOval(center.x-4, center.y-4, 8, 8);

           g2d.setColor(Color.yellow);
           g2d.drawString("Angle: "+angle, 10,450);

    }

    class loop extends TimerTask{


        public void run() {
            mainloop();

        }

    }


    public static void main(String[] args){
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new rotationPanel());
        f.setSize(500,500);
        f.setVisible(true);

    }

}

I hope this helps! Don't hesitate to contact me if you run into trouble!

Upvotes: 10

Related Questions