I seem to be having a bit of a math fail here... I need to convert an (x,y) point from one coordinate space to another - not in the sense of polar to cartesian or anything of the sort... just from one bound to another. i.e., for a particular (x,y) that falls in the rectangle with lower left (-100, -100) and upper right (100,100), I need to find out where that point would be in a rectangle with lower left (0,0) and upper right (500, 500).
I feel like this is just simple math but I'm having a heck of a time getting it right...
It is for a little computer graphics program written in java. Essentially there is a clip window that changes, and that clip window needs to fill the whole view window. Initial values for clip and view are given by the above rectangles, in that order. However, the clip could change to, for example, a rectangle with lower left (-80, -65) and upper right (75, 65). I would then need to convert a point that falls within that rectangle to a point that falls within the view window (lower left (0,0), upper right (500, 500))
Here is what I have for it right now:
public int normalizeX(float x) {
float clipWidth = clipRight - clipLeft;
int viewWidth = viewRight - viewLeft;
x += 100; //Get x into range [0, 200] instead of [-100, 100]
//First convert x to value within clip width, then "scale" to viewport width
return (int)(((clipWidth*x)/200) * (viewWidth/clipWidth));
}
public int normalizeY(float y) {
float clipHeight = clipTop - clipBottom;
int viewHeight = viewTop - viewBottom;
y += 100; //Get y into range [0, 200] instead of [-100, 100]
//First convert y to value within clip height, then "scale" to viewport height
return (int)(((clipHeight*y)/200) * (viewHeight/clipHeight));
}
Thanks for any help!
Assuming your old bounds are xLoOld and xHiOld (-80 and 75, respectively, in your example) and your new bounds are xLoNew and xHiNew (0 and 500, respectively, in your example), then you can normalize your xOld to your new coordinate system like this:
xNew = (xOld-xLoOld) / (xHiOld-xLoOld) * (xHiNew-xLoNew) + xLoNew
Same thing for y.
Something like this may help you.
public static float scale(
float x,
float old_min, float old_max,
float new_min, float new_max)
{
float old_range = old_max - old_min;
float new_range = new_max - new_min;
return new_min + (x - old_min) * new_range / old_range;
}
You will need to scale your coordinates in both dimensions. I've left out other computations such as casting to int, etc.
I also recommend creating suitable types such as Region or Rectangle to simplify your code by, at least, reducing the number of arguments to this function.
Simple as hell: you want a transformation which maps -100 to 0 and 100 to 500. Or put blindly, which maps the range [-100, 100] to the range [0, 500] :
[-100, 100] ----> [0, 500]
First step is to convert the range [-100, 100] to [0, 200]:
x ----> x + 100
Next step is to convert the range [0, 200] to [0, 500]
x ----> x * 500 / 200 = 2.5 * x
In fine, your transformation reads
x ----> 2.5 * (x + 100)
and likewise for y:
y ----> 2.5 * (y + 100).
Hope this makes it clear, and that you will be able to reuse the logic in more complex cases.
The slick and general way to do this is with an affine transform.
You'll have a 2 x 2 matrix (call it A) that characterizes the "stretchiness" of the original field to the second field, and a 2 x 1 matrix (call it b) characterizing the offset.
Then, with x as your (2 x 1) input and y as your (2 x 1) output, it's just y = Ax + b.
The technique also lets you do a lot more (like rotations) but those probably aren't important for your application.
http://en.wikipedia.org/wiki/Affine_transformation
Related
I want to calculate x,y coordinates of point on orbit. I have radius (for example 1), coordinates of center of orbit (0,0) and the time it takes to make full circle on orbit (for example 2), starting coordinates of object(-radius,0), and I want to calculate x and y after 1 day, so it should be on radius,0. But how to calculate it without angle?
You're going to have to start by translating the the orbit into a rotation rate, which will give you an equation for θ(t), where t and θ(t) is the angle (normally in radians) in the orbit at time t. The position would then be given by
X ← r·cos(θ(t)) + x0
Y ← r·sin(θ(t)) + y0
where r is the radius of your orbit (which you indicated was 1) and (x0, y0) is the center of the orbit (which you indicated was (0,0)).
If you want the point to have a constant rotation rate and arrive at (r, 0) after exactly 1 day, then your θ(t) would be a function of the form:
θ(t) ← 2·n·π·t + θ(0)
Where t is time in days and n is an integer value. θ(0) is just the starting angle which in your case will be π. There are an infinite number of other such functions which could permit this to occur if you wanted to use a non-constant rotation rate, but you would need to provide some additional requirements for that.
A more general function will permit you to specify the constant rotation rate α and calculate the angle at a time t. This would take on the form
θ(α, t) ← 2·α·π·t + θ(0)
So in your example of a rotation rate of 2, θ(2, t) = 4·π·t
Coding this in Java is left as an exercise to the reader.
I am currently trying to draw a line and keep the line proportionally the same no matter how the user resizes the JFrame. However, the problem I run into is when I try to draw a line when the user makes the JFrame smaller than the default value, as I end up multiplying the coordinates by fractions under 1, and since g2.drawLine() requires integers, it takes them as 0's and nothing is drawn. I'm wondering if there's a work-around to this little glitch or if you guys have any suggestions of how I should change my logic.
I think what you're seeing is just because of integer division. See Why is the result of 1/3 == 0?. When you have (width / 624), the result of this is always 0 if width is less than 624.
You could:
use (width / 624.0), which performs the division in floating-point (as double), or
you could rearrange your parentheses to be e.g. (int) ((x * width) / 624)) instead of (int) (x * (width / 624)).
However, to answer the question directly, you can draw a line with floating-point coordinates by using java.awt.geom.Line2D:
Line2D line2D = new Line2D.Double(x1, y1, x2, y2);
graphics2D.draw(line2D);
(Also see https://docs.oracle.com/javase/tutorial/2d/geometry/primitives.html.)
multiplying the coordinates by fractions under 1, and since
g2.drawLine() requires integers, it takes them as 0's
That's obviously false! If a coordinates is say, 327, then multiplying it by say 0.7 gives 228.9. That's not an integer but is has integer part, so you can safely convert it to integer:
double factor = ...;
int newCoord, oldCoord = ...;
newCoord = (int)(oldCoord * factor)
will give you the rounded result.
Or something else is wrong...
I'm making a little game just for fun and I got stuck making the bullets come out of the gun. In the code below, the direction of the player is a degree angle called rot.
float gunOffsetX = 106, gunOffsetY = 96;
double angle = Math.toRadians(rot); // convert direction of player from degrees to radians for sin and cos
x = getX(); // player X
y = getY(); // player Y
float bulletX = (float) (x + (gunOffsetX * Math.cos(angle) - gunOffsetY * Math.sin(angle)));
float bulletY = (float) (y + (gunOffsetX * Math.sin(angle) + gunOffsetY * Math.cos(angle)));
Instances.fire.add(new Fire(bulletX, bulletY, rot, weapon));
Also tried:
bulletX = (float) (x + Math.cos(angle + Math.atan2(gunOffsetX, gunOffsetY)) * Point2D.distance(0, 0, gunOffsetX, gunOffsetY));
But same results
Supposedly the bullets should spawn at the end of the gun but this isn't the case as you can see in the following gif..
Any help appreciated
One big issue (at least in my opinion) is how this game handles anchor points of shapes, like the player.
We can highlight the anchor point by drawing a little red rectangle on its place:
g.setColor(Color.RED);
g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);
This comes into the Draw#renderGame(Graphics2D) method, so it looks like:
private void renderGame(Graphics2D g) {
g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
g.drawImage(player.getCurrentFrame(), (int)player.getX(), (int)player.getY(), player.getWidth(), player.getHeight(), null);
g.setColor(Color.RED);
g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);
g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
//...
then we'll see that the anchor point is not in the center of the image:
As you can see, the anchor point (the original (0,0) point before the rotation) isn't in the center of the image and the crosshair is related to it, instead of the view of the player.
This happens due to the shifting operation while the player rotation:
g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
//...
g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
You're shifting the postion with +64. I suggest to remove that and add the shifting to the g.drawImage call instead, so the anchor point is correctly in the center (mind that I avoided the fixed value 64):
g.rotate(Math.toRadians(player.rot), player.getX(), player.getY());
g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2), player.getWidth(), player.getHeight(), null);
g.rotate(-Math.toRadians(player.rot), player.getX(), player.getY());
Then you now fire the gun you'll see that the bullet always "starts" from a certain position from the player. The problem here is the incorrect offset you used. The proper values are:
float gunOffsetX = 35, gunOffsetY = 29;
(I got them by trial and error, so you may adjust them a bit more, if you like)
Now it looks like this:
As you can see, the shot is still a bit misplaced, but this happens due to the incorrect rotation of the bullet (like you did it for the player shape):
g.rotate(Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);
It should look like this (without any X or Y adjustments):
g.rotate(Math.toRadians(f.rot), f.getX(), f.getY());
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX(), f.getY());
The end result is:
The player now correctly looks at the crosshair and the shots are placed in front of the gun.
If you like to fire directly through the center of the crosshair, you'll only need to adjust the player position and the bullet offset a bit.
Player (in Draw#renderGame(Graphics2D)):
g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2) - 30, player.getWidth(), player.getHeight(), null);
(mind the -30 in (int)player.getY() - (player.getHeight() / 2) - 30)
Bullet:
float gunOffsetX = 35, gunOffsetY = 0;
Now the bullet travels right through the crosshair (mind that the red rectangle is right on the weapon):
(I'm a bit too stupid to create proper GIF files, so I can only provide pictures)
Now you have the necessary offset values to get the result you want, but you should definitely try to understand why the values are like they are right now. You need to replace them later with dynamic values, since different weapons need different offsets for the bullet, because the player image differs. It should be helpful to have some kind of class with instances for each weapon type, which contains the images and the coordinates where the weapon barrel is located in the image. Then you can use these coordinates to correctly set the offsets for the bullet image.
I have a float[] newCoords variable that has a size of 9. The first 3 entries represent one vertex, the next 3 represent the second vertex and the last 3 represent the last vertex.
I have some code that is supposed to rotate a triangle anywhere in space when I feed it the coordinates. It looks like this:
float s = (float) Math.sin(0.5);
float c = (float) Math.cos(0.5);
float[] centroid = getCentroid(newCoords);
newCoords[0] -= centroid[0];
newCoords[1] -= centroid[1];
newCoords[3] -= centroid[0];
newCoords[4] -= centroid[1];
newCoords[6] -= centroid[0];
newCoords[7] -= centroid[1];
newCoords[0] = (newCoords[0] * c) - (newCoords[1] * s);
newCoords[1] = (newCoords[0] * s) + (newCoords[1] * c);
newCoords[3] = (newCoords[3] * c) - (newCoords[4] * s);
newCoords[4] = (newCoords[3] * s) + (newCoords[4] * c);
newCoords[6] = (newCoords[6] * c) - (newCoords[7] * s);
newCoords[7] = (newCoords[6] * s) + (newCoords[7] * c);
newCoords[0] += centroid[0];
newCoords[1] += centroid[1];
newCoords[3] += centroid[0];
newCoords[4] += centroid[1];
newCoords[6] += centroid[0];
newCoords[7] += centroid[1];
The problem is, its not rotating it properly, the triangles are spinning and getting smaller and smaller until they disappear for some reason, can anyone see why this is happening?
EDIT: whoops, almost forgot, here is my getCentroid() method.
private float[] getCentroid(float[] p1) {
float[] newCoords = new float[] {(p1[0] + p1[3] + p1[6]) / 3.0f,
(p1[1] + p1[4] + p1[7]) / 3.0f, 0};
return newCoords;
}
I see two problems with your code. Both are fixed with a little change.
You try to apply a rotation operation, taking X and Y coordinates as input and having the new X and Y as output. For every vertex you rotate, you have two lines of code: the first computes the X, the second the Y coordinate. But when computing the Y coordinate, you use the already rotated X coordinate! That's wrong.
There is also a numerical problem. You reuse the old values again and again, resulting in a chain of rotation computations a value makes though, so the numerical errors sum up. Never rely on such computations to work as expected. Instead, you should work with the original values and increase the angle in each frame. This makes sure that each value only participated in a single rotation computation.
For fixing both problems, keep the original coordinates somewhere in your code, I call them coords, and rewrite the code such that you take that array as input (keep newCoords as the output). Increase the rotation angle in each frame to achieve a rotation animation.
This fixes both problems because you get rid of that chain and also you have different arrays for input and output in your rotation function.
Pseudo-code:
// initial:
angle = 0.0;
coords = (initial coordinates)
// per frame:
angle += 0.5;
newCoords = rotate(coords, angle);
draw(newCoords);
Also, please note that 0.5 is a large angle if you want to rotate by that angle frame by frame. The math functions expect angle in radians (not degrees), so you might want to use a lower value depending on what you want to visualize in particular.
You might wonder why I reuse the old angle in each frame, as according to the above mentioned problem 2., it should introduce numerical problems, since it's also a chain of computations. That's not a problem with the rotation angle, as a simple summation doesn't show such bad numerical errors you experience with applying rotations. Yet it has some problems, but they only show up at very long running times when the angle reaches some billions. The reason why such a summation in general is not that bad is because you're changing the variable in the same direction in each frame as well as a slightly off rotation angle isn't noticed very much by the user.
I'm making pretty simple game. You have a sprite onscreen with a gun, and he shoots a bullet in the direction the mouse is pointing. The method I'm using to do this is to find the X to Y ratio based on 2 points (the center of the sprite, and the mouse position). The X to Y ratio is essentially "for every time the X changes by 1, the Y changes by __".
This is my method so far:
public static Vector2f getSimplifiedSlope(Vector2f v1, Vector2f v2) {
float x = v2.x - v1.x;
float y = v2.y - v1.y;
// find the reciprocal of X
float invert = 1.0f / x;
x *= invert;
y *= invert;
return new Vector2f(x, y);
}
This Vector2f is then passed to the bullet, which moves that amount each frame.
But it isn't working. When my mouse is directly above or below the sprite, the bullets move very fast. When the mouse is to the right of the sprite, they move very slow. And if the mouse is on the left side, the bullets shoot out the right side all the same.
When I remove the invert variable from the mix, it seems to work fine. So here are my 2 questions:
Am I way off-track, and there's a simpler, cleaner, more widely used, etc. way to do this?
If I'm on the right track, how do I "normalize" the vector so that it stays the same regardless of how far away the mouse is from the sprite?
Thanks in advance.
Use vectors to your advantage. I don't know if Java's Vector2f class has this method, but here's how I'd do it:
return (v2 - v1).normalize(); // `v2` is obj pos and `v1` is the mouse pos
To normalize a vector, just divide it (i.e. each component) by the magnitude of the entire vector:
Vector2f result = new Vector2f(v2.x - v1.x, v2.y - v1.y);
float length = sqrt(result.x^2 + result.y^2);
return new Vector2f(result.x / length, result.y / length);
The result is unit vector (its magnitude is 1). So to adjust the speed, just scale the vector.
Yes for both questions:
to find what you call ratio you can use the arctan function which will provide the angle of of the vector which goes from first object to second object
to normalize it, since now you are starting from an angle you don't need to do anything: you can directly use polar coordinates
Code is rather simple:
float magnitude = 3.0; // your max speed
float angle = Math.atan2(y,x);
Vector2D vector = new Vector(magnitude*sin(angle), magnitude*cos(angle));