I have a few geometric shapes which are in different co-ordinates that I want to be brought to the center of the panel. I have written a function called "Set Scale," which is bringing it in to center, but the shape is reversed.
My Code:
private void SetScale1(Graphics2D gr, int gr_width , int gr_height, double left_x , double right_x , double top_y , double bottom_y ){
Rectangle2D drawing_rect = new Rectangle2D.Double(left_x, top_y, right_x - left_x, bottom_y - top_y);
double drawing_cx=( left_x+ right_x) / 2;
double drawing_cy =(top_y + bottom_y) / 2;
AffineTransform at = AffineTransform.getTranslateInstance(-1 * drawing_cx, -1 * drawing_cy);
//gr.translate(-1 * drawing_cx, -1 * drawing_cy);
//gr.TranslateTransform(0, 0)
double scale_x=gr_width / drawing_rect.getWidth();
double scale_y=gr_height / Math.abs(drawing_rect.getHeight());
scale_x = Math.min(scale_x, scale_y);
scale_y = scale_x;
scale_x = Math.abs(scale_x);
// at = AffineTransform.getScaleInstance(scale_x, -1 * scale_y);
//gr.transform(tt);
gr.transform(at);
//' Translate to center over the drawing area.
double graphics_cx =gr_width / 2;
double graphics_cy = gr_height / 2;
gr.translate(graphics_cx, graphics_cy);
}
From the description given, I would recommend you take a look at your transformation matrix:
AffineTransform at = AffineTransform.getTranslateInstance(-1 * drawing_cx, -1 * drawing_cy);
Are you certain that your tx and ty parameters are correct? Here is the matrix returned by getTranslationInstance:
[ 1 0 tx ]
[ 0 1 ty ]
[ 0 0 1 ]
The -1 values you have set in the parameters for getTranslationInstance seem suspect to me... unless I've missed something, I don't see any reason why they should be negative. Negative values should mirror the result, so that may very well be why you're seeing the reversal. Try changing those (they will change the tx and ty fields in the matrix above, respectively) and let us know if it helps.
i Tried this... it Works:-)
Thanks for the help:-)
Rectangle2D drawing_rect = new Rectangle2D.Double(left_x, top_y, right_x - left_x, bottom_y - top_y);
drawing_cx=(left_x+ right_x) / 2;
drawing_cy =(top_y + bottom_y) / 2;
at= AffineTransform.getTranslateInstance( -1* drawing_cx, -1*drawing_cy);
//gr.translate(-1 * drawing_cx, -1 * drawing_cy);
//gr.TranslateTransform(0, 0)
double scale_x=gr_width / drawing_rect.getWidth();
double scale_y=gr_height / Math.abs(drawing_rect.getHeight());
scale_x = Math.min(scale_x, scale_y);
scale_y = scale_x;
scale_x = Math.abs(scale_x);
// at = AffineTransform.getScaleInstance(scale_x, -1 * scale_y);
gr.scale(Math.round(scale_x), Math.round( scale_y));
//gr.scale(1.2, 1.2);
//gr.transform(tt);
gr.transform(at);
//' Translate to center over the drawing area.
graphics_cx =gr_width / 2;
graphics_cy = gr_height / 2;
gr.translate(graphics_cx, graphics_cy);
Related
I get the x- and y-orientation from an RFID-Tag and want to animate the movement in a JavaFX application. It's my first java project so I'm sorry if there are stupid mistakes.
I rotate an box-node in the way of this and this thread.
Two pictures of how the green node rotates in front of the RFID-reader image in the background
xAxis=red, yAxis=green, zAxis=blue
.
I call the rotateNode method like that:
// calculate necessary variables:
delta_x = -x_angle + x_angle_old;
delta_y = -y_angle + y_angle_old;
delta_x_radians = Math.toRadians(delta_x);
delta_y_radians = Math.toRadians(delta_y);
pitch_rad = delta_y_radians;
yaw_rad = 0d; // not used at the moment
roll_rad = delta_x_radians;
if (!((roll_rad == 0d) && (pitch_rad == 0d) && (yaw_rad == 0d))) {
rotateNode(model3D, pitch_rad, yaw_rad, roll_rad);
}
My box-node has the position (0,0,-200) at the beginning and the center of the object should stay in that position the hole time. Just the orientation in two directions should change. My rotateNode method looks like this:
public static void rotateNode(Group n, double pitch_rad, double yaw_rad, double roll_rad) {// , TranslateTransition
// tt_z) {
double A11 = Math.cos(roll_rad) * Math.cos(yaw_rad);
double A12 = Math.cos(pitch_rad) * Math.sin(roll_rad)
+ Math.cos(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A13 = Math.sin(roll_rad) * Math.sin(pitch_rad)
- Math.cos(roll_rad) * Math.cos(pitch_rad) * Math.sin(yaw_rad);
double A21 = -Math.cos(yaw_rad) * Math.sin(roll_rad);
double A22 = Math.cos(roll_rad) * Math.cos(pitch_rad)
- Math.sin(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A23 = Math.cos(roll_rad) * Math.sin(pitch_rad)
+ Math.cos(pitch_rad) * Math.sin(roll_rad) * Math.sin(yaw_rad);
double A31 = Math.sin(yaw_rad);
double A32 = -Math.cos(yaw_rad) * Math.sin(pitch_rad);
double A33 = Math.cos(pitch_rad) * Math.cos(yaw_rad);
double d = Math.acos((A11 + A22 + A33 - 1d) / 2d);
if (d != 0d) {
double den = 2d * Math.sin(d);
if (den != 0d) {
Point3D p = new Point3D((A32 - A23) / den, (A13 - A31) / den, (A21 - A12) / den);
x_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxX()
+ n.localToScene(n.getBoundsInLocal()).getMinX()) / 2d;
y_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxY()
+ n.localToScene(n.getBoundsInLocal()).getMinY()) / 2d;
z_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxZ()
+ n.localToScene(n.getBoundsInLocal()).getMinZ()) / 2d;
r.setPivotX(x_pos_node);
r.setPivotY(y_pos_node);
r.setPivotZ(z_pos_node);
r.setAxis(p);
r.setAngle(Math.toDegrees(d));
n.getTransforms().add(r);
Transform all = n.getLocalToSceneTransform();
n.getTransforms().clear();
n.getTransforms().add(all);
}
}
}
Printing the following variables shows that the node moves in y although I don't want that to happen. Also I see, that slowly with time the pivot point of the rotation isn't in the center of the node anymore and when I turn the RFID-Tag it doesn't spin around the middle of the node it spins in a circle which gets bigger and bigger..
from:
x_pos_node: 0,00
y_pos_node: 0,39
z_pos_node: -200,00
MaxX: 199,00
MinX: -199,00
MaxY: 2,78
MinY: -2,00
MaxZ: -176,12
MinZ: -223,88
Depth: 47,76
Height: 4,78
Width: 398,00
to:
x_pos_node: 0,00
y_pos_node: 15,52
z_pos_node: -200,00
MaxX: 198,51
MinX: -198,51
MaxY: 38,35
MinY: -7,31
MaxZ: -130,85
MinZ: -269,15
Depth: 138,30
Height: 45,67
Width: 397,02
Picture from the side that shows how the green node moves under blue z-Axis / zero line:
.
Where is my mistake? Why does the object slowly moves instead of just rotating?
It is possible to fix the wrong position when I add an Translation:
n.getTransforms().add(new Translate(0, -y_pos_node, 0));
But that's just an hotfix and you can see how the object moves down and up again.. I think there is an error in the calculations or the positioning of the pivot point. It also turns a bit around the green y-Axis although "yaw_rad" is set to 0;
I have been writing a image editing application for fun and all is well but i have ran into a problem with the zoom feature. The image editor plane is 512 x 512 pixels large but the image i want to edit is only 16 x 16. I want to know how to project my mouse coordinates to the smaller image to edit it pixel by pixel.
i have devised this algorithm to to such.
/**
*
* #param pointx The x position of the point thats being bound
* #param pointy The y position of the point thats being bound
* #param oldsizeX The old grid size x of which the point is currently in. ( eg ==> 512*512)
* #param oldsizeY The old grid size y of which the point is currently in. ( eg 512* ==> 512)
* #param newsizeX The new grid size x for the new grid size of the point. ( eg ==> 16*16)
* #param newsizeY The new grid size y for the new grid size of the point. ( eg 16* ==> 16)
* #param normalOffsetX The offset x, if any, the grid has in the normal plane ( eg ==> 32*32 # (512*512))
* #param normalOffsetY The offset y, if any, the grid has in the normal plane ( eg 32* ==> 32 # (512*512)
* #return A Vector2 containing the bound points in the new plane.
*/
public static Vector2 bindPoint(int pointx, int pointy, int oldsizeX, int oldsizeY, int newsizeX, int newsizeY,int normalOffsetX,int normalOffsetY) {
Vector2 vec = new Vector2();
int tileSizeX = oldsizeX / newsizeX;
int tileSizeY = oldsizeY / newsizeY;
int offsetX = normalOffsetX, offsetY = normalOffsetY;
vec.x = (int) (pointx / 2) / (oldsizeX / tileSizeX) - (offsetX / tileSizeX);
vec.y = (int) (pointy / 2) / (oldsizeY / tileSizeY) - (offsetY / tileSizeY);
if(pointx >= normalOffsetX && pointx <= normalOffsetX + oldsizeX && pointy >= normalOffsetY && pointy <= normalOffsetY + oldsizeY) {
return vec;
}else {
return new Vector2(-1,-1);
}
}
This works as long as the smaller resolution is 16x16 and i have found that if i change the 2 after the pointX and pointY division to 0.5 and an image of 32x32 works. What i want to know is if there is a better way to do so, so that i can use any size image at any zoom level?
You should not use integers to represent the position. Use double instead when you do the calculations. In the end, when you have calculated everything and need a pixel value, round the double to an integer. Otherwise you will have lose precision all over the place (which explains the problems you see).
You get different results depending on how you use your brackets. For example, from a math point of view the below Systems out's should give you the same result, but they don't:
int i = 700;
int j = 70;
int k = 30;
System.out.println((i / 2) / (j / k)); --> 175
System.out.println(i / 2 / j * k); --> 150
I figured it out on my own lol, sorry, its late and i spaced and forgot how to proportion.
Here is the answer for anyone else who needs it!
/**
*
* #param pointx The x position of the point thats being bound
* #param pointy The y position of the point thats being bound
* #param oldsizeX The old grid size x of which the point is currently in. ( eg ==> 512*512)
* #param oldsizeY The old grid size y of which the point is currently in. ( eg 512* ==> 512)
* #param newsizeX The new grid size x for the new grid size of the point. ( eg ==> 16*16)
* #param newsizeY The new grid size y for the new grid size of the point. ( eg 16* ==> 16)
* #param normalOffsetX The offset x, if any, the grid has in the normal plane ( eg ==> 32*32 # (512*512))
* #param normalOffsetY The offset y, if any, the grid has in the normal plane ( eg 32* ==> 32 # (512*512)
* #return A Vector2 containing the bound points in the new plane.
*/
public static Vector2 bindPoint(int pointx, int pointy, int oldsizeX, int oldsizeY, int newsizeX, int newsizeY,int normalOffsetX,int normalOffsetY) {
Vector2 vec = new Vector2();
int tileSizeX = oldsizeX / newsizeX;
int tileSizeY = oldsizeY / newsizeY;
int offsetX = normalOffsetX, offsetY = normalOffsetY;
vec.x = (int) Math.floor(pointx * ((float) newsizeX) / (float) oldsizeX) - (offsetX / tileSizeX);
vec.y = (int) Math.floor(pointy * ((float) newsizeY) / (float) oldsizeY) - (offsetY / tileSizeY);
return vec;
}
my math isnt too great but im trying to learn though..
What im trying to do is give my games missiles a helix rocket effect, but i dont know how to work the Sin and Cos to make the helix play out in the right direction..
This is a 3D game by the way:
The problem is, depending on which direction the missile faces, the helix looks warped or flat..
Whats the best way to mathematically calculate a helix based on the missiles X,Y,Z/direction?, ive been trying to figure it out for a long time :/
Thanks alot!
double x = radius * Math.cos(theta);
double y = radius * Math.sin(theta);
double z = radius * Math.cos(theta);
location.add(x,y,z);
missile.shootFlame(location,2);
location.subtract(x,y,z);
Basis vectors
you need the overall direction of missile as 3D vector let call it W. From it you need to get 2 another vectors U,V which are all perpendicular to each other. To get them you can exploit cross product. So:
make W unit vector
Just W = W/|W| so:
l=sqrt(Wx*Wx+Wy*Wy+Wz*Wz);
Wx/=l;
Wy/=l;
Wz/=l;
choose U as any direction non parallel to W
so start with U=(1.0,0.0,0.0) and if U==W choose U=(0.0,1.0,0.0). If you got anything to lock to use that as U direction so the coordinate system will not rotate with time (like Up,North,Sun ...)
U should be unit so if not normalize it just like in #1
compute V
It should be perpendicular to U,W so use cross product:
V = W x U
Cross product of unit vectors is also unit vector so no need to normalize.
recompute U
As we choose the U manually we need to make sure it is also perpendicular to V,W so:
U = V x W
Now we have 3 perpendicular basis vectors where U,V lies in plane of the helix screws and W is the overall movement direction.
If you do not know how to compute the cross product see:
Understanding 4x4 homogenous transform matrices look for [edit2].
Now the Helix is easy:
Defines
so we have U,V,W on top of that we need radius r [units], movement speed v [units/s], angular speed o [rad/s] time t>=0.0 [s] and start position P0.
Helix equation
What we need is equation returning actual position for time so:
ang = o*t;
P(t) = P0 + U*r*cos(ang) + V*r*sin(ang) + W*v*t;
rewritten to scalars:
ang = o*t;
x = P0x + Ux*r*cos(ang) + Vx*r*sin(ang) + Wx*v*t;
y = P0y + Uy*r*cos(ang) + Vy*r*sin(ang) + Wy*v*t;
z = P0z + Uz*r*cos(ang) + Vz*r*sin(ang) + Wz*v*t;
[edit1] as you are incompetent to copy paste and or changing my code correctly...
Vector w = loc.getDirection();
double wX = w.getX();
double wY = w.getY();
double wZ = w.getZ();
double l = Math.sqrt((wX * wX) + (wY * wY) + (wZ * wZ));
wX = wX / l;
wY = wY / l;
wZ = wZ / l;
w = new Vector(wX,wY,wZ); // you forget to change W and use it latter ...
Vector u = new Vector(0, 1.0, 0);
if (Math.abs(wX)<1e-3) // if U and W are the same chose different U
if (Math.abs(wZ)<1e-3)
u = new Vector(1.0, 0.0, 0);
Vector v = w.crossProduct(u);
u = v.crossProduct(w);
double radius = 10; // helix radius [units]
double speed = 2.00; // movement speed [unit/s]
double omega = 0.628; // angular speed [rad/s]
//double omega = 36.0; // angular speed [deg/s]
for (double i = 0; i < 100; i += 1.0) // time [s]
{
double angle = omega * i; // actual screw position [rad] or [deg]
double x = u.getX() * radius * Math.cos(angle) + v.getX() * radius * Math.sin(angle) + wX * speed * i;
double y = u.getY() * radius * Math.cos(angle) + v.getY() * radius * Math.sin(angle) + wY * speed * i;
double z = u.getZ() * radius * Math.cos(angle) + v.getZ() * radius * Math.sin(angle) + wZ * speed * i;
loc.add(x,y,z); // What is this? should not you set the x,y,z instead of adding?
//do work
loc.subtract(x,y,z); // what is this?
}
This should provide you with helix points with traveled linear distance
speed*imax = 2.0*100.0 = 200.0 units
And screws:
omega*imax/(2*Pi) ~= 0.628*100.0/6.28 ~= 10 screws // in case of sin,cos want [rad]
omega*imax/360.0 = 36.0*100.0/360 = 10.0 screws // in case of sin,cos want [deg]
Do not forget to rem in/out the correct omega line (I choose [rad] as that is what I am used that my math libs use). Not sure If I translated to your environment correctly there may be bugs like abs has different name or u = new Vector(1.0, 0.0, 0); can be done on intermediate or declaration of variable only etc which I do not know as I do not code in it.
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
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?