Skip to main content

Rotating Points and/or Polygons around a center Point

2 replies [Last post]
dmarcum
Offline
Joined: 2010-02-16

After searching for what seems like hours to find a simple solution to this problem...and yes I know that AffineTransform can do this, but I knew there was a lot simpler/faster solution and one that doesn’t require creating new Objects.

The trick is to cache the angle and distance of each point from the center point. After doing so, the points can be rotated and the center point can be moved.

Here is the code...the angles are adjusted to 360 degrees...due north is zero.

import java.awt.*;
import javax.swing.*;

public class PointRotation extends JPanel
{
private Point points[] = new Point[4];
private double angles[] = new double[4];
private double distances[] = new double[4];
private Point center = new Point();
private float angle = 0;

public PointRotation()
{
setLayout(null);

int size = 20;
center.setLocation(100, 100);

points[0] = new Point(center.x - size, center.y - size);
points[1] = new Point(center.x + size, center.y - size);
points[2] = new Point(center.x + size, center.y + size);
points[3] = new Point(center.x - size, center.y + size);

angles[0] = this.getAngle360(center.x, center.y, points[0].x, points[0].y);
angles[1] = this.getAngle360(center.x, center.y, points[1].x, points[1].y);
angles[2] = this.getAngle360(center.x, center.y, points[2].x, points[2].y);
angles[3] = this.getAngle360(center.x, center.y, points[3].x, points[3].y);

distances[0] = center.distance(points[0]);
distances[1] = center.distance(points[1]);
distances[2] = center.distance(points[2]);
distances[3] = center.distance(points[3]);
}

protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;

g2.setPaint(Color.red);
g2.drawLine(points[0].x, points[0].y, points[1].x, points[1].y);
g2.setPaint(Color.blue);
g2.drawLine(points[1].x, points[1].y, points[2].x, points[2].y);
g2.setPaint(Color.green);
g2.drawLine(points[2].x, points[2].y, points[3].x, points[3].y);
g2.setPaint(Color.cyan);
g2.drawLine(points[3].x, points[3].y, points[0].x, points[0].y);

angle ++;

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

for (int index = 0; index < points.length; index ++)
{
this.rotatePoint(index, angle);
}

try
{
Thread.sleep(20);
}
catch (Exception exception)
{
// Ignore
}

repaint();
}

private double getAngle360(double x1, double y1, double x2, double y2)
{
double angle = Math.toDegrees(Math.atan2(y2 - y1, x2 - x1));
if (angle < 0) angle = 180 + (180 - -angle);
angle += 90;
if (angle >= 360) angle -= 360;
return angle;
}

private void rotatePoint(int index, float angle)
{
double newAngle = angles

+ angle;
if (newAngle >= 360) newAngle -= 360;

double radians = Math.toRadians(newAngle);
double distance = this.distances

;

int deltaX = (int) (distance * Math.sin(radians));
int deltaY = (int) (distance * -Math.cos(radians));

points

.setLocation(center.x + deltaX, center.y + deltaY);
}

public static void main(String[] args)
{
PointRotation c = new PointRotation();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(c);
f.setSize(400, 450);
f.setLocation(0, 325);
f.setVisible(true);
}
}

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
flar
Offline
Joined: 2003-06-11

I wanted to point out some other techniques which might also meet your needs.

The simplest would be to use:

g2.rotate(radians, centerx, centery);
g2.draw(untransformed_object);

This doesn't create any new objects at the application level, though some pipelines might create new objects internally. Have you investigated the performance of this technique to show that it isn't fast enough (or that it creates too much garbage in practice) for your needs?

Another solution for "doing it manually" as you do, but relying on standard objects to do your work for you would be to use:

private AffineTransform scratchAT = new AffineTransform();
private double origpoints[] = new double[npoints * 2];
private double txpoints[] = new double [npoints * 2];
... then in paintComponent()...
at.setToRotation(radians, centerx, centery);
at.transform(origpoints, 0, txpoints, 0, npoints);
... then either use drawLine: ...
for (int i = 2; i < txpoints.length; i += 2) {
g2.setColor(...);
g2.drawLine((int) txpoints[i-2], (int) txpoints[i-1], (int) txpoints[i], (int) txpoints[i+1]);
}
... or use a temp shape which would allow sub-pixel rendering positions: ...
for (int i = 2; i < txpoints.length; i += 2) {
g2.setColor(...);
templine.setLine(txpoints[i-2 through i+1]);
g2.draw(templine);
}

I'm not sure if either of those techniques meet your needs, but they might involve less custom code on your part.

It is important to note that your solution and the second solution above do not allow for quality line joins at the vertices should the rendering ever be scaled up (such as when it is printed). To make sure that the line joins look nice under a scale, you should use the first solution above.

Message was edited by: flar

dmarcum
Offline
Joined: 2010-02-16

oops...some of the code got cut off...replace the end of paintComponent method with this...

try
{
Thread.sleep(20);
}
catch (Exception exception)
{
// Ignore
}

repaint();
}

private double getAngle360(double x1, double y1, double x2, double y2)
{
double angle = Math.toDegrees(Math.atan2(y2 - y1, x2 - x1));
if (angle < 0) angle = 180 + (180 - -angle);
angle += 90;
if (angle >= 360) angle -= 360;
return angle;
}