I am trying to rotate a 2D Point in java around another with a specified degree value, in this case simply around Point (0, 0) at 90 degrees.
Method:
public void rotateAround(Point center, double angle) {
x = center.x + (Math.cos(Math.toRadians(angle)) * (x - center.x) - Math.sin(Math.toRadians(angle)) * (y - center.y));
y = center.y + (Math.sin(Math.toRadians(angle)) * (x - center.x) + Math.cos(Math.toRadians(angle)) * (y - center.y));
}
Expected for (3, 0): X = 0, Y = -3
Returned for (3, 0): X = 1.8369701987210297E-16, Y = 1.8369701987210297E-16
Expected for (0, -10): X = -10, Y = 0
Returned for (0, -10): X = 10.0, Y = 10.0
Is something wrong with the method itself? I ported the function from (Rotating A Point In 2D In Lua - GPWiki) to Java.
EDIT:
Did some performance tests. I wouldn't have thought so, but the vector solution won, so I'll use this one.
If you have access to java.awt, this is just
double[] pt = {x, y};
AffineTransform.getRotateInstance(Math.toRadians(angle), center.x, center.y)
.transform(pt, 0, pt, 0, 1); // specifying to use this double[] to hold coords
double newX = pt[0];
double newY = pt[1];
You're mutating the X value of center before performing the calculation on the Y value. Use a temporary point instead.
Additionally, that function takes three parameters. Why does yours only take two?
Related
I'm writing a program that will rotate a rectangular prism around a point. It handles the rotations via 3 rotation methods that each manage a rotation around a single axis (X, Y, and Z). Here's the code
public void spinZ(Spin spin) {
if (x == 0 && y == 0) {
return;
}
double mag = Math.sqrt(x * x + y * y);
double pxr = Math.atan(y / x);
x = Math.cos(spin.zr + pxr) * mag;
y = Math.sin(spin.zr + pxr) * mag;
}
public void spinY(Spin spin) {
if (z == 0 && x == 0) {
return;
}
double mag = Math.sqrt(x * x + z * z);
double pxr = Math.atan(z / x);
x = Math.cos(spin.yr + pxr) * mag;
z = Math.sin(spin.yr + pxr) * mag;
}
public void spinX(Spin spin) {
if (z == 0 && y == 0) {
return;
}
double mag = Math.sqrt(y * y + z * z);
double pxr = Math.atan(z / y);
y = Math.cos(spin.xr + pxr) * mag;
z = Math.sin(spin.xr + pxr) * mag;
}
public void addSpin(Spin spin) {
spinY(spin);
spinX(spin);
spinZ(spin);
}
Spin is a useless class that stores three doubles (which are rotations). These methods basically convert the rotations into 2D vectors (how I store the points) and rotate them as such. The first if statement makes sure the 2D vectors don't a magnitude of 0. They are allowed to, but in that case it's not necessary to carry out the rotation calculations. The other part just handles the trig. The bottom method just ties everything together and allows me to quickly change the order of the rotations (because order should and does affect the final rotation).
The problem isn't with the individual rotations but when they all come together. I can easily get a single rotation around a single axis to work without distorting the rectangular prism. When I put them all together, like if you were to call addSpin().
When spinY is called first, the prism is distorted when the rotations include a Y rotation (if the y component of the rotation is zero, and no rotation around the y-axis should occur, then no distortion occurs). In fact, if spinY() is called anytime but last a distortion of the cube will occur.
The same is the case with spinZ(). If spinZ() is called last, the cube won't get warped. However spinX() can go anywhere and not cause a distortion.
So the question is: Is there a problem with how I'm going about the rotations? The other question is while all rotations cannot be encompassed by rotations along just the X and Y axes or any other pair of distinct axes (like X and Z, or Y and Z), can those three sets collectively make all rotations? To clarify, can the rotations, which cannot be reached by a set of rotations around the X and Y axes, be reached by a set of rotations around the X and Z axes or the Y and Z axes?
I trust the medium I'm using to display the prisms. It's a ray-tracer I made that works well with rectangular prisms. This is a more math-based question, but it has a fairly comprehensive programming component.
These are some parallel calculations that still yield in distortions.
public void spinZ(Spin spin) {
double c = Math.cos(spin.yr);
double s = Math.sin(spin.yr);
double xp = x*c - y*s;
double yp = y*s + x*c;
x = xp;
y = yp;
}
public void spinY(Spin spin) {
double c = Math.cos(spin.yr);
double s = Math.sin(spin.yr);
double zp = z*c - x*s;
double xp = z*s + x*c;
x = xp;
z = zp;
}
public void spinX(Spin spin) {
double c = Math.cos(spin.yr);
double s = Math.sin(spin.yr);
double yp = y*c - z*s;
double zp = z*c + y*s;
y = yp;
z = zp;
}
Your checks for things like
x == 0
are unnecessary and dangerous as a double almost never will have the precise value 0. The atan when you have a division can lead to catastrophic loss of precision as well.
Why are they unnecessary? Because the following performs your rotation in a cleaner (numerically stable) fashion:
double c = Math.cos(spin.yr);
double s = Math.cos(spin.yr);
double zp = z*c - x*s;
double xp = z*s + x*c;
x = xp;
z = zp;
Of course, my example assumes you treat the y rotation with a right handed orientation, but from your sample code you seem to be treating it as left handed. Anyways, the wikipedia article on the Rotation matrix explains the math.
So I want to create array [64][64] of 0 in java and create triangle of ones here. I mean something like:
0001000
0010100
0100010
1111111
So I created class point:
public class Point {
public int x;
public int y;
Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
And I'm creating in main new points objects in that way:
int amountOfPowers = 6;
Point a = new Point((int) (Math.pow(2, amountOfPowers))/2, 0);
Point b = new Point((int) Math.pow(2, amountOfPowers), (int) (Math.pow(2, amountOfPowers) * Math.sqrt(3.0) / 2));
Point c = new Point(0, (int) (Math.pow(2, amountOfPowers) * Math.sqrt(3.0) / 2));
So A is (0, 64), B is (64, 64) and C is (32, 55). Somebody has any idea how can I write all positions in array from 0,64 to 64,64 as 1? (something like [0][64] = 1, [1][64] = 1, ..., [64][64] = 1).
And also how can I create lines of ones from (0,64) to (32, 55) and from (32, 55) to (64, 64). Somebody have any idea how can I do this?
#edit
I tried to use it:
https://en.wikipedia.org/wiki/Line_drawing_algorithm
Here is my method:
static void drawLine(Point from, Point to) {
// https://en.wikipedia.org/wiki/Line_drawing_algorithm
int dx = to.x - from.x;
int dy = to.y - from.y;
for (int x = from.x; x < to.x; x++) {
int y = from.y + dx * (x - from.x) / dy;
System.out.println("X: " + x + " Y: " + y);
canvas[x][y] = 1;
}
}
I wrote this in main:
drawLine(a, b);
drawLine(b, c);
drawLine(c, a);
And here is my result :/ It's something wrong.
0000000000000000000000000000000000000000000000000000000100000000
0000000000000000000000000000000000000000000000000000000100000000
0000000000000000000000000000000000000000000000000000001000000000
0000000000000000000000000000000000000000000000000000001000000000
0000000000000000000000000000000000000000000000000000010000000000
0000000000000000000000000000000000000000000000000000010000000000
0000000000000000000000000000000000000000000000000000100000000000
0000000000000000000000000000000000000000000000000001000000000000
0000000000000000000000000000000000000000000000000001000000000000
0000000000000000000000000000000000000000000000000010000000000000
0000000000000000000000000000000000000000000000000010000000000000
0000000000000000000000000000000000000000000000000100000000000000
0000000000000000000000000000000000000000000000000100000000000000
0000000000000000000000000000000000000000000000001000000000000000
0000000000000000000000000000000000000000000000010000000000000000
0000000000000000000000000000000000000000000000010000000000000000
0000000000000000000000000000000000000000000000100000000000000000
0000000000000000000000000000000000000000000000100000000000000000
0000000000000000000000000000000000000000000001000000000000000000
0000000000000000000000000000000000000000000010000000000000000000
0000000000000000000000000000000000000000000010000000000000000000
0000000000000000000000000000000000000000000100000000000000000000
0000000000000000000000000000000000000000000100000000000000000000
0000000000000000000000000000000000000000001000000000000000000000
0000000000000000000000000000000000000000001000000000000000000000
0000000000000000000000000000000000000000010000000000000000000000
0000000000000000000000000000000000000000100000000000000000000000
0000000000000000000000000000000000000000100000000000000000000000
0000000000000000000000000000000000000001000000000000000000000000
0000000000000000000000000000000000000001000000000000000000000000
0000000000000000000000000000000000000010000000000000000000000000
0000000000000000000000000000000000000100000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
0100000000000000000000000000000000000000000000000000000000000000
0100000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0001000000000000000000000000000000000000000000000000000000000000
0000100000000000000000000000000000000000000000000000000000000000
0000100000000000000000000000000000000000000000000000000000000000
0000010000000000000000000000000000000000000000000000000000000000
0000010000000000000000000000000000000000000000000000000000000000
0000001000000000000000000000000000000000000000000000000000000000
0000001000000000000000000000000000000000000000000000000000000000
0000000100000000000000000000000000000000000000000000000000000000
0000000010000000000000000000000000000000000000000000000000000000
0000000010000000000000000000000000000000000000000000000000000000
0000000001000000000000000000000000000000000000000000000000000000
0000000001000000000000000000000000000000000000000000000000000000
0000000000100000000000000000000000000000000000000000000000000000
0000000000010000000000000000000000000000000000000000000000000000
0000000000010000000000000000000000000000000000000000000000000000
0000000000001000000000000000000000000000000000000000000000000000
0000000000001000000000000000000000000000000000000000000000000000
0000000000000100000000000000000000000000000000000000000000000000
0000000000000100000000000000000000000000000000000000000000000000
0000000000000010000000000000000000000000000000000000000000000000
0000000000000001000000000000000000000000000000000000000000000000
0000000000000001000000000000000000000000000000000000000000000000
0000000000000000100000000000000000000000000000000000000000000000
0000000000000000100000000000000000000000000000000000000000000000
0000000000000000010000000000000000000000000000000000000000000000
0000000000000000001000000000000000000000000000000000000000000000
Anybody have idea how can I solve my problem? :/
If you will think about your arrays as a screen and about 1's as dark pixels and zeroes as white pixels than you have task of drawing a line on screen. In reality it is exactly the task that being actively performed in video card.
Algorithm is expressed here https://en.wikipedia.org/wiki/Line_drawing_algorithm
NB: in algorithm they use int's and due rounding issues you might be will have better results if you will swap coords and will iterate using y instead of x.
I have a circle of let's say 10 of radius with the center x=0 y=0. And I have a number n (e.g. 3). I want to get a point from that circle. Here is an explanation with an image:
So if n=0, the method would return 0;-6
And if n=1, the method would return 3;-5
etc.
But the method would receive parameters like the unit between each n etc.
The equation of a circle is
x = x0 + r * cos(a)
y = y0 + r * sin(a)
with (x0, y0) the center of the circle and a in 0...2Pi
so if you want y given x you will have :
sin(a) = (y - y0)/r
so
a = arcsin((y - y0)/r) if ((y - y0)/r is in -PI/2..PI/2)
a = -arcsin((y - y0)/r) if ((y - y0)/r is in -PI..-PI/2 or PI/2..PI)
a is undefine elsewhere
therefore
y = y0 + r * sin(arcsin((y - y0)/r)) if ((y - y0)/r is in -PI/2..PI/2))
y = y0 + r * sin(-arcsin((y - y0)/r)) if ((y - y0)/r is in -PI..-PI/2 or PI/2..PI))
y is undefine elsewhere
Use the roots of unity, it will give you the exponential form of a complex on the circle. You can then use the Euler formula to get the real coordinates of your point. Of course, since your circle is not unitary, you must take into account its radius.
I'm programming a software renderer in Java, and am trying to use Z-buffering for the depth calculation of each pixel. However, it appears to work inconsistently. For example, with the Utah teapot example model, the handle will draw perhaps half depending on how I rotate it.
My z-buffer algorithm:
for(int i = 0; i < m_triangles.size(); i++)
{
if(triangleIsBackfacing(m_triangles.get(i))) continue; //Backface culling
for(int y = minY(m_triangles.get(i)); y < maxY(m_triangles.get(i)); y++)
{
if((y + getHeight()/2 < 0) || (y + getHeight()/2 >= getHeight())) continue; //getHeight/2 and getWidth/2 is for moving the model to the centre of the screen
for(int x = minX(m_triangles.get(i)); x < maxX(m_triangles.get(i)); x++)
{
if((x + getWidth()/2 < 0) || (x + getWidth()/2 >= getWidth())) continue;
rayOrigin = new Point2D(x, y);
if(pointWithinTriangle(m_triangles.get(i), rayOrigin))
{
zDepth = zValueOfPoint(m_triangles.get(i), rayOrigin);
if(zDepth > zbuffer[x + getWidth()/2][y + getHeight()/2])
{
zbuffer[x + getWidth()/2][y + getHeight()/2] = zDepth;
colour[x + getWidth()/2][y + getHeight()/2] = m_triangles.get(i).getColour();
g2.setColor(m_triangles.get(i).getColour());
drawDot(g2, rayOrigin);
}
}
}
}
}
Method for calculating the z value of a point, given a triangle and the ray origin:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double d = -(A * triangle.getV1().x + B * triangle.getV1().y + C * triangle.getV1().z);
double rayZ = -(A * rayOrigin.x + B * rayOrigin.y + d) / C;
return rayZ;
}
Method for calculating if the ray origin is within a projected triangle:
private boolean pointWithinTriangle(Triangle triangle, Point2D rayOrigin)
{
Vector2D v0 = new Vector2D(triangle.getV3().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v1 = new Vector2D(triangle.getV2().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v2 = new Vector2D(rayOrigin, triangle.getV1().projectPoint(modelViewer));
double d00 = v0.dotProduct(v0);
double d01 = v0.dotProduct(v1);
double d02 = v0.dotProduct(v2);
double d11 = v1.dotProduct(v1);
double d12 = v1.dotProduct(v2);
double invDenom = 1.0 / (d00 * d11 - d01 * d01);
double u = (d11 * d02 - d01 * d12) * invDenom;
double v = (d00 * d12 - d01 * d02) * invDenom;
// Check if point is in triangle
if((u >= 0) && (v >= 0) && ((u + v) <= 1))
{
return true;
}
return false;
}
Method for calculating surface normal of a triangle:
private Vector3D getNormal(Triangle triangle)
{
Vector3D v1 = new Vector3D(triangle.getV1(), triangle.getV2());
Vector3D v2 = new Vector3D(triangle.getV3(), triangle.getV2());
return v1.crossProduct(v2);
}
Example of the incorrectly drawn teapot:
What am I doing wrong? I feel like it must be some small thing. Given that the triangles draw at all, I doubt it's the pointWithinTriangle method. Backface culling also appears to work correctly, so I doubt it's that. The most likely culprit to me is the zValueOfPoint method, but I don't know enough to know what's wrong with it.
My zValueOfPoint method was not working correctly. I'm unsure why :( however, I changed to a slightly different method of calculating the value of a point in a plane, found here: http://forum.devmaster.net/t/interpolation-on-a-3d-triangle-using-normals/20610/5
To make the answer here complete, we have the equation of a plane:
A * x + B * y + C * z + D = 0
Where A, B, and C are the surface normal x/y/z values, and D is -(Ax0 + By0 + Cz0).
x0, y0, and z0 are taken from one of the vertices of the triangle. x, y, and z are the coordinates of the point where the ray intersects the plane. x and y are known values (rayOrigin.x, rayOrigin.y) but z is the depth which we need to calculate. From the above equation we derive:
z = -A / C * x - B / C * y - D
Then, copied from the above link, we do:
"Note that for every step in the x-direction, z increments by -A / C, and likewise it increments by -B / C for every step in the y-direction.
So these are the gradients we're looking for to perform linear interpolation. In the plane equation (A, B, C) is the normal vector of the plane.
It can easily be computed with a cross product.
Now that we have the gradients, let's call them dz/dx (which is -A / C) and dz/dy (which is -B / C), we can easily compute z everywhere on the triangle.
We know the z value in all three vertex positions.
Let's call the one of the first vertex z0, and it's position coordinates (x0, y0). Then a generic z value of a point (x, y) can be computed as:"
z = z0 + dz/dx * (x - x0) + dz/dy * (y - y0)
This found the Z value correctly and fixed my code. The new zValueOfPoint method is:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double dzdx = -A / C;
double dzdy = -B / C;
double rayZ = triangle.getV1().z * modelViewer.getModelScale() + dzdx * (rayOrigin.x - triangle.getV1().projectPoint(modelViewer).x) + dzdy * (rayOrigin.y - triangle.getV1().projectPoint(modelViewer).y);
return rayZ;
}
We can optimize this by only calculating most of it once, and then adding dz/dx to get the z value for the next pixel, or dz/dy for the pixel below (with the y-axis going down). This means that we cut down on calculations per polygon significantly.
this must be really slow
so much redundant computations per iteration/pixel just to iterate its coordinates. You should compute the 3 projected vertexes and iterate between them instead look here:
triangle/convex polygon rasterization
I dislike your zValueOfPoint function
can not find any use of x,y coordinates from the main loops in it so how it can compute the Z value correctly ?
Or it just computes the average Z value per whole triangle ? or am I missing something? (not a JAVA coder myself) in anyway it seems that this is your main problem.
if you Z-value is wrongly computed then Z-Buffer can not work properly. To test that look at the depth buffer as image after rendering if it is not shaded teapot but some incoherent or constant mess instead then it is clear ...
Z buffer implementation
That looks OK
[Hints]
You have too much times terms like x + getWidth()/2 why not compute them just once to some variable? I know modern compilers should do it anyway but the code would be also more readable and shorter... at least for me
Alright, so I got a bit of movement code and I'm thinking I'm going to need to manually input when to go up/down a slope. All I got to work with is the slope's normal, and vector, and My current and previous position, and my yaw.
Is there a better way to rotate whether I go up or down the slope based on my yaw?
Vector3f move = new Vector3f(0,0,0);
move.x = (float)-Math.cos(Math.toRadians(yaw));
move.z = (float)-Math.sin(Math.toRadians(yaw));
System.out.println("slopeNormal.z: " + slopeNormal.z + "move.z: " + move.z);
move.normalise();
float vx = (float) (Math.sqrt(Math.pow(move.y, 2) + Math.pow(move.z, 2)) * move.x);
float vy = (float) (Math.sqrt(Math.pow(move.x, 2) + Math.pow(move.z, 2)) * move.y);
float vz = - vx * slopeNormal.x - vy * slopeNormal.y;
move.scale(movementSpeed * delta);
if(vz < 0)
move.y -= slopeVec.y * 1.5f;
if(vz > 0)
move.y += slopeVec.y * 1.5f;
Vector3f.add(pos, move, pos);
Edit: updated code.
First off, the following is incorrect:
move.x = (float)-Math.toDegrees(Math.cos(Math.toRadians(yaw)));
move.z = (float)-Math.toDegrees(Math.sin(Math.toRadians(yaw)));
Math.toDegrees converts an angle in radians to one in degrees, but the results of Math.cos and Math.sin are not angles.
Assume zero yaw is in the positive x-direction... and define vx, vy, vz = rate of motion along 3 axes, s = speed, and slope normal = nx, ny, nz where nx^2 + ny^2 + nz^2 = 1. So nx = ny = 0, nz = 1 would be flat.
First, I define x', y' = axes relative to the flat ground (motion is constrained to ground). Then (the following is not valid Java, but I'm enclosing it in code format anyway):
vx' = cos(yaw) * s
vy' = sin(yaw) * s
Then I need to rotate from x', y' coordinates to real-world coordinates. That is done using the slope normal:
vx = sqrt(vy^2 + vz^2) vx'
vy = sqrt(vx^2 + vz^2) vy'
vz = - vx' nx - vy' ny
A check on this transformation: vx^2 + vy^2 + vz^2 must equal vx'^2 + vy'^2 = s^2. I think this works out.
So to answer your question: up or down? vz > 0 is up, vz < 0 is down.