I wanted to ask what would be the right way to move an object in the direction it is rotated?
Right now I have:
local ang = body:getAngle() * 180 / 3.14 /// get body's rotation in degrees
local x, y = body:getPosition(); /// get current position
ang = ang%360
x = x + math.sin(ang)
y = y + math.cos(ang)
print(ang)
body:setPosition(x,y)
Yet the body is moving very weird. Any ideas what I'm doing wrong?
Thanks
You need the angle in radians and use the cosine function for the x value and the sine function for the y value. A function (untested) for this in lua would look something like this:
function moveAlongAngle(body, angleInRadians, dt, speedVector)
local x, y = body:getPosition()
x = x + math.cos(angleInRadians) * dt * speedVector.x
y = y + math.sin(angleInRadians) * dt * speedVector.y
body:setPosition(x,y)
end
This is because you convert the angle from polar to cartesian coordinates: http://en.wikipedia.org/wiki/Polar_coordinate_system#Converting_between_polar_and_Cartesian_coordinates
you are mixing radians and degrees. first you transform your angle into radians, but afterwards you try to normalize the degrees using modulo. you dont need to normalize, since sin and cos are periodic functions.
the math.sin and math.cos actually expect the angle in radians and not in degrees, so you need to devide by 180 and multiply by pi.
this is of course assuming your initial variable ang is in degrees.
Related
I have a map of world.
I need to create a function that gets two double parameters (longitude and latitude) and that function should draw a small circle on that area in the map image.
I have the following info about the map:
The width of the map in pixels
The height of the map in pixels
** I get to pixel X, Y based on that image.
I tried longitude = -106.346771 and latitude = 56.130366;
mapWidth = 3840.0 and mapHeight = 2160.0 ;
double x = (longitude + 180) / 360 * mapWidth;
double y = (1 - Math.log(Math.tan(Math.toRadians(latitude)) + 1 /
Math.cos(Math.toRadians(latitude))) / Math.PI) / 2 * mapHeight;
result : [lon: -106.346771 lat: 56.130366]: X: 785.6344426666666 Y: 671.2084211650845
but not selected in right location.
enter image description here
Unfortunately, this problem is way harder than it seems.
That map is a Projection, so it is distorted from the original (roughly) spherical coordinate system your data is in.
(actually, your lat & long values may themselve be in one of several different projections, but that only really matters if you are doing very precise measurements.)
So your first order of business is to find out exactly what projection the map is using, then start looking for GIS libraries you can use in Java (I think there are several) to perform the translation.
Decide on a radius, r, for your globe model. Your map will be 2πr pixels wide.
Decide on a a latitude cutoff. You can't project a pole to a plane, and at extreme latitudes things get so distorted as to be unusable. 85 degrees is a common choice.
Express your latitude in radians: lat_r
You X coordinate is r * (lat_r+ π). So at 180° W your X coordinate is r * (-π + π) = 0, and at 180° E your X coordinate is r * (π + π) = 2πr
Express your longitude in radians: lon_r
Your Y coordinate is r * tan(lon_r) * (1 - cos(lon_r)). So at 85° N your Y coordinate is r * tan(1.4835) * (1 - cos(1.4835)) = 10.4r
Please take a look at my other question which this was done incorrectly here.
What I need to do
I have an image of a map of my country, I took the piece of the map from Google Maps, so that means I know all of the corner's coordinates in longitude and latitude. My program needs to show up the map, and paint targets on it where each target has its only longitude and latitude, similar to how radar displays targets.
The problem
The problem with my solution is that it's not really using real mathematical formulas to get the X, Y position. It uses simple division and multiple by ratio with minimum and maximum as you can see in my other question.
this is the method:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr #oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
What I have tried
So I was a bit with myself tried to think of something logical, on how can I do this. And I came up with something that doesn't really work exactly:
If we have the corner top-left for example coordinates (Longitude and latitude), and we have the coordinates of the target that we want to display, that means we can do distanceToPoint to know how many kilometers far from the start it is.
After that, we need to know the heading to that point, so we do calculateHeading which gives us the angle to the target point.
So lets call A the starting point (top-left corner)
float aLat = 33.49f;
float aLong = 33.69f;
And our target point we call it b:
float bLat = 32f;
float bLong = 35f;
And then we can calculate the distance from A to B in kilometers:
double km = distanceTopPoint(aLat, aLong, bLat, bLong);
And then we calculate the angle to the point:
double angle = calculateHeading(aLat, aLong, bLat, bLong);
And if we have the km distance and angle, we can know the distance in km for longitude and latitude:
int latDistance = (int) Math.round(km * Math.cos(angle));
int lonDistance = (int) Math.round(km * Math.sin(angle));
So now I probably have the distance from the longitude to the target's longitude and same for latitude. But what can I do with this information?
I tried thinking again, and I figured out that I can know the distance from the left top corner to the right top corner distance in km and same for top left corner to top left bottom corner. And then I can do width / km to get the km per pixel.
But I am really unsure, im sure that I am doing something wrong.
Any ideas?
The Mercator projection is a cylindrical projection, i.e. the generalized coordinates can be calculated as:
a = longitude
b = tan(latitude)
These are unscaled coordinates, i.e. they do not correspond to pixel positions.
Let's say you have an image of w x h pixels that represents the area between (min_long, min_lat) - (max_long, max_lat). These coordinates can be converted to generalized projected coordinates with the above formulas, which yields (min_a, min_b) - (max_a, max_b).
Now you need a linear mapping of the generalized coordinates to pixel positions. This linear mapping can be expressed with four parameters (two scaling parameters and two offset parameters):
x = s_a * a + o_a
y = s_b * b = o_a
Therefore, you need to find the four parameters. You know that the top left corner has pixel coordinates (0, 0) and generalized coordinates (min_a, max_b). Similarly for the bottom right corner. This gives you four constraints and a linear system of equations:
0 = s_a * min_a + o_a
0 = s_b * max_b + o_b
w = s_a * max_a + o_a
h = s_b * min_b + o_b
The solution of this system is:
s_a = w / (max_a - min_a)
o_a = -w * min_a / (max_a - min_a)
s_b = -h / (max_b - min_b)
o_b = h * max_b / (max_b - min_b)
And this is it. If you want the pixel coordinates for some arbitrary point `(long, lat), then do the following:
Calculate the generalized coordinates a and b (be careful to use radians when calculating the tangens).
Use the linear map to convert a and b to pixel coordinates x and y with the pre-calculated parameters.
Inversion
To get latitude and longitude from pixel coordinates, do the following:
Calculate the generalized coordinates:
a = (x - o_a) / s_a
b = (x - o_b) / s_b
Calculate the geo-coordinates:
longitude = a
latitude = arc tan (b)
Again, be careful about radians/degrees.
I've made a Java program that displays lines in 3d space projected onto the 2d view, and so far, it's been working pretty well. I tried to make it possible to essentially rotate the 'world' about the camera's position about any axis, but now I'm running into some problems.
public void rotate(){
float ax = main.angleX; //main = camera
float ay = main.angleY;
float az = main.angleZ;
for(Line3d line : lines){ //all lines in the world
Vector3d start = Vector3d.Vector3dPMinus(line.start, main.getPoint()); //vetor value of starting point of line - camera's position
Vector3d end = Vector3d.Vector3dPMinus(line.end, main.getPoint());
start.rotate(ax, ay, az);
end.rotate(ax, ay, az); //rotate each vector
line.start = Point3d.pointFromVector3d(start).add(main.getPoint());
line.end = Point3d.pointFromVector3d(end).add(main.getPoint()); //vectors back into points
}
}
Rotation function:
public Vector3d rotate(float ax, float ay, float az){
Math.toRadians(ax *= 90);
Math.toRadians(ay *= 90);
Math.toRadians(az *= 90);
y = (float) (y * Math.cos(ax) - z * Math.sin(ax));
z = (float) (y * Math.sin(ax) + z * Math.cos(ax));
x = (float) (x * Math.cos(ay) + z * Math.sin(ay));
z = (float) (z * Math.cos(ay) - x * Math.sin(ay));
x = (float) (x * Math.cos(az) - y * Math.sin(az));
y = (float) (x * Math.sin(az) + y * Math.cos(az));
return this;
}
I've set it rotate about the x axis 3 times per second, and it displays exactly what I want it to before it starts rotating, but once it starts rotating, there's just some unidentifiable mess of usually just one horizontal line.
Is the method I used for rotating not right? Is there a better way to do it?
A Rotation of a 3d vector around the 3 axis is not that trivial as one might think. The thing you are probably trying to do is rotating with so called Euler Angles. You should be familiar with matrices to work with 3d space. I checked your rotations and they should work fine. BUT you should keep the following in mind: When you are rotating around an angle. The following rotations are affected by the previous rotation. You are rotating the rotation axis too.
To avoid this behaivour one ugly possibility is to rotate around a free axis. And when you are rotating around the x axis first. You rotate the y axis in reverse and rotate then your point around this y'. When you are rotating around z you need to rotate z axis with your reversed x and y rotation and then rotate around this z'. When you are doing this, you can always rotate around your world coordinatesystem. You should really use some math Library to accomplish this. It makes your life much easier. When you want to code it yourself. You need a proper matrice class with multiplication of matrices and vectors and some inversion method.
Your approach appears to take a reasonable general form. It looks like you are rotating the line endpoints relative to the current camera position, which is correct, and the three specific rotations you are performing could also be correct (but see below).
However, the three Math.toRadians() calls cannot be doing anything useful, because you ignore their results. Moreover, the expression ax *= 90 and its mates look awfully suspicious: are ax, ay, and az really expressed as fractions of a quarter-circle? That seems doubtful, and if it were the case then you would want to multiply by Math.PI/2 and skip toRadians(). On the other hand, if they are expressed in degrees then the following version of rotate() is correct for one reasonable definition of ax, ay, and az, and some possible Vector3D implementations:
public Vector3d rotate(double ax, double ay, double az){
ax = Math.toRadians(ax);
ay = Math.toRadians(ay);
az = Math.toRadians(az);
y = (float) ( y * Math.cos(ax) - z * Math.sin(ax));
z = (float) ( y * Math.sin(ax) + z * Math.cos(ax));
x = (float) ( x * Math.cos(ay) + z * Math.sin(ay));
z = (float) (-x * Math.sin(ay) + z * Math.cos(ay));
x = (float) ( x * Math.cos(az) - y * Math.sin(az));
y = (float) ( x * Math.sin(az) + y * Math.cos(az));
return this;
}
Overall, though, I am also a bit dubious of the correctness of the ax, ay, and az for this purpose. In particular, be aware that you cannot just accumulate separate x, y, and z rotation increments independently, as the resulting aggregate rotation depends greatly on the order in which the incremental rotations are performed. Moreover, even if ax, ay, and az correctly describe the orientation of the camera, it is unlikely that applying the same rotation to the world is what you actually want to do.
DESPITE ALL THE FOREGOING, though, I don't see any reason why your code would distort the model as you describe it doing. I don't think it will apply the rotation you want (even with my suggested fix), but the reason for the distortion is likely somewhere else.
I'm trying to create a method which calculates the x and y in a grid and eventually the distance between that point and the middle. The problem is, I only know a few values. To explain the case a bit better an image:
(the values between '(..,..)' are lat/long combinations).
As you can see, I know the following values:
start of canvas: xy(0,0)
middle of canvas: xy(540,800) and lat/long(52.3702160, 4.8951680)
max dimension of canvas: x 1080, y 1600
point: xy(?,?) and lat/long(52.4167267, 4.8052174)
point: xy(?,?) and lat/long(52,2422306, 5.1129068)
First, I need to do something to calculate the missing x and y's from the points.
I already tried doing the following:
double mapWidth = screenWidth;
double mapHeight = screenHeight;
// get x value
x = (location.getLongitude()+180)*(mapWidth/360);
// convert from degrees to radians
double latRad = location.getLatitude()*Math.PI/180;
// get y value
double mercN = Math.log(Math.tan((Math.PI/4)+(latRad/2)));
y = (mapHeight/2)-(mapWidth*mercN/(2*Math.PI));
point = new PointF((float)x,(float)y);
This works but I'm getting the wrong x and y values.
For example if my points lat/long are further away the x and y's are getting bigger (more to the middle). But they need to be more at the side because the lat/long point is further away.
All lat/long points inside 2km diameter need to be in my grid, if the point is for example 0.9km away from the center it needs to be nearly at the side.
After that I need to calculate the distance between the two points. I already got that part using the following:
Math.sqrt((point.x - point2.x) * (point.x - point2.x) + (point.y - point2.y) * (point.y - point2.y));
My main problem is calculating the x and y from my lat/long points.
If anyone wants to help, thanks in advance!
I completely rethought my way of calculating the x and y.
I solved it by doing the following:
double distanceMeters = mCurrentLocation.distanceTo(location);
x = ((1000+distanceMeters)*mMiddleCoords.x)/1000; //1000 = radius.
y = ((1000+distanceMeters)*mMiddleCoords.y)/1000;
You can't directly use the distance formula from latitude and longitude. You'll have to take into account the curvature of the sphere to calculate the distance.
The minimum distance between two points on a sphere (and hence earth, simplifying it to a perfect sphere) is the length of the chord on what is called the Great Circle running through those points. A Great Circle is a circle with its center running through the center of the sphere).
From Wikipedia:
C = SQRT(X^2 + Y^2 + Z^2)
where:
X = cos(lat2) * cos(long2) - cos(lat1) * cos(long1)
Y = cos(lat2) * sin(long2) - cos(lat1) * sin(long1)
Z = sin(lat2) - sin(lat1)
And the distance is (2 * R * arcsin(C/2)) where R is the radius of the earth or 6371 km
The other alternative - if you know you will always have the Android libraries - is the Location.distanceTo() method.
I am trying to rotate a box in java using a rotation matrix.
(I am using the LWJGL and Slick 2D libraries)
my code to rotate 1 point around the center point is this:
point1X = (float) (centerX * Math.cos(rotation) - centerY * Math.sin(rotation));
point1Y = (float) (centerX * Math.sin(rotation) + centerY * Math.cos(rotation));
Right now I just update the rotation every update like so:
rotation += delta * 0.001;
This works great except the rotation number does not seem to correspond to a degree from 0˚ to 360˚
Is there a formula or something that will translate the rotation number to a readable degree and vice versa?
Normally, trig functions expect their arguments to be in radians, not degrees.
2*pi radians = 360 degrees.