Trying to rotate about a point, but lose precision no matter what - java

Im trying to make some objects (represented by points) rotate around a fixed point, but while they rotate, they get closer and closer to the point of rotation. Here is a screenshot taken early of the rotating triangles:
and here is a shot taken moments later
As you can tell the distance from the center is shortened as well as their distance from each other. Here is the code I'm using to rotate the points about a another point:
public void rotateAboutPoint(double x, double y, double angleRad){
for(int i = 0; i<rep.size(); i++){
rep.get(i).x = (Math.cos(angleRad) * (rep.get(i).x - x) - Math.sin(angleRad)*(rep.get(i).y - y) + x);
rep.get(i).y = (Math.sin(angleRad) * (rep.get(i).x - x) + Math.cos(angleRad)*(rep.get(i).y - y) + y);
}}
I used the same algorithm used here. Everything works fine except the objects shrink. I've tried using more precise types, which doesn't help. I've tried wrapping the expressions with Math.Round() to see if that would help by rounding up when necessary. Any thoughts?

The value of rep.get(i).x you need for the second formula should be the original value, not the new value. Just use a temporary variable. I think this should work.
public void rotateAboutPoint(double x, double y, double angleRad){
for(int i = 0; i<rep.size(); i++){
double temp = (Math.cos(angleRad) * (rep.get(i).x - x) - Math.sin(angleRad)*(rep.get(i).y - y) + x);
rep.get(i).y = (Math.sin(angleRad) * (rep.get(i).x - x) + Math.cos(angleRad)*(rep.get(i).y - y) + y);
rep.get(i).x = temp;
}}

Related

Sorting by Polar Angle

I'm trying to implement the Graham’s Scan algorithm for a convex hull in Java, and have trouble sorting the points by polar angle with respect to the point with lowest y-value.
I have found this answer and understand how to calculate the polar angle but not how to sort the points.
I have seen implementations were Collections.sort() is being used but none of them seem to work with the Point2D class which I want to use because I want to be able to have doubles as coordinates.
Basically I want to be able to sort an ArrayList<Point2D> by their polar angle to the point with the lowest y-value in the same ArrayList.
Could someone please help me with this? Thanks.
I modified the last answer to this question.
Define a new class for Point:
class Point {
private long x, y;
Point(long x, long y) {
this.x = x;
this.y = y;
}
Point() {
x = 0;
y = 0;
}
public long getX() {
return x;
}
public long getY() {
return y;
}
}
Define a new function for calculating the cross product of two vectors:
public long cross(long x1, long y1, long x2, long y2) {
return x1 * y2 - x2 * y1;
}
Let's assume that initial is the Point which has the lowest Y coordinate. Also, let's assume that List<Point> points is a list with all the other points available, but it does NOT contain the initial point.
In order to sort the list, we can use Collections.sort with the comparator:
Collections.sort(points, (a, b) -> {
long cr = cross(a.getX() - initial.getX(), a.getY() - initial.getY(), b.getX() - initial.getX(), b.getY() - initial.getY());
if (cr > 0)
return 1;
else
return -1;
});
In this solution, we used the cross product to check whether two vectors are positioned clockwise or counter-clockwise.
This solution has two benefits:
It's more precious when our coordinates are integer numbers. When we calculate angles in other solutions, we may have some errors in floating point calculations.
In other solutions we may have "division by zero", but we don't have this problem here.
Let's assume that initial is the Point2D which has the lowest Y coordinate. Also, let's assume that List<Point2D> points is a list with all the other points available, but it does NOT contain the initial point.
In order to sort the list, we can use Collections.sort with the comparator:
Collections.sort(points, (a, b) -> {
double cotanA = -(a.getX() - initial.getX()) / (a.getY() - initial.getY());
double cotanB = -(b.getX() - initial.getX()) / (b.getY() - initial.getY());
if (cotanA - cotanB < 0) {
return 1;
}
return -1;
});
Edit:
This solution might encounter a division by zero. One way to overcome this is to use a cross product. See this answer from Erfan Alimohammadi.

