From Google Earth I got a Box with coordinates for a picture, like following:
<LatLonBox>
<north>53.10685</north>
<south>53.10637222222223</south>
<east>8.853144444444444</east>
<west>8.851858333333333</west>
<rotation>-26.3448</rotation>
</LatLonBox>
Now I want to test weather a point intersect with this LatLonBox.
My base idea to check, whether a point intersect with the LatLonBox was, to rotate the point back by the given angle, and then to test whether the point intersect with a regular (not rotated) rectangle.
I tried to calculate the rotation manually:
public static MyGeoPoint rotatePoint(MyGeoPoint point, MyGeoPoint origion, double degree)
{
double x = origion.getLatitude() + (Math.cos(Math.toRadians(degree)) * (point.getLatitude() - origion.getLatitude()) - Math.sin(Math.toRadians(degree)) * (point.getLongitude() - origion.getLongitude()));
double y = origion.getLongitude() + (Math.sin(Math.toRadians(degree)) * (point.getLatitude() - origion.getLatitude()) + Math.cos(Math.toRadians(degree)) * (point.getLongitude() - origion.getLongitude()));
return new MyGeoPoint(x, y);
}
public boolean intersect(MyGeoPoint geoPoint)
{
geoPoint = MyGeoPoint.rotatePoint(geoPoint, this.getCenter(), - this.getRotation());
return (geoPoint.getLatitude() < getTopLeftLatitude()
&& geoPoint.getLatitude() > getBottomRightLatitude()
&& geoPoint.getLongitude() > getTopLeftLongitude()
&& geoPoint.getLongitude() < getBottomRightLongitude());
}
And it seems that the results are wrong.
LatLonBox box = new LatLonBox(53.10685, 8.851858333333333, 53.10637222222223, 8.853144444444444, -26.3448);
MyGeoPoint point1 = new MyGeoPoint(53.106872, 8.852311);
MyGeoPoint point2 = new MyGeoPoint(53.10670378322918, 8.852967186822669);
MyGeoPoint point3 = new MyGeoPoint(53.10652664993972, 8.851994565566875);
MyGeoPoint point4 = new MyGeoPoint(53.10631650700605, 8.85270995172055);
System.out.println(box.intersect(point1));
System.out.println(box.intersect(point2));
System.out.println(box.intersect(point3));
System.out.println(box.intersect(point4));
The result is true, false, false, true. But it should be 4x true.
Probably I´, making some kind of error in reasoning.
Maybe because the latitude values are getting bigger upwards. But I don´t knwo how to change the formular.
I need some help ...
EDIT:
I think my basic idea and formular is right. Also I found similar solutions eg. link and couldn´t find any difference.
So I think the only possible error source is, that the axis are not proportional. So the problem is how to take account of this.
I hope someone has got an idea.
The problem was indeed that the axis were not proportional.
The following method takes care of it.
public static MyGeoPoint rotatePoint(MyGeoPoint point, MyGeoPoint origion, double degree)
{
double x = origion.longitude + (Math.cos(Math.toRadians(degree)) * (point.longitude - origion.longitude) - Math.sin(Math.toRadians(degree)) * (point.latitude - origion.latitude) / Math.abs(Math.cos(Math.toRadians(origion.latitude)));
double y = origion.latitude + (Math.sin(Math.toRadians(degree)) * (point.longitude - origion.longitude) * Math.abs(Math.cos(Math.toRadians(origion.latitude))) + Math.cos(Math.toRadians(degree)) * (point.latitude - origion.latitude));
return new MyGeoPoint(x, y);
}
if I understand correctly you want to check if these four points are in rotated rectangle.
I would recommend checking not by corner points because your rectangle is rotated but:
if you have rotated rectangle ABCD then calculate lines |AB|, |BC|,|CD| and |DA|. If you have two points then use y=ax+b (you will calculate a,b by by giving [x,y] of both coordinates that gives you two easy equatations).
Finally function intersect will check
if point <= line |CD|
AND point >= line |AB|
AND point <= line |BC|
AND point >= |DA|
then it is inside rect.
This can be done when your point P[x,y] you put in ax+y+b (a>0 or -ax-y-b). If it is zero it is lying on the line, if it is < than it is under line or "on the left side". Hope I helped..
BTW why are you using -degree value, which you multiply by -1 , is it necessary?
The problem appears to be that the data structure LatLonBox doesn't make any sense as a description for the boundary of a picture. A box in lat-lon coordinates is not a geometric rectangle. (Think about a box near or including the north pole.) You need to re-think your application to deal in a lat/lon coordinate for the center of the picture and then deal with the rotation as an angle with respect to lines of latitude (parallel to the equator). (Even then, a picture with center on the north or south pole will be a degenerate case that must be handled separately.) So a box should properly be something like:
<geobox>
<center_lat>41</center_lat>
<center_lon>-74</center_lon>
<rotation_degrees_ccw>-23</rotation_degrees_ccw>
<width>1000</width> <!-- in pixels or meters, but not in degrees! -->
<height>600</height> <!-- same as above -->
</geobox>
Having said all that, suppose you have a true geometric box centered at (x0,y0), width w, height h, rotated angle T about its center. Then you can test a point P(x,y) for membership in the box with the following. You need the transformation that takes the box to the origin and aligns it with the axes. This is Translate(-x0,-y0) then Rotate(-T). This transformation as a matrix is
[cos(-T) -sin(-T) 0][1 0 -x0] [ cos(T) sin(T) -x0*cos(T)-y0*sin(T)]
[sin(-T) cos(-T) 0][0 1 -y0] = [-sin(T) cos(T) x0*sin(T)-y0*cos(T)]
[0 0 1][0 0 1] [ 0 0 1 ]
You want to apply this transformation to the point to be tested and then see if it lies in the desired box:
// Transform the point to be tested.
ct = cos(T);
st = sin(T);
xp = ct * x + st * y - x0 * ct - y0 * st;
yp = -st * x + ct * y + x0 * st - y0 * ct;
// Test for membership in the box.
boolean inside = xp >= -w/2 && xp <= w/2 && yp >= -h/2 && yp <= h/2;
It's late and I haven't checked this arithmetic, but it's close. Say if it doesn't work.
Related
I have a Rectangle object that is placed on the screen and rendered using paintComponent.
I also have a rotation variable that determines the rotation of the object (using right and left keys to rotate) and repaints the object on the screen using Affine Transform.
In the keyPressed method, I have this which allows me to shoot bullets:
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + 10, player.y - 10, ID.Bullet);
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
If you see where I create a new bullet, in the second line, the second and third arguments in the new Bullet() are what determines where the bullets are created. Currently they just shoot from the same position on the rectangle regardless of the rotation.
I have failed at allowing the player to shoot a bullet from the direction they are facing, so if anyone has any suggestions that would be very helpful.
So basically you are trying to find the offset Vector from the player's position.
Let's assume, that you have the offset Vector, when you haven't rotated the object.
Then your task would be to rotate the given Vector by the rotation variable.
To do this we first need to look into geometry a bit. Let's think of a 2d-Vector as a triangle, that we can split into it's x and y components. We know, that the rotated Vector should have the same hypothenuse length as the original offset or in other terms the same magnitude.
This means that:
x₀² + y₀² = x₁² + y₁²
Since:
x² + y² = Vector-Magnitude
Your rotation variable keeps track of the inner angle of that "triangle vector" and hence we can describe the lengths as:
x = sin(rotation) * Vector Magnitude
y = cos(rotation) * Vector Magnitude
Now the only thing left to do is evalute those values. To do that, let us jump into the code, shall we?
float magnitude = offset.x*offset.x + offset.y*offset.y;
float x = Math.sin(rotation) * magnitude;
float y = Math.cos(rotation) * magnitude;
Bullet b = new Bullet(handler, player.x + x, player.y + y, ID.Bullet);
possible Errors and fixes
Make sure that you have the correct angle-format. The Java-Math class uses radiens for trigonometric methods.
There could be a constant rotation deviation, depending on what exactly you want your result to look like. This is a rather easy fix, as you just have to add this constant to the rotation variable. In unlikely instances you maybe also need to negate the rotation variable simply by using -rotation in the places I used rotation in my code.
If you know the rotation angle and the width of the rectangle then use this information to rotate the start position of the bullet too. For example to let the start position at the right side:
x = rectMiddleX + width/2 * cos(angle)
y = rectMiddleY + width/2 * sin(angle)
If angle is 0 it will start at the right side
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + width/2 * cos(player.rotation), player.y - width/2 * sin(player.rotation), ID.Bullet);
//if player.x/y is in corner, add width/2 / height/2
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
My gravity simulation acts more like a gravity slingshot. Once the two bodies pass over each other, they accelerate far more than they decelerate on the other side. It's not balanced. It won't oscillate around an attractor.
How do other gravity simulators get around it? example: http://www.testtubegames.com/gravity.html, if you create 2 bodies they will just oscillate back and forth, not drifting any further apart than their original distance even though they move through each other as in my example.
That's how it should be. But in my case, as soon as they get close they just shoot away from each other to the edges of the imaginary galaxy never to come back for a gazillion years.
edit: Here is a video of the bug https://imgur.com/PhhRhP7
Here is a minimal test case to run in processing.
//Constants:
float v;
int unit = 1; //1 pixel = 1 meter
float x;
float y;
float alx;
float aly;
float g = 6.67408 * pow(10, -11) * sq(unit); //g constant
float m1 = (1 * pow(10, 15)); // attractor mass
float m2 = 1; //object mass
void setup() {
size (200,200);
a = 0;
v = 0;
x = width/2; // object x
y = 0; // object y
alx = width/2; //attractor x
aly = height/2; //attractor y
}
void draw() {
background(0);
getAcc();
applyAcc();
fill(0,255,0);
ellipse(x, y, 10, 10); //object
fill(255,0,0);
ellipse(alx, aly, 10, 10); //attractor
}
void applyAcc() {
a = getAcc();
v += a * (1/frameRate); //add acceleration to velocity
y += v * (1/frameRate); //add velocity to Y
a = 0;
}
float getAcc() {
float a = 0;
float d = dist(x, y, alx, aly); //distance to attractor
float gravity = (g * m1 * m2)/sq(d); //gforce
a += gravity/m2;
if (y > aly){
a *= -1;}
return a;
}
Your distance doesn't include width of the object, so the objects effectively occupy the same space at the same time.
The way to "cap gravity" as suggested above is add a normal force when the outer edges touch, if it's a physical simulation.
You should get into the habit of debugging your code. Which line of code is behaving differently from what you expected?
For example, if I were you I would start by printing out the value of gravity every time you calculate it:
float gravity = (g * m1 * m2)/sq(d); //gforce
println(gravity);
You'll notice that your gravity value skyrockets as your circles get closer to each other. And this makes sense, because you're dividing by sq(d). Ad d gets smaller, your gravity increases.
You could simply cap your gravity value so it doesn't go off the charts anymore:
float gravity = (g * m1 * m2)/sq(d);
if(gravity > 100){
gravity = 100;
}
Alternatively you could cap d so it never goes below a certain value, but the result is the same.
In the end you'll find that this is not going to be as easy as you expected. You're going to have to tune the parameters quite a bit so your simulation works how you want.
Working demo here: https://beta.observablehq.com/#shaunlebron/1d-gravity
I followed the solution posted by the author of the sim that inspired this question here:
-First off, shrinking the timestep is always helpful. My simulation runs, as a baseline, about 40 ‘steps’ per frame, and 30 frames per second.
-To deal with the exact issue you talk about, I think modeling the bodies not as pure point masses - but rather spherical masses with a certain radius will be vital. That prevents the force of gravity from diverging to infinity. So, for instance, if you drop an asteroid into a star in my simulation (with collisions turned off), the force of gravity will increase as the asteroid gets closer, up until it reaches the surface of the star, at which point the force will begin to decrease. And the moment it’s at the center of the star (or nearby), the force will be zero (or nearly zero) - instead of near-infinite.
In my demo, I just completed turned off gravity when two objects are close enough together. Seems to work well enough.
I'm working on a game for a game jam right now, and the problem is related to the flight path of a certain game enemy. I'm trying to have several of them fly in formation, and the idea was to have them fly in a wide radius circle around the center of the screen so they essentially box in the player. To do this, I tried to use the following formula...
public Pair<Double> move(Pair<Double> currPos, Pair<Double> playerPos) {
Pair<Double> newPos = new Pair<>(currPos.x, currPos.y);
// The radius used as the distance from the center.
double r = (Framework.CANVAS_WIDTH / 2) - Player.SHIP_SIZE;
// X,Y coords for center of the screen.
double cX = (Framework.CANVAS_WIDTH / 2);
double cY = (Framework.CANVAS_HEIGHT / 2);
// Trigonometric equation for transforming the object in a circle.
newPos.x = cX + (r * Math.cos(Framework.getHypotenuse(currPos, new Pair<Double>(cX, cY)) + (Math.PI / 90)));
newPos.y = cY + (r * Math.sin(Framework.getHypotenuse(currPos, new Pair<Double>(cX, cY)) + (Math.PI / 90)));
return newPos;
}
I can't figure out why the equation doesn't seem to work. When I test the movement pattern, there seem to be two enemies on screen rotating around the center, even though I only spawned one. However, they're blinking really fast, which makes it seem like maybe the ship is jumping back and forth really fast. This is supported by the fact that when I took a screenshot, there was only one ship. Is there something wrong with my trigonometry that would cause this, or does the problem lie elsewhere?
The following pseudocode gives the standard way to make an object move in a circular path:
double r = (...); // Radius of circle
double cX = (...); // x-coordinate of center of rotation
double cY = (...); // y-coordinate of center of rotation
double omega = (...); // Angular velocity, like 1
double t = (...); // Time step, like 0.00, 0.01, 0.02, 0.03, etc.
newPos.x = cX + r * Math.cos(t * omega);
newPos.y = cY + r * Math.sin(t * omega);
I'm making a 2d game in libgdx and I would like to know what the standard way of moving (translating between two known points) on the screen is.
On a button press, I am trying to animate a diagonal movement of a sprite between two points. I know the x and y coordinates of start and finish point. However I can't figure out the maths that determines where the texture should be in between on each call to render. At the moment my algorithm is sort of like:
textureProperty = new TextureProperty();
firstPtX = textureProperty.currentLocationX
firstPtY = textureProperty.currentLocationY
nextPtX = textureProperty.getNextLocationX()
nextPtX = textureProperty.getNextLocationX()
diffX = nextPtX - firstPtX
diffY = nextPtY - firstPtY
deltaX = diffX/speedFactor // Arbitrary, controlls speed of the translation
deltaX = diffX/speedFactor
renderLocX = textureProperty.renderLocX()
renderLocY = textureProperty.renderLocY()
if(textureProperty.getFirstPoint() != textureProperty.getNextPoint()){
animating = true
}
if (animating) {
newLocationX = renderLocX + deltaX
newLocationY = renderLocY + deltaY
textureProperty.setRenderPoint(renderLocX, renderLocY)
}
if (textureProperty.getRenderPoint() == textureProperty.getNextPoint()){
animating = false
textureProperty.setFirstPoint(textureProperty.getNextPoint())
}
batch.draw(texture, textureProperty.renderLocX(), textureProperty.renderLocY())
However, I can foresee a few issues with this code.
1) Since pixels are integers, if I divide that number by something that doesn't go evenly, it will round. 2) as a result of number 1, it will miss the target.
Also when I do test the animation, the objects moving from point1, miss by a long shot, which suggests something may be wrong with my maths.
Here is what I mean graphically:
Desired outcome:
Actual outcome:
Surely this is a standard problem. I welcome any suggestions.
Let's say you have start coordinates X1,Y1 and end coordinates X2,Y2. And let's say you have some variable p which holds percantage of passed path. So if p == 0 that means you are at X1,Y1 and if p == 100 that means you are at X2, Y2 and if 0<p<100 you are somewhere in between. In that case you can calculate current coordinates depending on p like:
X = X1 + ((X2 - X1)*p)/100;
Y = Y1 + ((Y2 - Y1)*p)/100;
So, you are not basing current coords on previous one, but you always calculate depending on start and end point and percentage of passed path.
First of all you need a Vector2 direction, giving the direction between the 2 points.
This Vector should be normalized, so that it's length is 1:
Vector2 dir = new Vector2(x2-x1,y2-y1).nor();
Then in the render method you need to move the object, which means you need to change it's position. You have the speed (given in distance/seconds), a normalized Vector, giving the direction, and the time since the last update.
So the new position can be calculated like this:
position.x += speed * delta * dir.x;
position.y += speed * delta * dir.y;
Now you only need to limit the position to the target position, so that you don't go to far:
boolean stop = false;
if (position.x >= target.x) {
position.x = target.x;
stop = true;
}
if (position.y >= target.y) {
position.y = target.y;
stop = true;
}
Now to the pixel-problem:
Do not use pixels! Using pixels will make your game resolution dependent.
Use Libgdx Viewport and Camera instead.
This alows you do calculate everything in you own world unit (for example meters) and Libgdx will convert it for you.
I didn't saw any big errors, tho' i saw some like you are comparing two objects using == and !=, But i suggest u to use a.equals(b) and !a.equals(b) like that. And secondly i found that your renderLock coords are always being set same in textureProperty.setRenderPoint(renderLocX, renderLocY) you are assigning the same back. Maybe you were supposed to use newLocation coords.
BTW Thanks for your code, i was searching Something that i got by you <3
So I'm rendering points in 3D space. To find their X position on the screen, I'm using this math:
double sin = Math.sin(viewPointRotX);
double cos = Math.cos(viewPointRotX);
double xx = x - viewPointX;
double zz = z - viewPointZ;
double rotx = xx * cos - zz * sin;
double rotz = zz * cos + xx * sin;
double xpix = (rotx / rotz * height + width / 2);
I'm doing a similar process for Y.
This works fine, but points can render as if they were in front of the camera when they are actually behind it.
How can I work out using the data I've got whether a given point is in front of or behind the camera?
We can tell if a point is in front or behind a camera by comparing coordinates with a little 3D coordinate geometry.
Consider a very simple example: The camera is located at (0,0,0) and pointed straight up. That would mean every point with a positive Z coordinate is "in front" of the camera. Now, we could get more complicated and account for the fact that the field of view of a camera is really a cone with a particular angle.
For instance, if the camera has a 90 degree field of view (45 degrees in both directions of the way it is facing), then we can handle this with some linear math. In 2D space, again with camera at the origin facing up, the field of view would be bound by the lines y = x and y = -x (or all points within y = |x|).
For 3D space, take the line y = x and spin it around the z-axis. We now have the cone:
z = x*x + y*y, so if
if(z < x*x + y*y && z>0)
then the point is in front of the camera and within the field of view.
If the camera is not at the origin, we can just translate the coordinate system so that it is and then run the calculation above. If the viewing angle is not 90 degrees, then we need to trace out some line y = mx (where m = Math.tan(viewAngle/2) ) and spin this line about the z-axis to get the light cone.
The way it looks like you are doing this is transforming the point coordinates so that they are aligned with and relative to the view.
If you do this the right way then you can just check that the rotated z-value is positive and greater than the distance from the focal-point to the "lens".
Find the view direction vector:
V = (cos(a)sin(b), sin(a)sin(b), cos(b)), where a and b are the camera's rotation angles.
Project the offset vector (xx, yy, zz) onto the view direction vector, and find the magnitude, giving the distance along the camera's view axis of the point:
distance = xx * cos(a)sin(b) + yy * sin(a)sin(b) + zz * cos(b)
Now just check that distance > focalLength.
This should work but you have to be careful to set everything up right. You might have to use a different calculation to find the view direction vector depending on how you are representing the camera's orientation.