import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot)
/**
* Actors that inherit this class have methods available to them to rotate around any
* given point in the world. They also maintain double precision for their co-ordinates.
*
*
*
* Double precision code taken from Poul's SmoothMover class.
*
* @author Michael Berry (mjrb4)
* @author Poul Henriksen
* @version 17/02/09
*/
public abstract class Rotator extends Actor
{
private double x = 0;
private double y = 0;
/**
* Get exactly where we are in the world
* @return the x value of our position
*/
public double getExactX()
{
return x;
}
/**
* Get exactly where we are in the world
* @return the y value of our position
*/
public double getExactY()
{
return y;
}
/**
* Providing 'setLocation' based on doubles. This will eventually call
* the built-in 'setLocation' method in Actor.
*/
public void setLocation(double x, double y)
{
this.x = x;
this.y = y;
super.setLocation((int) x, (int) y);
}
/**
* Override the default 'setLocation' method to keep our own coordinates
* up-to-date.
*/
public void setLocation(int x, int y)
{
setLocation((double) x, (double) y);
}
/**
* Get the rotation around a specific point in the world.
* @param x the x co-ordinate of the point
* @param y the y co-ordinate of the point
* @return the rotation in degrees around the point
* (0 degrees is to the left of the point)
*/
public double getRotation(double x, double y)
{
double xDistance = getExactX()-x;
double yDistance = getExactY()-y;
double angle = Math.toDegrees(Math.atan2(yDistance, xDistance))+180;
return angle;
}
/**
* Set the rotation of this object to a value around
* a specific point.
* @param degrees the degrees to set the rotation to
* (0 degrees is to the left of the point)
* @param x the x co-ordinate of the point
* @param y the y co-ordinate of the point
*/
public void setRotation(double degrees, double x, double y)
{
//Convert degrees to between 180 and -180
degrees = ((degrees+180) % 360)-180;
/*
* We need to deal with 180 and -180 especially, but this is
* pretty simple - just put the actor the other side of the
* point of rotation.
*
* HACK: Until it deals with 180 properly, it'll just call the
* method with 179.9. This works but isn't very nice, so I'll
* update this at some point to work properly...
*/
if(Math.abs(degrees)==180) {
setRotation(179.9, x, y);
return;
}
/*
* Otherwise, we've got a bit more maths on our hands...
* Fair bit of trig going on here, including the sine rule.
*/
else {
/*
* Let's get the distance between this point and the
* point we're rotating around.
*/
double xDistance = getExactX()-x;
double yDistance = getExactY()-y;
double distance = Math.sqrt((xDistance*xDistance + yDistance*yDistance));
/*
* Bit of a hack, but first set the location to 0
* degrees around the point.
* Then we can just work from there.
*/
setLocation(x-distance, y);
/*
* Next is a bit trickier. Imagine a triangle formed
* from a line that starts at the point we're rotating
* around at 0 degrees and another line at *degrees*
* degrees. Both those lines are length *distance*,
* and the third line joins those two. The triangle is
* therefore isocoles.
*
* One angle is degrees, the other two (identical)
* angles are theta.
*
* c is the length of the side that isn't necessarily
* the same length of distance. (Sine rule is needed
* to calculate this.
*
* Finally, imagine a second triangle formed by a
* vertical line drawn from the point in the triangle
* furthest away in the y direction from the point of
* rotation, to intersect with the side of the
* triangle drawn at 0 degrees to the point of rotation.
* This line then forms a triangle by splitting the
* triangle in two - the triangle we're interested
* in is the one that doesn't necessarily have an
* angle the size of *degrees* in it. This triangle
* is a right angled triangle, so we can use basic
* trig to find what we need (the offsets of x and y.)
*
* The line that we initially drew to form this
* second triangle is the magnitude of the y offset
* needed, and the other side (not the hypotenuse)
* is the x offset.
*
* So phew, we're done - simply add the x offset to the
* x position and likewise with the y offset, and we
* have our circle rotation!
*/
double theta = (180-degrees)/2;
double c = (distance*sin(degrees))/(sin(theta)); //Sine rule
double dx = c*cos(theta);
double dy = c*sin(theta);
setLocation(getExactX()+dx, getExactY()-dy);
}
}
/**
* Get the sine of a number in degrees.
* @param a the number to manipulate
* @return the sine of a in degrees
*/
private double sin(double a)
{
return Math.sin(Math.toRadians(a));
}
/**
* Get the cosine of a number in degrees.
* @param a the number to manipulate
* @return the cosine of a in degrees
*/
private double cos(double a)
{
return Math.cos(Math.toRadians(a));
}
/**
* Get the tan of a number in degrees.
* @param a the number to manipulate
* @return the tan of a in degrees
*/
private double tan(double a)
{
return Math.tan(Math.toRadians(a));
}
}