Rotating Polygon Objects

Rotating Asteroids ( Polygons )
I am trying to rotate asteroids(polygons) so that they look nice. I am doing this through multiple mathematical equations. To start I give the individual asteroid a rotation velocity:
rotVel = ((Math.random()-0.5)*Math.PI/16);
Then I create the polygon shape,
this.shape = new Polygon();
Followed by generating the points,
for (j = 0; j < s; j++) {
theta = 2 * Math.PI / s * j;
r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
x = (int) -Math.round(r * Math.sin(theta)) + asteroidData[0];
y = (int) Math.round(r * Math.cos(theta)) + asteroidData[1];
shape.addPoint(x, y);
}
Finally, in a loop a method is being called in which it attempts to move the polygon and its points down as well as rotating them. (I'm just pasting the rotating part as the other one is working)
for (int i = 0; i < shape.npoints; i++) {
// Subtract asteroid's x and y position
double x = shape.xpoints[i] - asteroidData[0];
double y = shape.ypoints[i] - asteroidData[1];
double temp_x = ((x * Math.cos(rotVel)) - (y * Math.sin(rotVel)));
double temp_y = ((x * Math.sin(rotVel)) + (y * Math.cos(rotVel)));
shape.xpoints[i] = (int) Math.round(temp_x + asteroidData[0]);
shape.ypoints[i] = (int) Math.round(temp_y + asteroidData[1]);
}
now, the problem is that when it prints to the screen the asteroids appear to 'warp' or rather the x and y positions on some of the polygon points 'float' off course.
I've noticed that when I make 'rotVel' be a whole number the problem is solved however the asteroid will rotate at mach speeds. So I've concluded that the problem has to be in the rounding but no matter what I do I can't seem to find a way to get it to work as the Polygon object requires an array of ints.
Does anyone know how to fix this?
Currently your asteroids rotate around (0 , 0) as far as i can see. Correct would be to rotate them around the center of the shape, which would be (n , m), where n is the average of all x-coordinates of the shape, and m is the average of all y-coordinates of the shape.
Your problem is definitely caused by rounding to int! The first improvement is to make all shape coordinates to be of type double. This will solve most of your unwanted 'effects'.
But even with double you might experience nasty rounding errors in case you do a lot of very small updates of the coordinates. The solution is simple: Just avoid iterative updates of the asteroid points. Every time, you update the coordinates based on the previous coordinates, the rounding error will get worse.
Instead, add a field for the rotation angle to the shape and increment it instead of the points themselves. Not until drawing the shape, you compute the final positions by applying the rotation to the points. Note that this will never change the points themselves.
You can extend this concept to other transformations (e.g. translation) too. What you get is some kind of local coordinate system for every shape/object. The points of the shape are defined in the local coordinate system. By moving and rotating this system, you can reposition the entire object anywhere in space.
public class Shape {
// rotation and position of the local coordinate system
private double rot, x, y;
// points of the shape in local coordinate system
private double[] xp, yp;
private int npoints;
// points of the shape in world coordinates
private int[][] wxp, wyp;
private boolean valid;
public void setRotation(double r) { this.rot = r; valid = false; }
public void setPosition(double x, double y) { this.x = x; this.y = y; valid = false; }
public void addPoint(double x, double y) {
// TODO: add point to xp, yp
valid = false;
}
public void draw(...) {
if (!valid) {
computeWorldCoordinates(wxp, wyp);
valid = true;
}
// TODO: draw shape at world coordaintes wxp and wyp
}
protected void computeWorldCoordinates(int[] xcoord, int[] ycoord) {
for (int i = 0; i < npoints; i++) {
double temp_x = xp[i] * Math.cos(rot) - yp[i] * Math.sin(rot);
double temp_y = xp[i] * Math.sin(rot) + yp[i] * Math.cos(rot);
xcoord[i] = (int) Math.round(x + temp_x);
ycoord[i] = (int) Math.round(y + temp_y);
}
}
}

Java: My object won't travel at an angle between 0 and 45 degrees

I started learning java just over a year ago so i'm still fairly new.
I'm trying to make an object travel from one point to another at a constant net velocity no matter where the second point is in the frame. Currently it's working fairly well as long as I run the method every few frames.
The only problem is that it it will only move horizontally unless the second point is approximately between 45 and 135 degrees or between 225 and 315 degrees (1/2π and 3/2π or 5/2π and 7/2π).
It may be because of the 'if' statements meant to stop it from dividing by 0 but it doesn't seem like it. Also if there is any way to simplify those equations or remove 'if' statements I wouldn't mind some advice there too.
Note: vel is the net velocity the objects travel at and Prime.mx and Prime.my is the location of the target point.
public void target()
{
if (Prime.mx > x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x + (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.mx < x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x - (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my > y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y + (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my < y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y - (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
}
I use Math.round twice because the first brings it to a float from a double and the second makes it an int. I need the x and y as ints so the paint method can draw the objects.
I found a few simillar problems on the site but the closest one was in python and and the anwer didn't seem applicable to my problem.
I believe you are overcomplicating this. If your starting point is (sx, sy) and your destination is (dx, dy) then you can easily calculate any point (x, y) that is p distance along the line (0.0 <= p <= 1.0). You can use this to move at velocity v. So I suggest finding your end point and then using simple arithmetic to move on the x and y axis.
float dx = dist * Math.cos(angle);
float dy = dist * Math.sin(angle);
for (float p = 0.0; p <= 1.0; p = Math.min(1.0, p + dist / v) {
x = sx + p * (dx - sx);
y = sy + p * (dy - sy);
}
The Math.min expression in the for loop ensures that you end up exactly at the destination point.
If you already have the destination point then it's just as easy. Instead of finding dx and dy from dist and angle you find dist from dx and dy using pythagoras.
More than solution these are some advices.
First, implement all you coordinate variables as floats to prevent rounding precision loss errors and round only right before painting.
Second, define float dx = Prime.mx - x; float dy = Prime.my - y; distance to target from current point (to use later). I would use Math.atan2(dy,dx) to compute angle between current point and target. Then use that angle to increment coordinates like this:
x += Math.cos(angle)*vel;
y += Math.sin(angle)*vel;
Third, check if your object is at target using (dx*dx + dy*dy <= radius*radius) for suitable radius (can be 1).
Also note that if the y axis goes down, then the angle will be CW (clock-wise) instead of CCW (counter-clock-wise).

smooth color interpolation along a "bresenham" line

I am trying to interpolate color along a line so that, given two points and their respective RGB values, I can draw a line with a smooth color gradient. Using Bresenham's Line Algorithm, I can now draw lines, but am not sure how to begin interpolating colors between the two end points. The following is part of the drawLine() function that works for all line whose slope are less than 1.
int x_start = p1.x, x_end = p2.x, y_start =p1.y, y_end = p2.y;
int dx = Math.abs(x_end-x_start), dy = Math.abs(y_end-y_start);
int x = x_start, y = y_start;
int step_x = x_start < x_end ? 1:-1;
int step_y = y_start < y_end ? 1:-1;
int rStart = (int)(255.0f * p1.c.r), rEnd = (int)(255.0f * p2.c.r);
int gStart = (int)(255.0f * p1.c.g), gEnd = (int)(255.0f * p2.c.g);
int bStart = (int)(255.0f * p1.c.b), bEnd = (int)(255.0f * p2.c.b);
int xCount = 0;
//for slope < 1
int p = 2*dy-dx;
int twoDy = 2*dy, twoDyMinusDx = 2*(dy-dx);
int xCount = 0;
// draw the first point
Point2D start = new Point2D(x, y, new ColorType(p1.c.r, p1.c.g, p1.c.b));
drawPoint(buff, start);
float pColor = xCount / Math.abs((x_end - x_start));
System.out.println(x_end + " " + x_start);
while(x != x_end){
x+= step_x;
xCount++;
if(p<0){
p+= twoDy;
}
else{
y += step_y;
p += twoDyMinusDx;
}
Point2D draw_line = new Point2D(x, y, new ColorType(p1.c.r*(1-pColor)+p2.c.r*pColor,p1.c.g*(1-pColor)+p2.c.g*pColor,p1.c.b*(1-pColor)+p2.c.b*pColor));
System.out.println(pColor);
drawPoint(buff,draw_line );
}
So what I'm thinking is that, just like drawing lines, I also need some sort of decision parameter p to determine when to change the RGB values. I am thinking of something along lines of as x increments, look at each rgb value and decide if I want to manipualte them or not.
I initialized rStart and rEnd(and so on for g and b) but have no idea where to start. any kind of help or suggestions would be greatly appreciated!
Edit: thanks #Compass for the great suggestion ! Now I've ran into another while trying to implementing that strategy, and I am almost certain it's an easy bug. I just can't see it right now. For some reason my pColor always return 0, I am not sure why. I ran some print statements to make sure xCount is indeed increasing, so I am not sure what else might've made this variable always 0.
I remember figuring this out way back when I was learning GUI! I'll explain the basic concepts for you.
Let's say we have two colors,
RGB(A,B,C)
and
RGB(X,Y,Z)
for simplicity.
If we know the position percentage-wise (we'll call this P, a float 0 for beginning, 1.0 at end) along the line, we can calculate what color should be there using the following:
Resultant Color = RGB(A*(1-P)+X*P,B*(1-P)+Y*P,C*(1-P)+Z*P)
In other words, you average out the individual RGB values along the line.
Actually you will be drawing the line in RGB space as well !
Bresenham lets you compute point coordinates from (X0, Y0) to (X1, Y1).
This is done by a loop on X or Y, with a linear interpolation on the other coordinate.
Just extend the algorithm to draw a line from (X0, Y0, R0, G0, B0) to (X1, Y1, R1, G1, B1), in the same loop on X or Y, with a linear interpolation on the other coordinates.

Rotation - lack of precision - Java

I hava a class representing a point (x and y coordinates are double type) and a function to rotate the point around another point:
public Point2D rotate(double angle, Point2D origin) {
double sin = Math.sin(angle);
double cos = Math.cos(angle);
x -= origin.getX();
y -= origin.getY();
x = x*cos - y*sin;
y = x*sin + y*cos;
x += origin.getX();
y += origin.getY();
return this;
}
However when I repeat the rotation many times (i.e. by 1 Degree) I loose much of precision. Example:
Point2D point = new Point2D(10, 10);
System.out.println(point);
for(int i = 0; i < 360; i++)
point.rotate(Math.toRadians(1), new Point2D(300, 150));
System.out.println(point);
And the results:
[10.0, 10.0]
[25.5048671135757, 17.40466547204096]
Do you have any idea how to solve this issue? Thanks in advance.
firstly, there's an error in your formula...
the line
x = x*cos - y*sin;
modifies the value of x and the modified value is used in the next line
y = x*sin + y*cos;
You have to use a temp variable to store the new value of x and y, this is what user2602548 meant with his sugestion.
I guess you're already using double since you would otherwise need a cast to float for those two lines.
If you fix the algorithmic error you get something like [9.99999999999659, 9.999999999998778] .
If that's not good enough for you, you could either round after the rotations and if that's also not good enough use a lib that provides trigonometric functions with more precision like apfloat.
Using BigDecimal with that problem won't give you any more precision because the problem here is that the results of sin() and cos() are still only double precision.

Categories